diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml index cc7cdc18..82125143 100644 --- a/.github/workflows/export.yml +++ b/.github/workflows/export.yml @@ -23,10 +23,10 @@ jobs: - name: Export game id: export - uses: firebelley/godot-export@v6.0.0 + uses: firebelley/godot-export@v7.0.0 with: - godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/4.4.1-stable/Godot_v4.4.1-stable_mono_linux_x86_64.zip - godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/4.4.1-stable/Godot_v4.4.1-stable_mono_export_templates.tpz + godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/4.6.1-stable/Godot_v4.6.1-stable_mono_linux_x86_64.zip + godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/4.6.1-stable/Godot_v4.6.1-stable_mono_export_templates.tpz relative_project_path: ./ relative_export_path: ./builds/ wine_path: ${{ steps.wine_install.outputs.WINE_PATH }} @@ -34,7 +34,7 @@ jobs: export_debug: true - name: Create release - uses: ncipollo/release-action@v1.13.0 + uses: ncipollo/release-action@v1.20.0 with: token: ${{ secrets.GITHUB_TOKEN }} generateReleaseNotes: true @@ -45,7 +45,7 @@ jobs: prerelease: ${{ contains(fromJSON('["dev", "pre", "alpha", "beta", "rc"]'), github.event_name) }} - name: Upload Artifact for publish - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: build path: ${{ steps.export.outputs.build_directory }} @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Write release version run: | @@ -63,13 +63,13 @@ jobs: echo Version: $VERSION echo "VERSION=$VERSION" >> $GITHUB_ENV - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: build path: build/ - name: Deploy on itch.io - uses: Ayowel/butler-to-itch@v1.0.0 + uses: Ayowel/butler-to-itch@v1.3.0 with: butler_key: ${{ secrets.BUTLER_CREDENTIALS }} itch_game: monologue diff --git a/.github/workflows/test-export.yml b/.github/workflows/test-export.yml index 75cbd19b..62c64804 100644 --- a/.github/workflows/test-export.yml +++ b/.github/workflows/test-export.yml @@ -12,7 +12,7 @@ jobs: name: Monologue export steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install wine id: wine_install @@ -23,10 +23,10 @@ jobs: - name: Export game id: export - uses: firebelley/godot-export@v6.0.0 + uses: firebelley/godot-export@v7.0.0 with: - godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/4.4.1-stable/Godot_v4.4.1-stable_mono_linux_x86_64.zip - godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/4.4.1-stable/Godot_v4.4.1-stable_mono_export_templates.tpz + godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/4.6.1-stable/Godot_v4.6.1-stable_mono_linux_x86_64.zip + godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/4.6.1-stable/Godot_v4.6.1-stable_mono_export_templates.tpz relative_project_path: ./ relative_export_path: ./builds/ wine_path: ${{ steps.wine_install.outputs.WINE_PATH }} @@ -34,7 +34,7 @@ jobs: export_debug: true - name: Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: build path: ${{ steps.export.outputs.build_directory }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8fead71d..f0a8685c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,9 @@ jobs: statuses: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: MikeSchulze/gdUnit4-action@v1 with: - godot-version: '4.4.1' + godot-version: '4.6.1' paths: | res://unit diff --git a/addons/Todo_Manager/ColourPicker.gd b/addons/Todo_Manager/ColourPicker.gd deleted file mode 100644 index 39a3f9a6..00000000 --- a/addons/Todo_Manager/ColourPicker.gd +++ /dev/null @@ -1,17 +0,0 @@ -@tool -extends HBoxContainer - -var colour : Color -var title : String: - set = set_title -var index : int - -@onready var colour_picker := $TODOColourPickerButton - -func _ready() -> void: - $TODOColourPickerButton.color = colour - $Label.text = title - -func set_title(value: String) -> void: - title = value - $Label.text = value diff --git a/addons/Todo_Manager/ColourPicker.gd.uid b/addons/Todo_Manager/ColourPicker.gd.uid deleted file mode 100644 index 515031ee..00000000 --- a/addons/Todo_Manager/ColourPicker.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://crapvn4bs75pj diff --git a/addons/Todo_Manager/Current.gd b/addons/Todo_Manager/Current.gd deleted file mode 100644 index d3961c9b..00000000 --- a/addons/Todo_Manager/Current.gd +++ /dev/null @@ -1,44 +0,0 @@ -@tool -extends Panel - -signal tree_built # used for debugging - -const Todo := preload("res://addons/Todo_Manager/todo_class.gd") -const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd") - -var _sort_alphabetical := true - -@onready var tree := $Tree as Tree - -func build_tree(todo_item : TodoItem, patterns : Array, cased_patterns : Array[String]) -> void: - tree.clear() - var root := tree.create_item() - root.set_text(0, "Scripts") - var script := tree.create_item(root) - script.set_text(0, todo_item.get_short_path() + " -------") - script.set_metadata(0, todo_item) - for todo in todo_item.todos: - var item := tree.create_item(script) - var content_header : String = todo.content - if "\n" in todo.content: - content_header = content_header.split("\n")[0] + "..." - item.set_text(0, "(%0) - %1".format([todo.line_number, content_header], "%_")) - item.set_tooltip_text(0, todo.content) - item.set_metadata(0, todo) - for i in range(0, len(cased_patterns)): - if cased_patterns[i] == todo.pattern: - item.set_custom_color(0, patterns[i][1]) - emit_signal("tree_built") - - -func sort_alphabetical(a, b) -> bool: - if a.script_path > b.script_path: - return true - else: - return false - -func sort_backwards(a, b) -> bool: - if a.script_path < b.script_path: - return true - else: - return false diff --git a/addons/Todo_Manager/Current.gd.uid b/addons/Todo_Manager/Current.gd.uid deleted file mode 100644 index 20622714..00000000 --- a/addons/Todo_Manager/Current.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://mb0ik7qyjrvj diff --git a/addons/Todo_Manager/Dock.gd b/addons/Todo_Manager/Dock.gd deleted file mode 100644 index c71b6b2e..00000000 --- a/addons/Todo_Manager/Dock.gd +++ /dev/null @@ -1,297 +0,0 @@ -@tool -extends Control - -#signal tree_built # used for debugging -enum { CASE_INSENSITIVE, CASE_SENSITIVE } - -const Project := preload("res://addons/Todo_Manager/Project.gd") -const Current := preload("res://addons/Todo_Manager/Current.gd") - -const Todo := preload("res://addons/Todo_Manager/todo_class.gd") -const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd") -const ColourPicker := preload("res://addons/Todo_Manager/UI/ColourPicker.tscn") -const Pattern := preload("res://addons/Todo_Manager/UI/Pattern.tscn") -const DEFAULT_PATTERNS := [["\\bTODO\\b", Color("96f1ad"), CASE_INSENSITIVE], ["\\bHACK\\b", Color("d5bc70"), CASE_INSENSITIVE], ["\\bFIXME\\b", Color("d57070"), CASE_INSENSITIVE]] -const DEFAULT_SCRIPT_COLOUR := Color("ccced3") -const DEFAULT_SCRIPT_NAME := false -const DEFAULT_SORT := true - -var plugin : EditorPlugin - -var todo_items : Array - -var script_colour := Color("ccced3") -var ignore_paths : Array[String] = [] -var full_path := false -var auto_refresh := true -var builtin_enabled := false -var _sort_alphabetical := true - -var patterns := [["\\bTODO\\b", Color("96f1ad"), CASE_INSENSITIVE], ["\\bHACK\\b", Color("d5bc70"), CASE_INSENSITIVE], ["\\bFIXME\\b", Color("d57070"), CASE_INSENSITIVE]] - - -@onready var tabs := $VBoxContainer/TabContainer as TabContainer -@onready var project := $VBoxContainer/TabContainer/Project as Project -@onready var current := $VBoxContainer/TabContainer/Current as Current -@onready var project_tree := $VBoxContainer/TabContainer/Project/Tree as Tree -@onready var current_tree := $VBoxContainer/TabContainer/Current/Tree as Tree -@onready var settings_panel := $VBoxContainer/TabContainer/Settings as Panel -@onready var colours_container := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3/Colours as VBoxContainer -@onready var pattern_container := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns as VBoxContainer -@onready var ignore_textbox := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths/TextEdit as LineEdit -@onready var auto_refresh_button := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/RefreshCheckButton as CheckButton - -func _ready() -> void: - load_config() - populate_settings() - - -func build_tree() -> void: - if tabs: - match tabs.current_tab: - 0: - project.build_tree(todo_items, ignore_paths, patterns, plugin.cased_patterns, _sort_alphabetical, full_path) - create_config_file() - 1: - current.build_tree(get_active_script(), patterns, plugin.cased_patterns) - create_config_file() - 2: - pass - _: - pass - - -func get_active_script() -> TodoItem: - var current_script : Script = plugin.get_editor_interface().get_script_editor().get_current_script() - if current_script: - var script_path = current_script.resource_path - for todo_item in todo_items: - if todo_item.script_path == script_path: - return todo_item - - # nothing found - var todo_item := TodoItem.new(script_path, []) - return todo_item - else: - # not a script - var todo_item := TodoItem.new("res://Documentation", []) - return todo_item - - -func go_to_script(script_path: String, line_number : int = 0) -> void: - if plugin.get_editor_interface().get_editor_settings().get_setting("text_editor/external/use_external_editor"): - var exec_path = plugin.get_editor_interface().get_editor_settings().get_setting("text_editor/external/exec_path") - var args := get_exec_flags(exec_path, script_path, line_number) - OS.execute(exec_path, args) - else: - var script := load(script_path) - plugin.get_editor_interface().edit_resource(script) - plugin.get_editor_interface().get_script_editor().goto_line(line_number - 1) - -func get_exec_flags(editor_path : String, script_path : String, line_number : int) -> PackedStringArray: - var args : PackedStringArray - var script_global_path = ProjectSettings.globalize_path(script_path) - - if editor_path.ends_with("code.cmd") or editor_path.ends_with("code"): ## VS Code - args.append(ProjectSettings.globalize_path("res://")) - args.append("--goto") - args.append(script_global_path + ":" + str(line_number)) - - elif editor_path.ends_with("rider64.exe") or editor_path.ends_with("rider"): ## Rider - args.append("--line") - args.append(str(line_number)) - args.append(script_global_path) - - else: ## Atom / Sublime - args.append(script_global_path + ":" + str(line_number)) - - return args - -func sort_alphabetical(a, b) -> bool: - if a.script_path > b.script_path: - return true - else: - return false - -func sort_backwards(a, b) -> bool: - if a.script_path < b.script_path: - return true - else: - return false - - -func populate_settings() -> void: - for i in patterns.size(): - ## Create Colour Pickers - var colour_picker: Variant = ColourPicker.instantiate() - colour_picker.colour = patterns[i][1] - colour_picker.title = patterns[i][0] - colour_picker.index = i - colours_container.add_child(colour_picker) - colour_picker.colour_picker.color_changed.connect(change_colour.bind(i)) - - ## Create Patterns - var pattern_edit: Variant = Pattern.instantiate() - pattern_edit.text = patterns[i][0] - pattern_edit.index = i - pattern_container.add_child(pattern_edit) - pattern_edit.line_edit.text_changed.connect(change_pattern.bind(i, - colour_picker)) - pattern_edit.remove_button.pressed.connect(remove_pattern.bind(i, - pattern_edit, colour_picker)) - pattern_edit.case_checkbox.button_pressed = patterns[i][2] - pattern_edit.case_checkbox.toggled.connect(case_sensitive_pattern.bind(i)) - - var pattern_button := $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns/AddPatternButton - $VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns.move_child(pattern_button, 0) - - # path filtering - var ignore_paths_field := ignore_textbox - if not ignore_paths_field.is_connected("text_changed", _on_ignore_paths_changed): - ignore_paths_field.connect("text_changed", _on_ignore_paths_changed) - var ignore_paths_text := "" - for path in ignore_paths: - ignore_paths_text += path + ", " - ignore_paths_text = ignore_paths_text.trim_suffix(", ") - ignore_paths_field.text = ignore_paths_text - - auto_refresh_button.button_pressed = auto_refresh - - -func rebuild_settings() -> void: - for node in colours_container.get_children(): - node.queue_free() - for node in pattern_container.get_children(): - if node is Button: - continue - node.queue_free() - populate_settings() - - -#### CONFIG FILE #### -func create_config_file() -> void: - var config = ConfigFile.new() - config.set_value("scripts", "full_path", full_path) - config.set_value("scripts", "sort_alphabetical", _sort_alphabetical) - config.set_value("scripts", "script_colour", script_colour) - config.set_value("scripts", "ignore_paths", ignore_paths) - - config.set_value("patterns", "patterns", patterns) - - config.set_value("config", "auto_refresh", auto_refresh) - config.set_value("config", "builtin_enabled", builtin_enabled) - - var err = config.save("res://addons/Todo_Manager/todo.cfg") - - -func load_config() -> void: - var config := ConfigFile.new() - if config.load("res://addons/Todo_Manager/todo.cfg") == OK: - full_path = config.get_value("scripts", "full_path", DEFAULT_SCRIPT_NAME) - _sort_alphabetical = config.get_value("scripts", "sort_alphabetical", DEFAULT_SORT) - script_colour = config.get_value("scripts", "script_colour", DEFAULT_SCRIPT_COLOUR) - ignore_paths = config.get_value("scripts", "ignore_paths", [] as Array[String]) - patterns = config.get_value("patterns", "patterns", DEFAULT_PATTERNS) - auto_refresh = config.get_value("config", "auto_refresh", true) - builtin_enabled = config.get_value("config", "builtin_enabled", false) - else: - create_config_file() - - -#### Events #### -func _on_SettingsButton_toggled(button_pressed: bool) -> void: - settings_panel.visible = button_pressed - if button_pressed == false: - create_config_file() -# plugin.find_tokens_from_path(plugin.script_cache) - if auto_refresh: - plugin.rescan_files(true) - -func _on_Tree_item_activated() -> void: - var item : TreeItem - match tabs.current_tab: - 0: - item = project_tree.get_selected() - 1: - item = current_tree.get_selected() - if item.get_metadata(0) is Todo: - var todo : Todo = item.get_metadata(0) - call_deferred("go_to_script", todo.script_path, todo.line_number) - else: - var todo_item = item.get_metadata(0) - call_deferred("go_to_script", todo_item.script_path) - -func _on_FullPathCheckBox_toggled(button_pressed: bool) -> void: - full_path = button_pressed - -func _on_ScriptColourPickerButton_color_changed(color: Color) -> void: - script_colour = color - -func _on_RescanButton_pressed() -> void: - plugin.rescan_files(true) - -func change_colour(colour: Color, index: int) -> void: - patterns[index][1] = colour - -func change_pattern(value: String, index: int, this_colour: Node) -> void: - patterns[index][0] = value - this_colour.title = value - plugin.rescan_files(true) - -func remove_pattern(index: int, this: Node, this_colour: Node) -> void: - patterns.remove_at(index) - this.queue_free() - this_colour.queue_free() - plugin.rescan_files(true) - -func case_sensitive_pattern(active: bool, index: int) -> void: - if active: - patterns[index][2] = CASE_SENSITIVE - else: - patterns[index][2] = CASE_INSENSITIVE - plugin.rescan_files(true) - -func _on_DefaultButton_pressed() -> void: - patterns = DEFAULT_PATTERNS.duplicate(true) - _sort_alphabetical = DEFAULT_SORT - script_colour = DEFAULT_SCRIPT_COLOUR - full_path = DEFAULT_SCRIPT_NAME - rebuild_settings() - plugin.rescan_files(true) - -func _on_AlphSortCheckBox_toggled(button_pressed: bool) -> void: - _sort_alphabetical = button_pressed - plugin.rescan_files(true) - -func _on_AddPatternButton_pressed() -> void: - patterns.append(["\\bplaceholder\\b", Color.WHITE, CASE_INSENSITIVE]) - rebuild_settings() - -func _on_RefreshCheckButton_toggled(button_pressed: bool) -> void: - auto_refresh = button_pressed - -func _on_Timer_timeout() -> void: - plugin.refresh_lock = false - -func _on_ignore_paths_changed(new_text: String) -> void: - var text = ignore_textbox.text - var split: Array = text.split(',') - ignore_paths.clear() - for elem in split: - if elem == " " || elem == "": - continue - ignore_paths.push_front(elem.lstrip(' ').rstrip(' ')) - # validate so no empty string slips through (all paths ignored) - var i := 0 - for path in ignore_paths: - if (path == "" || path == " "): - ignore_paths.remove_at(i) - i += 1 - plugin.rescan_files(true) - -func _on_TabContainer_tab_changed(tab: int) -> void: - build_tree() - -func _on_BuiltInCheckButton_toggled(button_pressed: bool) -> void: - builtin_enabled = button_pressed - plugin.rescan_files(true) diff --git a/addons/Todo_Manager/Dock.gd.uid b/addons/Todo_Manager/Dock.gd.uid deleted file mode 100644 index 5f3f40f5..00000000 --- a/addons/Todo_Manager/Dock.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c4qqmuk2wdyd7 diff --git a/addons/Todo_Manager/Pattern.gd b/addons/Todo_Manager/Pattern.gd deleted file mode 100644 index 4e610afc..00000000 --- a/addons/Todo_Manager/Pattern.gd +++ /dev/null @@ -1,21 +0,0 @@ -@tool -extends HBoxContainer - - -var text : String : set = set_text -var disabled : bool -var index : int - -@onready var line_edit := $LineEdit as LineEdit -@onready var remove_button := $RemoveButton as Button -@onready var case_checkbox := %CaseSensativeCheckbox as CheckBox - -func _ready() -> void: - line_edit.text = text - remove_button.disabled = disabled - - -func set_text(value: String) -> void: - text = value - if line_edit: - line_edit.text = value diff --git a/addons/Todo_Manager/Pattern.gd.uid b/addons/Todo_Manager/Pattern.gd.uid deleted file mode 100644 index 0d20144c..00000000 --- a/addons/Todo_Manager/Pattern.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://jhhqs3082s4a diff --git a/addons/Todo_Manager/Project.gd b/addons/Todo_Manager/Project.gd deleted file mode 100644 index 60e0d0f3..00000000 --- a/addons/Todo_Manager/Project.gd +++ /dev/null @@ -1,74 +0,0 @@ -@tool -extends Panel - -signal tree_built # used for debugging - -const Todo := preload("res://addons/Todo_Manager/todo_class.gd") - -var _sort_alphabetical := true -var _full_path := false - -@onready var tree := $Tree as Tree - -func build_tree(todo_items : Array, ignore_paths : Array, patterns : Array, cased_patterns: Array[String], sort_alphabetical : bool, full_path : bool) -> void: - _full_path = full_path - tree.clear() - if sort_alphabetical: - todo_items.sort_custom(Callable(self, "sort_alphabetical")) - else: - todo_items.sort_custom(Callable(self, "sort_backwards")) - var root := tree.create_item() - root.set_text(0, "Scripts") - for todo_item in todo_items: - var ignore := false - for ignore_path in ignore_paths: - var script_path : String = todo_item.script_path - if script_path.begins_with(ignore_path) or script_path.begins_with("res://" + ignore_path) or script_path.begins_with("res:///" + ignore_path): - ignore = true - break - if ignore: - continue - var script := tree.create_item(root) - if full_path: - script.set_text(0, todo_item.script_path + " -------") - else: - script.set_text(0, todo_item.get_short_path() + " -------") - script.set_metadata(0, todo_item) - for todo in todo_item.todos: - var item := tree.create_item(script) - var content_header : String = todo.content - if "\n" in todo.content: - content_header = content_header.split("\n")[0] + "..." - item.set_text(0, "(%0) - %1".format([todo.line_number, content_header], "%_")) - item.set_tooltip_text(0, todo.content) - item.set_metadata(0, todo) - #print(todo.title) - for i in range(0, len(cased_patterns)): - if cased_patterns[i] == todo.pattern: - item.set_custom_color(0, patterns[i][1]) - emit_signal("tree_built") - - -func sort_alphabetical(a, b) -> bool: - if _full_path: - if a.script_path < b.script_path: - return true - else: - return false - else: - if a.get_short_path() < b.get_short_path(): - return true - else: - return false - -func sort_backwards(a, b) -> bool: - if _full_path: - if a.script_path > b.script_path: - return true - else: - return false - else: - if a.get_short_path() > b.get_short_path(): - return true - else: - return false diff --git a/addons/Todo_Manager/Project.gd.uid b/addons/Todo_Manager/Project.gd.uid deleted file mode 100644 index 90ab2401..00000000 --- a/addons/Todo_Manager/Project.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dx8ly2snkm58 diff --git a/addons/Todo_Manager/UI/ColourPicker.tscn b/addons/Todo_Manager/UI/ColourPicker.tscn deleted file mode 100644 index 95886e53..00000000 --- a/addons/Todo_Manager/UI/ColourPicker.tscn +++ /dev/null @@ -1,21 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bie1xn8v1kd66"] - -[ext_resource type="Script" uid="uid://crapvn4bs75pj" path="res://addons/Todo_Manager/ColourPicker.gd" id="1"] - -[node name="TODOColour" type="HBoxContainer"] -offset_right = 105.0 -offset_bottom = 31.0 -script = ExtResource("1") -metadata/_edit_use_custom_anchors = false - -[node name="Label" type="Label" parent="."] -offset_top = 4.0 -offset_right = 1.0 -offset_bottom = 27.0 - -[node name="TODOColourPickerButton" type="ColorPickerButton" parent="."] -custom_minimum_size = Vector2(40, 0) -offset_left = 65.0 -offset_right = 105.0 -offset_bottom = 31.0 -size_flags_horizontal = 10 diff --git a/addons/Todo_Manager/UI/Dock.tscn b/addons/Todo_Manager/UI/Dock.tscn deleted file mode 100644 index 80f8ef7c..00000000 --- a/addons/Todo_Manager/UI/Dock.tscn +++ /dev/null @@ -1,316 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://b6k0dtftankcx"] - -[ext_resource type="Script" uid="uid://c4qqmuk2wdyd7" path="res://addons/Todo_Manager/Dock.gd" id="1"] -[ext_resource type="Script" uid="uid://dx8ly2snkm58" path="res://addons/Todo_Manager/Project.gd" id="2"] -[ext_resource type="Script" uid="uid://mb0ik7qyjrvj" path="res://addons/Todo_Manager/Current.gd" id="3"] - -[sub_resource type="ButtonGroup" id="ButtonGroup_kqxcu"] - -[sub_resource type="ButtonGroup" id="ButtonGroup_kltg3"] - -[node name="Dock" type="Control"] -custom_minimum_size = Vector2(0, 200) -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_vertical = 3 -script = ExtResource("1") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 4.0 -grow_horizontal = 2 -grow_vertical = 2 -metadata/_edit_layout_mode = 1 - -[node name="Header" type="HBoxContainer" parent="VBoxContainer"] -visible = false -layout_mode = 2 - -[node name="HeaderLeft" type="HBoxContainer" parent="VBoxContainer/Header"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Title" type="Label" parent="VBoxContainer/Header/HeaderLeft"] -layout_mode = 2 -text = "Todo Dock:" - -[node name="HeaderRight" type="HBoxContainer" parent="VBoxContainer/Header"] -layout_mode = 2 -size_flags_horizontal = 3 -alignment = 2 - -[node name="SettingsButton" type="Button" parent="VBoxContainer/Header/HeaderRight"] -visible = false -layout_mode = 2 -toggle_mode = true -text = "Settings" - -[node name="TabContainer" type="TabContainer" parent="VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="Project" type="Panel" parent="VBoxContainer/TabContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("2") - -[node name="Tree" type="Tree" parent="VBoxContainer/TabContainer/Project"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -hide_root = true - -[node name="Current" type="Panel" parent="VBoxContainer/TabContainer"] -visible = false -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -script = ExtResource("3") - -[node name="Tree" type="Tree" parent="VBoxContainer/TabContainer/Current"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -hide_folding = true -hide_root = true - -[node name="Settings" type="Panel" parent="VBoxContainer/TabContainer"] -visible = false -layout_mode = 2 - -[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/TabContainer/Settings"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="Scripts" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Scripts"] -layout_mode = 2 -text = "Scripts:" - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Scripts"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 5 - -[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer"] -layout_mode = 2 - -[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] -layout_mode = 2 - -[node name="Scripts" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] -layout_mode = 2 - -[node name="ScriptName" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"] -layout_mode = 2 -text = "Script Name:" - -[node name="FullPathCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"] -layout_mode = 2 -button_group = SubResource("ButtonGroup_kqxcu") -text = "Full path" - -[node name="ShortNameCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName"] -layout_mode = 2 -button_pressed = true -button_group = SubResource("ButtonGroup_kqxcu") -text = "Short name" - -[node name="ScriptSort" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"] -layout_mode = 2 -text = "Sort Order:" - -[node name="AlphSortCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"] -layout_mode = 2 -button_pressed = true -button_group = SubResource("ButtonGroup_kltg3") -text = "Alphabetical" - -[node name="RAlphSortCheckBox" type="CheckBox" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort"] -layout_mode = 2 -button_group = SubResource("ButtonGroup_kltg3") -text = "Reverse Alphabetical" - -[node name="ScriptColour" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour"] -layout_mode = 2 -text = "Script Colour:" - -[node name="ScriptColourPickerButton" type="ColorPickerButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour"] -custom_minimum_size = Vector2(40, 0) -layout_mode = 2 -color = Color(0.8, 0.807843, 0.827451, 1) - -[node name="IgnorePaths" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"] -layout_mode = 2 -text = "Ignore Paths:" - -[node name="TextEdit" type="LineEdit" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -expand_to_text_length = true - -[node name="Label3" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/IgnorePaths"] -layout_mode = 2 -text = "(Separated by commas)" - -[node name="TODOColours" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/TODOColours"] -layout_mode = 2 -text = "TODO Colours:" - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/TODOColours"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3"] -layout_mode = 2 - -[node name="Colours" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer3"] -layout_mode = 2 - -[node name="Patterns" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Patterns"] -layout_mode = 2 -text = "Patterns:" - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Patterns"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBoxContainer4" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4"] -layout_mode = 2 - -[node name="Patterns" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="AddPatternButton" type="Button" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns"] -layout_mode = 2 -size_flags_horizontal = 0 -text = "Add" - -[node name="Config" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Config"] -layout_mode = 2 -text = "Config:" - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/Config"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBoxContainer5" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="VSeparator" type="VSeparator" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5"] -layout_mode = 2 - -[node name="Patterns" type="VBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5"] -layout_mode = 2 - -[node name="RefreshCheckButton" type="CheckButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"] -layout_mode = 2 -size_flags_horizontal = 0 -button_pressed = true -text = "Auto Refresh" - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"] -layout_mode = 2 - -[node name="BuiltInCheckButton" type="CheckButton" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer"] -layout_mode = 2 -text = "Scan Built-in Scripts" - -[node name="Label" type="Label" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer"] -layout_mode = 2 -text = "Some functionality will not work for built-in scripts" - -[node name="DefaultButton" type="Button" parent="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns"] -layout_mode = 2 -size_flags_horizontal = 0 -text = "Reset to default" - -[node name="Timer" type="Timer" parent="."] -one_shot = true - -[node name="RescanButton" type="Button" parent="."] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -102.0 -offset_top = 3.0 -offset_bottom = 34.0 -grow_horizontal = 0 -text = "Rescan Files" -flat = true - -[connection signal="toggled" from="VBoxContainer/Header/HeaderRight/SettingsButton" to="." method="_on_SettingsButton_toggled"] -[connection signal="tab_changed" from="VBoxContainer/TabContainer" to="." method="_on_TabContainer_tab_changed"] -[connection signal="item_activated" from="VBoxContainer/TabContainer/Project/Tree" to="." method="_on_Tree_item_activated"] -[connection signal="item_activated" from="VBoxContainer/TabContainer/Current/Tree" to="." method="_on_Tree_item_activated"] -[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptName/FullPathCheckBox" to="." method="_on_FullPathCheckBox_toggled"] -[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptSort/AlphSortCheckBox" to="." method="_on_AlphSortCheckBox_toggled"] -[connection signal="color_changed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer2/Scripts/ScriptColour/ScriptColourPickerButton" to="." method="_on_ScriptColourPickerButton_color_changed"] -[connection signal="pressed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer4/Patterns/AddPatternButton" to="." method="_on_AddPatternButton_pressed"] -[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/RefreshCheckButton" to="." method="_on_RefreshCheckButton_toggled"] -[connection signal="toggled" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/HBoxContainer/BuiltInCheckButton" to="." method="_on_BuiltInCheckButton_toggled"] -[connection signal="pressed" from="VBoxContainer/TabContainer/Settings/ScrollContainer/MarginContainer/VBoxContainer/HBoxContainer5/Patterns/DefaultButton" to="." method="_on_DefaultButton_pressed"] -[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] -[connection signal="pressed" from="RescanButton" to="." method="_on_RescanButton_pressed"] diff --git a/addons/Todo_Manager/UI/Pattern.tscn b/addons/Todo_Manager/UI/Pattern.tscn deleted file mode 100644 index 8e76a41e..00000000 --- a/addons/Todo_Manager/UI/Pattern.tscn +++ /dev/null @@ -1,26 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bx11sel2q5wli"] - -[ext_resource type="Script" uid="uid://jhhqs3082s4a" path="res://addons/Todo_Manager/Pattern.gd" id="1"] - -[node name="Pattern" type="HBoxContainer"] -script = ExtResource("1") - -[node name="LineEdit" type="LineEdit" parent="."] -layout_mode = 2 -size_flags_horizontal = 0 -expand_to_text_length = true - -[node name="RemoveButton" type="Button" parent="."] -layout_mode = 2 -text = "-" - -[node name="MarginContainer" type="MarginContainer" parent="."] -custom_minimum_size = Vector2(20, 0) -layout_mode = 2 -size_flags_horizontal = 0 - -[node name="CaseSensativeCheckbox" type="CheckBox" parent="."] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 0 -text = "Case Sensitive" diff --git a/addons/Todo_Manager/doc/images/Instruct1.png b/addons/Todo_Manager/doc/images/Instruct1.png deleted file mode 100644 index 99a8db02..00000000 Binary files a/addons/Todo_Manager/doc/images/Instruct1.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/Instruct1.png.import b/addons/Todo_Manager/doc/images/Instruct1.png.import deleted file mode 100644 index 98608c1a..00000000 --- a/addons/Todo_Manager/doc/images/Instruct1.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dym47vrgj0hx8" -path="res://.godot/imported/Instruct1.png-698c6faa3ef3ac4960807faa3e028bd6.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/Instruct1.png" -dest_files=["res://.godot/imported/Instruct1.png-698c6faa3ef3ac4960807faa3e028bd6.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/Instruct2.png b/addons/Todo_Manager/doc/images/Instruct2.png deleted file mode 100644 index 83798f9a..00000000 Binary files a/addons/Todo_Manager/doc/images/Instruct2.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/Instruct2.png.import b/addons/Todo_Manager/doc/images/Instruct2.png.import deleted file mode 100644 index 18b26207..00000000 --- a/addons/Todo_Manager/doc/images/Instruct2.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://d2xr8n3nl5g6w" -path="res://.godot/imported/Instruct2.png-4e8664e49d00a397bbd539f7dee976ff.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/Instruct2.png" -dest_files=["res://.godot/imported/Instruct2.png-4e8664e49d00a397bbd539f7dee976ff.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/Instruct3.png b/addons/Todo_Manager/doc/images/Instruct3.png deleted file mode 100644 index 283c604f..00000000 Binary files a/addons/Todo_Manager/doc/images/Instruct3.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/Instruct3.png.import b/addons/Todo_Manager/doc/images/Instruct3.png.import deleted file mode 100644 index e1ee1989..00000000 --- a/addons/Todo_Manager/doc/images/Instruct3.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://uflndslhr1ig" -path="res://.godot/imported/Instruct3.png-3cc62ed99bf29d90b803cb8eb40881e9.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/Instruct3.png" -dest_files=["res://.godot/imported/Instruct3.png-3cc62ed99bf29d90b803cb8eb40881e9.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/Instruct4.png b/addons/Todo_Manager/doc/images/Instruct4.png deleted file mode 100644 index 66ef1c9a..00000000 Binary files a/addons/Todo_Manager/doc/images/Instruct4.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/Instruct4.png.import b/addons/Todo_Manager/doc/images/Instruct4.png.import deleted file mode 100644 index 309098ca..00000000 --- a/addons/Todo_Manager/doc/images/Instruct4.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://wwu4v2tqb8js" -path="res://.godot/imported/Instruct4.png-bf5aa1cffc066175cecf9281b0774809.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/Instruct4.png" -dest_files=["res://.godot/imported/Instruct4.png-bf5aa1cffc066175cecf9281b0774809.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/Instruct5.png b/addons/Todo_Manager/doc/images/Instruct5.png deleted file mode 100644 index d418faf5..00000000 Binary files a/addons/Todo_Manager/doc/images/Instruct5.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/Instruct5.png.import b/addons/Todo_Manager/doc/images/Instruct5.png.import deleted file mode 100644 index 99b31909..00000000 --- a/addons/Todo_Manager/doc/images/Instruct5.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c3xevo216odqw" -path="res://.godot/imported/Instruct5.png-001538ed8b5682dcf232de08035aab38.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/Instruct5.png" -dest_files=["res://.godot/imported/Instruct5.png-001538ed8b5682dcf232de08035aab38.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png b/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png deleted file mode 100644 index 6d19fee4..00000000 Binary files a/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import b/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import deleted file mode 100644 index 75d1a2da..00000000 --- a/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://djacx5rlif2qp" -path="res://.godot/imported/TODO_Manager_Logo.png-e07d7ec75201c66b732ef87ec1bece15.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png" -dest_files=["res://.godot/imported/TODO_Manager_Logo.png-e07d7ec75201c66b732ef87ec1bece15.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/TodoExternal.gif b/addons/Todo_Manager/doc/images/TodoExternal.gif deleted file mode 100644 index 25d0850d..00000000 Binary files a/addons/Todo_Manager/doc/images/TodoExternal.gif and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/example1.png b/addons/Todo_Manager/doc/images/example1.png deleted file mode 100644 index 786bd152..00000000 Binary files a/addons/Todo_Manager/doc/images/example1.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/example1.png.import b/addons/Todo_Manager/doc/images/example1.png.import deleted file mode 100644 index a88dd0c9..00000000 --- a/addons/Todo_Manager/doc/images/example1.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c1hfqwmrav4cc" -path="res://.godot/imported/example1.png-6386c332ca46e1e62ea061b956a901cd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/example1.png" -dest_files=["res://.godot/imported/example1.png-6386c332ca46e1e62ea061b956a901cd.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/doc/images/example2.png b/addons/Todo_Manager/doc/images/example2.png deleted file mode 100644 index 256d7546..00000000 Binary files a/addons/Todo_Manager/doc/images/example2.png and /dev/null differ diff --git a/addons/Todo_Manager/doc/images/example2.png.import b/addons/Todo_Manager/doc/images/example2.png.import deleted file mode 100644 index bb9a7bc1..00000000 --- a/addons/Todo_Manager/doc/images/example2.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://8unqybdfe5hp" -path="res://.godot/imported/example2.png-2e3a8f9cd1e178daf22b83dc0513f37a.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/Todo_Manager/doc/images/example2.png" -dest_files=["res://.godot/imported/example2.png-2e3a8f9cd1e178daf22b83dc0513f37a.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/Todo_Manager/plugin.cfg b/addons/Todo_Manager/plugin.cfg deleted file mode 100644 index 629da570..00000000 --- a/addons/Todo_Manager/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Todo Manager" -description="Dock for housing TODO messages." -author="Peter de Vroom" -version="2.2.2" -script="plugin.gd" diff --git a/addons/Todo_Manager/plugin.gd b/addons/Todo_Manager/plugin.gd deleted file mode 100644 index 41ccaa32..00000000 --- a/addons/Todo_Manager/plugin.gd +++ /dev/null @@ -1,280 +0,0 @@ -@tool -extends EditorPlugin - -const DockScene := preload("res://addons/Todo_Manager/UI/Dock.tscn") -const Dock := preload("res://addons/Todo_Manager/Dock.gd") -const Todo := preload("res://addons/Todo_Manager/todo_class.gd") -const TodoItem := preload("res://addons/Todo_Manager/todoItem_class.gd") - -var _dockUI : Dock - -class TodoCacheValue: - var todos: Array - var last_modified_time: int - - func _init(todos: Array, last_modified_time: int): - self.todos = todos - self.last_modified_time = last_modified_time - -var todo_cache : Dictionary # { key: script_path, value: TodoCacheValue } -var remove_queue : Array -var combined_pattern : String -var cased_patterns : Array[String] - -var refresh_lock := false # makes sure _on_filesystem_changed only triggers once - - -func _enter_tree() -> void: - _dockUI = DockScene.instantiate() as Control - add_control_to_bottom_panel(_dockUI, "TODO") - get_editor_interface().get_resource_filesystem().connect("filesystem_changed", - _on_filesystem_changed) - get_editor_interface().get_file_system_dock().connect("file_removed", queue_remove) - get_editor_interface().get_script_editor().connect("editor_script_changed", - _on_active_script_changed) - _dockUI.plugin = self - - combined_pattern = combine_patterns(_dockUI.patterns) - find_tokens_from_path(find_scripts()) - _dockUI.build_tree() - - -func _exit_tree() -> void: - _dockUI.create_config_file() - remove_control_from_bottom_panel(_dockUI) - _dockUI.free() - - -func queue_remove(file: String): - for i in _dockUI.todo_items.size() - 1: - if _dockUI.todo_items[i].script_path == file: - _dockUI.todo_items.remove_at(i) - - -func find_tokens_from_path(scripts: Array[String]) -> void: - for script_path in scripts: - var file := FileAccess.open(script_path, FileAccess.READ) - var contents := file.get_as_text() - find_tokens(contents, script_path) - -func find_tokens_from_script(script: Resource) -> void: - find_tokens(script.source_code, script.resource_path) - - -func find_tokens(text: String, script_path: String) -> void: - var cached_todos = get_cached_todos(script_path) - if cached_todos.size() != 0: -# var i := 0 -# for todo_item in _dockUI.todo_items: -# if todo_item.script_path == script_path: -# _dockUI.todo_items.remove_at(i) -# i += 1 - var todo_item := TodoItem.new(script_path, cached_todos) - _dockUI.todo_items.append(todo_item) - else: - var regex = RegEx.new() - # if regex.compile("#\\s*\\bTODO\\b.*|#\\s*\\bHACK\\b.*") == OK: - if regex.compile(combined_pattern) == OK: - var result : Array[RegExMatch] = regex.search_all(text) - if result.is_empty(): - for i in _dockUI.todo_items.size(): - if _dockUI.todo_items[i].script_path == script_path: - _dockUI.todo_items.remove_at(i) - return # No tokens found - var match_found : bool - var i := 0 - for todo_item in _dockUI.todo_items: - if todo_item.script_path == script_path: - match_found = true - var updated_todo_item := update_todo_item(todo_item, result, text, script_path) - _dockUI.todo_items.remove_at(i) - _dockUI.todo_items.insert(i, updated_todo_item) - break - i += 1 - if !match_found: - _dockUI.todo_items.append(create_todo_item(result, text, script_path)) - - -func create_todo_item(regex_results: Array[RegExMatch], text: String, script_path: String) -> TodoItem: - var todo_item = TodoItem.new(script_path, []) - todo_item.script_path = script_path - var last_line_number := 0 - var lines := text.split("\n") - for r in regex_results: - var new_todo : Todo = create_todo(r.get_string(), script_path) - new_todo.line_number = get_line_number(r.get_string(), text, last_line_number) - # GD Multiline comment - var trailing_line := new_todo.line_number - var should_break = false - while trailing_line < lines.size() and lines[trailing_line].dedent().begins_with("#"): - for other_r in regex_results: - if lines[trailing_line] in other_r.get_string(): - should_break = true - break - if should_break: - break - - new_todo.content += "\n" + lines[trailing_line] - trailing_line += 1 - - last_line_number = new_todo.line_number - todo_item.todos.append(new_todo) - cache_todos(todo_item.todos, script_path) - return todo_item - - -func update_todo_item(todo_item: TodoItem, regex_results: Array[RegExMatch], text: String, script_path: String) -> TodoItem: - todo_item.todos.clear() - var lines := text.split("\n") - for r in regex_results: - var new_todo : Todo = create_todo(r.get_string(), script_path) - new_todo.line_number = get_line_number(r.get_string(), text) - # GD Multiline comment - var trailing_line := new_todo.line_number - var should_break = false - while trailing_line < lines.size() and lines[trailing_line].dedent().begins_with("#"): - for other_r in regex_results: - if lines[trailing_line] in other_r.get_string(): - should_break = true - break - if should_break: - break - - new_todo.content += "\n" + lines[trailing_line] - trailing_line += 1 - todo_item.todos.append(new_todo) - return todo_item - - -func get_line_number(what: String, from: String, start := 0) -> int: - what = what.split('\n')[0] # Match first line of multiline C# comments - var temp_array := from.split('\n') - var lines := Array(temp_array) - var line_number# = lines.find(what) + 1 - for i in range(start, lines.size()): - if what in lines[i]: - line_number = i + 1 # +1 to account of 0-based array vs 1-based line numbers - break - else: - line_number = 0 # This is an error - return line_number - - -func _on_filesystem_changed() -> void: - if !refresh_lock: - if _dockUI.auto_refresh: - refresh_lock = true - _dockUI.get_node("Timer").start() - rescan_files(false) - - -func find_scripts() -> Array[String]: - var scripts : Array[String] - var directory_queue : Array[String] - var dir := DirAccess.open("res://") - ### FIRST PHASE ### - if dir.get_open_error() == OK: - get_dir_contents(dir, scripts, directory_queue) - else: - printerr("TODO_Manager: There was an error during find_scripts() ### First Phase ###") - - ### SECOND PHASE ### - while not directory_queue.is_empty(): - if dir.change_dir(directory_queue[0]) == OK: - get_dir_contents(dir, scripts, directory_queue) - else: - printerr("TODO_Manager: There was an error at: " + directory_queue[0]) - directory_queue.pop_front() - - return scripts - - -func cache_todos(todos: Array, script_path: String) -> void: - var last_modified_time = FileAccess.get_modified_time(script_path) - todo_cache[script_path] = TodoCacheValue.new(todos, last_modified_time) - - -func get_cached_todos(script_path: String) -> Array: - if todo_cache.has(script_path): - var cached_value: TodoCacheValue = todo_cache[script_path] - if cached_value.last_modified_time == FileAccess.get_modified_time(script_path): - return cached_value.todos - return [] - -func get_dir_contents(dir: DirAccess, scripts: Array[String], directory_queue: Array[String]) -> void: - dir.include_navigational = false - dir.include_hidden = false - dir.list_dir_begin() - var file_name : String = dir.get_next() - - while file_name != "": - if dir.current_is_dir(): - if file_name == ".import" or file_name == ".mono": # Skip .import folder which should never have scripts - pass - else: - directory_queue.append(dir.get_current_dir() + "/" + file_name) - else: - if file_name.ends_with(".gd") or file_name.ends_with(".cs") \ - or file_name.ends_with(".c") or file_name.ends_with(".cpp") or file_name.ends_with(".h") \ - or ((file_name.ends_with(".tscn") and _dockUI.builtin_enabled)): - if dir.get_current_dir() == "res://": - scripts.append(dir.get_current_dir() + file_name) - else: - scripts.append(dir.get_current_dir() + "/" + file_name) - file_name = dir.get_next() - - -func rescan_files(clear_cache: bool) -> void: - _dockUI.todo_items.clear() - if clear_cache: - todo_cache.clear() - combined_pattern = combine_patterns(_dockUI.patterns) - find_tokens_from_path(find_scripts()) - _dockUI.build_tree() - - -func combine_patterns(patterns: Array) -> String: - # Case Sensitivity - cased_patterns = [] - for pattern in patterns: - if pattern[2] == _dockUI.CASE_INSENSITIVE: - cased_patterns.append(pattern[0].insert(0, "((?i)") + ")") - else: - cased_patterns.append("(" + pattern[0] + ")") - - if patterns.size() == 1: - return cased_patterns[0] - else: - var pattern_string := "((\\/\\*)|(#|\\/\\/))\\s*(" - for i in range(patterns.size()): - if i == 0: - pattern_string += cased_patterns[i] - else: - pattern_string += "|" + cased_patterns[i] - pattern_string += ")(?(2)[\\s\\S]*?\\*\\/|.*)" - return pattern_string - - -func create_todo(todo_string: String, script_path: String) -> Todo: - var todo := Todo.new() - var regex = RegEx.new() - for pattern in cased_patterns: - if regex.compile(pattern) == OK: - var result : RegExMatch = regex.search(todo_string) - if result: - todo.pattern = pattern - todo.title = result.strings[0] - else: - continue - else: - printerr("Error compiling " + pattern) - - todo.content = todo_string - todo.script_path = script_path - return todo - - -func _on_active_script_changed(script) -> void: - if _dockUI: - if _dockUI.tabs.current_tab == 1: - _dockUI.build_tree() diff --git a/addons/Todo_Manager/plugin.gd.uid b/addons/Todo_Manager/plugin.gd.uid deleted file mode 100644 index f431d76d..00000000 --- a/addons/Todo_Manager/plugin.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dsjpaje1h786x diff --git a/addons/Todo_Manager/todo.cfg b/addons/Todo_Manager/todo.cfg deleted file mode 100644 index d6967b63..00000000 --- a/addons/Todo_Manager/todo.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[scripts] - -full_path=false -sort_alphabetical=true -script_colour=Color(0.8, 0.807843, 0.827451, 1) -ignore_paths=Array[String](["addons"]) - -[patterns] - -patterns=[["\\bTODO\\b", Color(0.588235, 0.945098, 0.678431, 1), 0], ["\\bHACK\\b", Color(0.835294, 0.737255, 0.439216, 1), 0], ["\\bFIXME\\b", Color(0.835294, 0.439216, 0.439216, 1), 0]] - -[config] - -auto_refresh=true -builtin_enabled=false diff --git a/addons/Todo_Manager/todoItem_class.gd b/addons/Todo_Manager/todoItem_class.gd deleted file mode 100644 index 9bcb000b..00000000 --- a/addons/Todo_Manager/todoItem_class.gd +++ /dev/null @@ -1,18 +0,0 @@ -@tool -extends RefCounted - -var script_path : String -var todos : Array - -func _init(script_path: String, todos: Array): - self.script_path = script_path - self.todos = todos - -func get_short_path() -> String: - var temp_array := script_path.rsplit('/', false, 1) - var short_path : String - if not temp_array.size() > 1: - short_path = "(!)" + temp_array[0] - else: - short_path = temp_array[1] - return short_path diff --git a/addons/Todo_Manager/todoItem_class.gd.uid b/addons/Todo_Manager/todoItem_class.gd.uid deleted file mode 100644 index 8b7bda11..00000000 --- a/addons/Todo_Manager/todoItem_class.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://xowpbrd00trg diff --git a/addons/Todo_Manager/todo_class.gd b/addons/Todo_Manager/todo_class.gd deleted file mode 100644 index af6b26b3..00000000 --- a/addons/Todo_Manager/todo_class.gd +++ /dev/null @@ -1,9 +0,0 @@ -@tool -extends RefCounted - - -var pattern : String -var title : String -var content : String -var script_path : String -var line_number : int diff --git a/addons/Todo_Manager/todo_class.gd.uid b/addons/Todo_Manager/todo_class.gd.uid deleted file mode 100644 index 7b5f994e..00000000 --- a/addons/Todo_Manager/todo_class.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://2w7itjst8cfj diff --git a/addons/beautify_code_on_save/plugin.cfg b/addons/beautify_code_on_save/plugin.cfg new file mode 100644 index 00000000..ac305754 --- /dev/null +++ b/addons/beautify_code_on_save/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Beautify Code on Save" +description="Automatically formats and lints GDScript files on save using gdformat and gdlint." +author="nuevocharrua" +version="1.1.1" +script="plugin.gd" diff --git a/addons/beautify_code_on_save/plugin.gd b/addons/beautify_code_on_save/plugin.gd new file mode 100644 index 00000000..8a951812 --- /dev/null +++ b/addons/beautify_code_on_save/plugin.gd @@ -0,0 +1,210 @@ +@tool +extends EditorPlugin + +const SUCCESS: int = 0 +const AUTO_RELOAD_SETTING = "text_editor/behavior/files/auto_reload_scripts_on_external_change" +const ENABLE_EXTERNAL_EDITOR = "text_editor/external/use_external_editor" +# Plugin custom settings +const GDFORMAT_PATH_SETTING = "beautify_code_on_save/paths/gdformat_path" +const GDLINT_PATH_SETTING = "beautify_code_on_save/paths/gdlint_path" + +var original_reload_setting: bool +var original_external_editor: bool + + +func _enter_tree() -> void: + var settings = get_editor_interface().get_editor_settings() + + # Save original settings + original_reload_setting = settings.get_setting(AUTO_RELOAD_SETTING) + original_external_editor = settings.get_setting(ENABLE_EXTERNAL_EDITOR) + + # Configure auto-reload and external editor + settings.set_setting(AUTO_RELOAD_SETTING, true) + settings.set_setting(ENABLE_EXTERNAL_EDITOR, false) + settings.set_initial_value(AUTO_RELOAD_SETTING, true, false) + settings.set_initial_value(ENABLE_EXTERNAL_EDITOR, false, false) + + # Register plugin settings + _setup_plugin_settings(settings) + + resource_saved.connect(_on_resource_saved_deferred) + + +func _exit_tree() -> void: + resource_saved.disconnect(_on_resource_saved_deferred) + + var settings = get_editor_interface().get_editor_settings() + settings.set_setting(AUTO_RELOAD_SETTING, original_reload_setting) + settings.set_setting(ENABLE_EXTERNAL_EDITOR, original_external_editor) + + +func _setup_plugin_settings(settings: EditorSettings) -> void: + # Detect default paths + var default_gdformat = _detect_default_path("gdformat") + var default_gdlint = _detect_default_path("gdlint") + + # Register settings + if not settings.has_setting(GDFORMAT_PATH_SETTING): + settings.set_setting(GDFORMAT_PATH_SETTING, default_gdformat) + if not settings.has_setting(GDLINT_PATH_SETTING): + settings.set_setting(GDLINT_PATH_SETTING, default_gdlint) + + # Configure settings metadata + ( + settings + .add_property_info( + { + "name": GDFORMAT_PATH_SETTING, + "type": TYPE_STRING, + "hint": PROPERTY_HINT_GLOBAL_FILE, + "hint_string": "", + } + ) + ) + ( + settings + .add_property_info( + { + "name": GDLINT_PATH_SETTING, + "type": TYPE_STRING, + "hint": PROPERTY_HINT_GLOBAL_FILE, + "hint_string": "", + } + ) + ) + + +func _detect_default_path(command: String) -> String: + # Try to detect command path using 'which' + var output = [] + var exit_code = OS.execute("which", [command], output, true) + + if exit_code == SUCCESS and not output.is_empty(): + return output[0].strip_edges() + + # Common paths as fallback + var common_paths = [ + "/usr/local/bin/" + command, + "/usr/bin/" + command, + OS.get_environment("HOME") + "/.local/bin/" + command + ] + + for path in common_paths: + if FileAccess.file_exists(path): + return path + + return "" + + +func _on_resource_saved_deferred(script: Resource) -> void: + call_deferred("_on_resource_saved", script) + + +func _on_resource_saved(script: Resource) -> void: + if not script is Script: + return + + var current_script = get_editor_interface().get_script_editor().get_current_script() + if current_script != script: + return + + var text_edit = ( + get_editor_interface().get_script_editor().get_current_editor().get_base_editor() + ) + var file_path = ProjectSettings.globalize_path(script.resource_path) + + # Get paths from settings + var settings = get_editor_interface().get_editor_settings() + var gdformat_path = settings.get_setting(GDFORMAT_PATH_SETTING) + var gdlint_path = settings.get_setting(GDLINT_PATH_SETTING) + + # Add date time + var date_time = Time.get_datetime_string_from_system() + print("\n%s" % date_time) + + # First run gdformat + if not gdformat_path or not FileAccess.file_exists(gdformat_path): + push_warning("❌ GDFormat not found. Please configure the path in Editor Settings.") + else: + var gdformat_output = [] + var gdformat_exit_code = OS.execute(gdformat_path, [file_path], gdformat_output, true) + + if gdformat_exit_code == SUCCESS: + await get_tree().process_frame + var formatted_source = FileAccess.get_file_as_string(script.resource_path) + _reload_script(text_edit, formatted_source) + print("✓ GDFormat: Successfully formatted: '%s'" % script.resource_path) + else: + push_error("❌ GDFormat Error: " + str(gdformat_output)) + return # If formatting fails, don't continue with linting + + # Then run gdlint on the formatted code + if not gdlint_path or not FileAccess.file_exists(gdlint_path): + push_warning("❌ GDLint not found. Please configure the path in Editor Settings.") + else: + var gdlint_output = [] + var gdlint_exit_code = OS.execute(gdlint_path, [file_path], gdlint_output, true) + + if gdlint_exit_code != SUCCESS: + _show_lint_errors(gdlint_output[0], file_path) + else: + print("✓ GDLint : Successfully linted : '%s'" % script.resource_path) + + +func _reload_script(text_edit: TextEdit, source_code: String) -> void: + # Save cursor and scroll position + var column = text_edit.get_caret_column() + var row = text_edit.get_caret_line() + var scroll_position_h = text_edit.scroll_horizontal + var scroll_position_v = text_edit.scroll_vertical + + # Update text + text_edit.text = source_code + + # Restore cursor and scroll position + + text_edit.set_caret_column(column) + text_edit.set_caret_line(row) + text_edit.scroll_horizontal = scroll_position_h + text_edit.scroll_vertical = scroll_position_v + + # Mark as saved + text_edit.tag_saved_version() + + +func _show_lint_errors(output: String, path: String) -> void: + print("\n⚠ GDLint found the following issues:") + print("File: %s" % _get_res_path(path)) + push_error("❌ GDLint Error: Please see output with more information.") + var lines = output.split("\n") + var error_count := 0 + + for line in lines: + if line.is_empty() or line.begins_with("Failure:"): + continue + + var error_parts = line.split(": Error: ") + if error_parts.size() >= 2: + error_count += 1 + var location = error_parts[0].get_file() + var line_number = ( + error_parts[0].split(":")[2] + if OS.get_name() == "Windows" + else error_parts[0].split(":")[1] + ) + var error_msg = error_parts[1] + + print("%d) Line %s: %s" % [error_count, line_number, error_msg]) + + print("\nTotal: %d issues found" % error_count) + + +func _get_res_path(absolute_path: String) -> String: + var project_root = ProjectSettings.globalize_path("res://") + if absolute_path.begins_with(project_root): + return "res://" + absolute_path.substr(project_root.length()) + var addons_pos = absolute_path.find("/addons/") + if addons_pos != -1: + return "res://" + absolute_path.substr(addons_pos + 1) + return absolute_path diff --git a/addons/beautify_code_on_save/plugin.gd.uid b/addons/beautify_code_on_save/plugin.gd.uid new file mode 100644 index 00000000..fb83ed2f --- /dev/null +++ b/addons/beautify_code_on_save/plugin.gd.uid @@ -0,0 +1 @@ +uid://bd1mkvvfjhvfr diff --git a/addons/gdUnit4/GdUnitRunner.cfg b/addons/gdUnit4/GdUnitRunner.cfg index b2caafaf..26f3d367 100644 --- a/addons/gdUnit4/GdUnitRunner.cfg +++ b/addons/gdUnit4/GdUnitRunner.cfg @@ -1,1060 +1,6 @@ { + "exit_on_first_fail": false, "server_port": 31002, - "tests": [ - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_from_dict", - "fully_qualified_name": "unit.test_action_node.test_from_dict", - "guid": "8b3a8d57-f923462-2be70e5-92fbd40cd2", - "line_number": 17, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_action_node.gd", - "suite_name": "test_action_node", - "test_name": "test_from_dict" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_arguments_to_dict", - "fully_qualified_name": "unit.test_action_node.test_arguments_to_dict", - "guid": "4f7bdc24-4edf40f-e82ee0e-bdfda0cb23", - "line_number": 36, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_action_node.gd", - "suite_name": "test_action_node", - "test_name": "test_arguments_to_dict" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_option", - "fully_qualified_name": "unit.test_choice_node.test_add_option", - "guid": "d68db92d-45f14f0-8bde5f8-d5183d84f9", - "line_number": 28, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_choice_node.gd", - "suite_name": "test_choice_node", - "test_name": "test_add_option" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_to_fields", - "fully_qualified_name": "unit.test_choice_node.test_to_fields", - "guid": "091d9df9-b72849c-08391cd-1706aebafc", - "line_number": 50, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_choice_node.gd", - "suite_name": "test_choice_node", - "test_name": "test_to_fields" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_integration", - "fully_qualified_name": "unit.test_choice_node.test_language_integration", - "guid": "27af2362-abfe447-fa035ff-b9634a8fbe", - "line_number": 62, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_choice_node.gd", - "suite_name": "test_choice_node", - "test_name": "test_language_integration" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_restore_options", - "fully_qualified_name": "unit.test_choice_node.test_restore_options", - "guid": "a987c51b-b2c8494-58cfbcc-6d74538314", - "line_number": 102, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_choice_node.gd", - "suite_name": "test_choice_node", - "test_name": "test_restore_options" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_update_parent", - "fully_qualified_name": "unit.test_choice_node.test_update_parent", - "guid": "3fd45d76-5b7e4dd-b99ae16-474e96f72d", - "line_number": 130, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_choice_node.gd", - "suite_name": "test_choice_node", - "test_name": "test_update_parent" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_root", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_add_root", - "guid": "179cd145-74104e6-d901e79-2e1c32869d", - "line_number": 15, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_add_root" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_tab_first", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_add_tab_first", - "guid": "4d6e51e7-6735474-9ae787f-1865f58e1a", - "line_number": 29, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_add_tab_first" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_tab_with_previous", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_add_tab_with_previous", - "guid": "927774e3-b0aa4bd-ab9f4f3-191e66193d", - "line_number": 35, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_add_tab_with_previous" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_connect_side_panel", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_connect_side_panel", - "guid": "53de68af-9cea454-4928262-9633a9fac5", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_connect_side_panel" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_commit_side_panel", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_commit_side_panel", - "guid": "e5780596-18e5455-5838115-1e2246c817", - "line_number": 56, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_commit_side_panel" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_get_current_graph_edit", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_get_current_graph_edit", - "guid": "3d78084f-fa7f486-793e9d5-5bf1708199", - "line_number": 62, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_get_current_graph_edit" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_is_file_opened_false", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_is_file_opened_false", - "guid": "a1a8e4ff-2fed4cc-dabf680-90889f556b", - "line_number": 73, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_is_file_opened_false" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_is_file_opened_true", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_is_file_opened_true", - "guid": "11c8d58a-18c64a5-f83dd0a-adc73cd6b3", - "line_number": 77, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_is_file_opened_true" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_new_graph_edit", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_new_graph_edit", - "guid": "9e338c89-299c4ce-2809695-51fc5e117d", - "line_number": 84, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_new_graph_edit" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_on_tab_close_pressed_saved", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_on_tab_close_pressed_saved", - "guid": "0fe0c990-427a4b0-f9e5dcc-3ff12bfdc9", - "line_number": 93, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_on_tab_close_pressed_saved" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_on_tab_close_pressed_unsaved", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_on_tab_close_pressed_unsaved", - "guid": "0b07bd64-6b0d4d4-385c6ee-153ba92998", - "line_number": 102, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_on_tab_close_pressed_unsaved" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_show_current_config", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_show_current_config", - "guid": "92b08ef9-ac704f8-491345e-1c6221a481", - "line_number": 117, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_show_current_config" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_update_save_state_saved", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_update_save_state_saved", - "guid": "e7224d3f-6af64eb-5a28751-f66bef818c", - "line_number": 126, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_update_save_state_saved" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_update_save_state_unsaved", - "fully_qualified_name": "unit.test_graph_edit_switcher.test_update_save_state_unsaved", - "guid": "638451a5-4f104ab-5ad66b0-74d936f397", - "line_number": 136, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_edit_switcher.gd", - "suite_name": "test_graph_edit_switcher", - "test_name": "test_update_save_state_unsaved" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_enable_picker_mode", - "fully_qualified_name": "unit.test_graph_node_picker.test_enable_picker_mode", - "guid": "47f84251-0be6447-2b42090-345eabfdb8", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_node_picker.gd", - "suite_name": "test_graph_node_picker", - "test_name": "test_enable_picker_mode" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_enable_picker_mode_invalid_graph", - "fully_qualified_name": "unit.test_graph_node_picker.test_enable_picker_mode_invalid_graph", - "guid": "008f8541-9ff4415-9815729-0cf170ecd3", - "line_number": 27, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_node_picker.gd", - "suite_name": "test_graph_node_picker", - "test_name": "test_enable_picker_mode_invalid_graph" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_close", - "fully_qualified_name": "unit.test_graph_node_picker.test_close", - "guid": "cf67938e-d5cd4a3-b8694fd-2aa4d154f1", - "line_number": 37, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_graph_node_picker.gd", - "suite_name": "test_graph_node_picker", - "test_name": "test_close" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_delete_language", - "fully_qualified_name": "unit.test_language_switcher.test_delete_language", - "guid": "cb0d9463-7253478-db8f6f7-832e5adb39", - "line_number": 13, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_delete_language" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_delete_language_destroys_raw_data", - "fully_qualified_name": "unit.test_language_switcher.test_delete_language_destroys_raw_data", - "guid": "6d7cfb08-870e4aa-b8222aa-55377d0f6f", - "line_number": 24, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_delete_language_destroys_raw_data" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_delete_language_undo_restores_data", - "fully_qualified_name": "unit.test_language_switcher.test_delete_language_undo_restores_data", - "guid": "5609cea3-f7c148a-bbe5f1a-bc4a4d3d38", - "line_number": 37, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_delete_language_undo_restores_data" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_first_language_does_not_show_delete_button", - "fully_qualified_name": "unit.test_language_switcher.test_first_language_does_not_show_delete_button", - "guid": "eaf477a8-1762483-2931e83-12a56b0b70", - "line_number": 53, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_first_language_does_not_show_delete_button" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_get_value_renamed", - "fully_qualified_name": "unit.test_language_switcher.test_language_get_value_renamed", - "guid": "33d52919-c6b84ae-c959c33-c83aa10e33", - "line_number": 63, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_get_value_renamed" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_get_value_nonexist_after_switch", - "fully_qualified_name": "unit.test_language_switcher.test_language_get_value_nonexist_after_switch", - "guid": "7f24331f-17eb4bb-098e5b2-1b3c8e37ed", - "line_number": 72, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_get_value_nonexist_after_switch" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_set_value_locale_dictionary", - "fully_qualified_name": "unit.test_language_switcher.test_language_set_value_locale_dictionary", - "guid": "33600f8a-29d94c0-2b849ac-b7a0eaa988", - "line_number": 81, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_set_value_locale_dictionary" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_set_value_locale_dictionary_non_default", - "fully_qualified_name": "unit.test_language_switcher.test_language_set_value_locale_dictionary_non_default", - "guid": "14bc24e9-48b6468-5b306fe-2f10570ab9", - "line_number": 90, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_set_value_locale_dictionary_non_default" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_set_value_string", - "fully_qualified_name": "unit.test_language_switcher.test_language_set_value_string", - "guid": "4b8f1e19-91e8463-7880654-b5e8091b9e", - "line_number": 98, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_set_value_string" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_language_set_value_and_switch_locales", - "fully_qualified_name": "unit.test_language_switcher.test_language_set_value_and_switch_locales", - "guid": "931f554e-402b4bd-bb05c39-f39d9ca090", - "line_number": 104, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_language_set_value_and_switch_locales" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_load_languages", - "fully_qualified_name": "unit.test_language_switcher.test_load_languages", - "guid": "2f5c634f-483e43d-0a7ed37-4bd8e75b9a", - "line_number": 124, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_load_languages" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_load_languages_duplicate", - "fully_qualified_name": "unit.test_language_switcher.test_load_languages_duplicate", - "guid": "5b463b9b-9b3d4b9-592b40a-21442a6eb1", - "line_number": 132, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_language_switcher.gd", - "suite_name": "test_language_switcher", - "test_name": "test_load_languages_duplicate" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_to_dict", - "fully_qualified_name": "unit.test_option_node.test_to_dict", - "guid": "76edd481-52fc4ce-4ade2ab-83a0c7e2b5", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_option_node.gd", - "suite_name": "test_option_node", - "test_name": "test_to_dict" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_from_dict_v2", - "fully_qualified_name": "unit.test_option_node.test_from_dict_v2", - "guid": "562978a7-dba945f-680a83f-6839cf6e2a", - "line_number": 20, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_option_node.gd", - "suite_name": "test_option_node", - "test_name": "test_from_dict_v2" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_from_dict_v3", - "fully_qualified_name": "unit.test_option_node.test_from_dict_v3", - "guid": "fb9dc20c-84f4432-5920b31-d07ed25118", - "line_number": 40, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_option_node.gd", - "suite_name": "test_option_node", - "test_name": "test_from_dict_v3" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 0, - "display_name": "test_absolute_to_relative_linux:0 ('/home/mrsharpener/Pen/w/b/20/ac.mp3', 'w/b/20/ac.mp3')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_linux.test_absolute_to_relative_linux:0 ('/home/mrsharpener/Pen/w/b/20/ac.mp3', 'w/b/20/ac.mp3')", - "guid": "392e4080-e447468-c84de6b-0ec208648b", - "line_number": 29, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 1, - "display_name": "test_absolute_to_relative_linux:1 ('/home/mrsharpener/Pen/1.txt', '1.txt')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_linux.test_absolute_to_relative_linux:1 ('/home/mrsharpener/Pen/1.txt', '1.txt')", - "guid": "a3b19b01-b2a74d3-0b2875a-92e4609a26", - "line_number": 29, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 2, - "display_name": "test_absolute_to_relative_linux:2 ('/home/missyeraser/K/y.o.u', '../../missyeraser/K/y.o.u')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_linux.test_absolute_to_relative_linux:2 ('/home/missyeraser/K/y.o.u', '../../missyeraser/K/y.o.u')", - "guid": "f639d195-2a6349b-18caa33-11b71fa0a0", - "line_number": 29, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 3, - "display_name": "test_absolute_to_relative_linux:3 ('/opt/x/1/bro', '/opt/x/1/bro')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_linux.test_absolute_to_relative_linux:3 ('/opt/x/1/bro', '/opt/x/1/bro')", - "guid": "82e7695e-f29745c-48cefb6-41c107abe8", - "line_number": 29, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 0, - "display_name": "test_absolute_to_relative_windows:0 ('C:\\\\Users\\\\MrSharpener\\\\Pen\\\\w\\\\b\\\\20\\\\ac.mp3', 'w/b/20/ac.mp3')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_windows.test_absolute_to_relative_windows:0 ('C:\\\\Users\\\\MrSharpener\\\\Pen\\\\w\\\\b\\\\20\\\\ac.mp3', 'w/b/20/ac.mp3')", - "guid": "aff013d2-f15a419-5a7089b-d6faaefd1b", - "line_number": 36, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 1, - "display_name": "test_absolute_to_relative_windows:1 ('C:\\\\Users\\\\MrSharpener\\\\Pen\\\\1.txt', '1.txt')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_windows.test_absolute_to_relative_windows:1 ('C:\\\\Users\\\\MrSharpener\\\\Pen\\\\1.txt', '1.txt')", - "guid": "56ff097e-4f3747a-4af3a0b-e94e09acf4", - "line_number": 36, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 2, - "display_name": "test_absolute_to_relative_windows:2 ('C:\\\\Users\\\\MissyEraser\\\\K\\\\y.o.u', '../../MissyEraser/K/y.o.u')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_windows.test_absolute_to_relative_windows:2 ('C:\\\\Users\\\\MissyEraser\\\\K\\\\y.o.u', '../../MissyEraser/K/y.o.u')", - "guid": "af373b44-44f5418-7acb838-0e1952e4f4", - "line_number": 36, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 3, - "display_name": "test_absolute_to_relative_windows:3 ('E:\\\\10. __World\\\\g1d a', 'E:\\\\10. __World\\\\g1d a')", - "fully_qualified_name": "unit.test_path.test_absolute_to_relative_windows.test_absolute_to_relative_windows:3 ('E:\\\\10. __World\\\\g1d a', 'E:\\\\10. __World\\\\g1d a')", - "guid": "c513260a-bdd84c2-8847b21-000cfe33e4", - "line_number": 36, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_absolute_to_relative_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 0, - "display_name": "test_relative_to_absolute_linux:0 ('w/b/20/ac.mp3', '/home/mrsharpener/Pen/w/b/20/ac.mp3')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_linux.test_relative_to_absolute_linux:0 ('w/b/20/ac.mp3', '/home/mrsharpener/Pen/w/b/20/ac.mp3')", - "guid": "0af2839a-497b4bb-6b4d2d5-559d969dfd", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 1, - "display_name": "test_relative_to_absolute_linux:1 ('1.txt', '/home/mrsharpener/Pen/1.txt')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_linux.test_relative_to_absolute_linux:1 ('1.txt', '/home/mrsharpener/Pen/1.txt')", - "guid": "33322afb-3bee47b-aa39375-2655ff8c36", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 2, - "display_name": "test_relative_to_absolute_linux:2 ('../../missyeraser/K/y.o.u', '/home/missyeraser/K/y.o.u')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_linux.test_relative_to_absolute_linux:2 ('../../missyeraser/K/y.o.u', '/home/missyeraser/K/y.o.u')", - "guid": "64d6e013-8ae9425-693a675-ff724a8564", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 3, - "display_name": "test_relative_to_absolute_linux:3 ('/opt/x/1/bro', '/opt/x/1/bro')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_linux.test_relative_to_absolute_linux:3 ('/opt/x/1/bro', '/opt/x/1/bro')", - "guid": "c6976c5f-7a944a9-dbc0515-14eaba95de", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_linux" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 0, - "display_name": "test_relative_to_absolute_windows:0 ('w/b/20/ac.mp3', 'C:/Users/MrSharpener/Pen/w/b/20/ac.mp3')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_windows.test_relative_to_absolute_windows:0 ('w/b/20/ac.mp3', 'C:/Users/MrSharpener/Pen/w/b/20/ac.mp3')", - "guid": "c51d8e5d-4436449-aa3130b-485161d13f", - "line_number": 54, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 1, - "display_name": "test_relative_to_absolute_windows:1 ('1.txt', 'C:/Users/MrSharpener/Pen/1.txt')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_windows.test_relative_to_absolute_windows:1 ('1.txt', 'C:/Users/MrSharpener/Pen/1.txt')", - "guid": "29bc0f6c-3514484-08e1b53-7b289515a7", - "line_number": 54, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 2, - "display_name": "test_relative_to_absolute_windows:2 ('../../MissyEraser/K/y.o.u', 'C:/Users/MissyEraser/K/y.o.u')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_windows.test_relative_to_absolute_windows:2 ('../../MissyEraser/K/y.o.u', 'C:/Users/MissyEraser/K/y.o.u')", - "guid": "13f4983f-5047424-ea8054c-b0dde53a81", - "line_number": 54, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": 3, - "display_name": "test_relative_to_absolute_windows:3 ('E:/10. __World/g1d a', 'E:/10. __World/g1d a')", - "fully_qualified_name": "unit.test_path.test_relative_to_absolute_windows.test_relative_to_absolute_windows:3 ('E:/10. __World/g1d a', 'E:/10. __World/g1d a')", - "guid": "5c2e662c-33ae461-180a014-56fb771303", - "line_number": 54, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_path.gd", - "suite_name": "test_path", - "test_name": "test_relative_to_absolute_windows" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_from_empty", - "fully_qualified_name": "unit.test_recent_file_container.test_add_from_empty", - "guid": "a2ff69d6-c4184b3-48b4ce9-0c48b5b0b3", - "line_number": 16, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_add_from_empty" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_add_override", - "fully_qualified_name": "unit.test_recent_file_container.test_add_override", - "guid": "e4eeea69-7e7043b-4a1a3bd-e1d9d2ceec", - "line_number": 22, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_add_override" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_create_file", - "fully_qualified_name": "unit.test_recent_file_container.test_create_file", - "guid": "66b76b2f-1514457-19863c7-07ce2f667c", - "line_number": 34, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_create_file" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_load_file_empty", - "fully_qualified_name": "unit.test_recent_file_container.test_load_file_empty", - "guid": "ff1a3d15-7aff44e-38fc6f9-a57397bdfb", - "line_number": 40, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_load_file_empty" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_load_file_exclude_not_exist", - "fully_qualified_name": "unit.test_recent_file_container.test_load_file_exclude_not_exist", - "guid": "ee1a90e2-075e4d4-c8f9e94-27ec9bb350", - "line_number": 45, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_load_file_exclude_not_exist" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_load_file_existing", - "fully_qualified_name": "unit.test_recent_file_container.test_load_file_existing", - "guid": "ed732e73-3f79405-2abdf63-9c5148005a", - "line_number": 57, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_recent_file_container.gd", - "suite_name": "test_recent_file_container", - "test_name": "test_load_file_existing" - }, - { - "@path": "res://addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd", - "@subpath": "", - "assembly_location": "", - "attribute_index": -1, - "display_name": "test_backwards_compatibility", - "fully_qualified_name": "unit.test_sentence_node.test_backwards_compatibility", - "guid": "2e03e022-65954f3-989932f-d198ecced7", - "line_number": 4, - "metadata": { - - }, - "require_godot_runtime": true, - "source_file": "res://unit/test_sentence_node.gd", - "suite_name": "test_sentence_node", - "test_name": "test_backwards_compatibility" - } - ], + "tests": [], "version": "5.0" } \ No newline at end of file diff --git a/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid b/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid index 94633f65..2bac9c82 100644 --- a/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid +++ b/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid @@ -1 +1 @@ -uid://p88p3dkvm32r +uid://beelf5du15t6i diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd b/addons/gdUnit4/bin/GdUnitCopyLog.gd index fdd63d2c..8b228051 100644 --- a/addons/gdUnit4/bin/GdUnitCopyLog.gd +++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd @@ -82,9 +82,9 @@ func _process(_delta: float) -> bool: func set_current_report_path() -> void: # scan for latest report directory var iteration := GdUnitFileAccess.find_last_path_index( - _report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX + _report_root_path, GdUnitConstants.REPORT_DIR_PREFIX ) - _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitHtmlReport.REPORT_DIR_PREFIX, iteration] + _current_report_path = "%s/%s%d" % [_report_root_path, GdUnitConstants.REPORT_DIR_PREFIX, iteration] func set_report_directory(path: String) -> void: diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid index 33c3e3f6..4542d319 100644 --- a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid +++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid @@ -1 +1 @@ -uid://qnmevknqtkcr +uid://cdgs4g2m7by8p diff --git a/addons/gdUnit4/plugin.cfg b/addons/gdUnit4/plugin.cfg index 45d0af55..8293c3f2 100644 --- a/addons/gdUnit4/plugin.cfg +++ b/addons/gdUnit4/plugin.cfg @@ -3,5 +3,5 @@ name="gdUnit4" description="Unit Testing Framework for Godot Scripts" author="Mike Schulze" -version="5.0.0" +version="6.1.1" script="plugin.gd" diff --git a/addons/gdUnit4/plugin.gd b/addons/gdUnit4/plugin.gd index cefdc412..7cc3afa3 100644 --- a/addons/gdUnit4/plugin.gd +++ b/addons/gdUnit4/plugin.gd @@ -1,24 +1,39 @@ @tool extends EditorPlugin -# We need to define manually the slot id's, to be downwards compatible -const CONTEXT_SLOT_FILESYSTEM = 1 # EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM -const CONTEXT_SLOT_SCRIPT_EDITOR = 2 # EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR - var _gd_inspector: Control var _gd_console: Control -var _gd_filesystem_context_menu: Variant -var _gd_scripteditor_context_menu: Variant +var _filesystem_context_menu: EditorContextMenuPlugin +var _editor_context_menu: EditorContextMenuPlugin +var _editor_code_context_menu: EditorContextMenuPlugin func _enter_tree() -> void: + var inferred_declaration: int = ProjectSettings.get_setting("debug/gdscript/warnings/inferred_declaration") + + var is_gdunit_excluded_warnings: bool = false + if Engine.get_version_info().hex >= 0x40600: + var dirctrory_rules: Dictionary = ProjectSettings.get_setting("debug/gdscript/warnings/directory_rules") + if dirctrory_rules.has("res://addons/gdUnit4") and dirctrory_rules["res://addons/gdUnit4"] == 0: + is_gdunit_excluded_warnings = true + else: + is_gdunit_excluded_warnings = ProjectSettings.get_setting("debug/gdscript/warnings/exclude_addons") + if !is_gdunit_excluded_warnings and inferred_declaration != 0: + printerr("GdUnit4: 'inferred_declaration' is set to Warning/Error!") + if Engine.get_version_info().hex >= 0x40600: + printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded the addon (debug/gdscript/warnings/directory_rules)") + else: + printerr("GdUnit4 is not 'inferred_declaration' save, you have to excluded addons (debug/gdscript/warnings/exclude_addons)") + printerr("Loading GdUnit4 Plugin failed.") + return + if check_running_in_test_env(): @warning_ignore("return_value_discarded") GdUnitCSIMessageWriter.new().prints_warning("It was recognized that GdUnit4 is running in a test environment, therefore the GdUnit4 plugin will not be executed!") return - if Engine.get_version_info().hex < 0x40300: - prints("The GdUnit4 plugin requires Godot version 4.3 or higher to run.") + if Engine.get_version_info().hex < 0x40500: + prints("This GdUnit4 plugin version '%s' requires Godot version '4.5' or higher to run." % GdUnit4Version.current()) return GdUnitSettings.setup() # Install the GdUnit Inspector @@ -27,11 +42,12 @@ func _enter_tree() -> void: add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UR, _gd_inspector) # Install the GdUnit Console _gd_console = (load("res://addons/gdUnit4/src/ui/GdUnitConsole.tscn") as PackedScene).instantiate() - var control := add_control_to_bottom_panel(_gd_console, "gdUnitConsole") - @warning_ignore("unsafe_method_access") + var control: Control = add_control_to_bottom_panel(_gd_console, "gdUnitConsole") await _gd_console.setup_update_notification(control) - if GdUnit4CSharpApiLoader.is_dotnet_supported(): + if GdUnit4CSharpApiLoader.is_api_loaded(): prints("GdUnit4Net version '%s' loaded." % GdUnit4CSharpApiLoader.version()) + else: + prints("No GdUnit4Net found.") # Connect to be notified for script changes to be able to discover new tests GdUnitTestDiscoverGuard.instance() @warning_ignore("return_value_discarded") @@ -49,48 +65,33 @@ func _exit_tree() -> void: if is_instance_valid(_gd_console): remove_control_from_bottom_panel(_gd_console) _gd_console.free() - var gdUnitTools := load("res://addons/gdUnit4/src/core/GdUnitTools.gd") - @warning_ignore("unsafe_method_access") + var gdUnitTools: GDScript = load("res://addons/gdUnit4/src/core/GdUnitTools.gd") gdUnitTools.dispose_all(true) prints("Unload GdUnit4 Plugin success") func check_running_in_test_env() -> bool: - var args := OS.get_cmdline_args() + var args: PackedStringArray = OS.get_cmdline_args() args.append_array(OS.get_cmdline_user_args()) return DisplayServer.get_name() == "headless" or args.has("--selftest") or args.has("--add") or args.has("-a") or args.has("--quit-after") or args.has("--import") func _add_context_menus() -> void: - if Engine.get_version_info().hex >= 0x40400: - # With Godot 4.4 we have to use the 'add_context_menu_plugin' to register editor context menus - _gd_filesystem_context_menu = _create_context_menu("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx") - call_deferred("add_context_menu_plugin", CONTEXT_SLOT_FILESYSTEM, _gd_filesystem_context_menu) - # the CONTEXT_SLOT_SCRIPT_EDITOR is adding to the script panel instead of script editor see https://github.com/godotengine/godot/pull/100556 - #_gd_scripteditor_context_menu = _preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx") - #call_deferred("add_context_menu_plugin", CONTEXT_SLOT_SCRIPT_EDITOR, _gd_scripteditor_context_menu) - # so we use the old hacky way to add the context menu - _gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new()) - else: - # TODO Delete it if the minimum requirement for the plugin is set to Godot 4.4. - _gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd").new()) - _gd_inspector.add_child(preload("res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd").new()) + _filesystem_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd").new() + _editor_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new() + _editor_code_context_menu = preload("res://addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd").new() + add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_FILESYSTEM, _filesystem_context_menu) + add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR, _editor_context_menu) + add_context_menu_plugin(EditorContextMenuPlugin.CONTEXT_SLOT_SCRIPT_EDITOR_CODE, _editor_code_context_menu) func _remove_context_menus() -> void: - if is_instance_valid(_gd_filesystem_context_menu): - call_deferred("remove_context_menu_plugin", _gd_filesystem_context_menu) - if is_instance_valid(_gd_scripteditor_context_menu): - call_deferred("remove_context_menu_plugin", _gd_scripteditor_context_menu) - - -func _create_context_menu(script_path: String) -> Variant: - var context_menu_script := GDScript.new() - context_menu_script.source_code = FileAccess.get_file_as_string(script_path) - var err := context_menu_script.reload(true) - if err != OK: - push_error("Can't create context menu %s, error: %s" % [script_path, error_string(err)]) - return context_menu_script.new() + if is_instance_valid(_filesystem_context_menu): + remove_context_menu_plugin(_filesystem_context_menu) + if is_instance_valid(_editor_context_menu): + remove_context_menu_plugin(_editor_context_menu) + if is_instance_valid(_editor_code_context_menu): + remove_context_menu_plugin(_editor_code_context_menu) func _on_resource_saved(resource: Resource) -> void: diff --git a/addons/gdUnit4/plugin.gd.uid b/addons/gdUnit4/plugin.gd.uid index cf0dbb04..d01a0597 100644 --- a/addons/gdUnit4/plugin.gd.uid +++ b/addons/gdUnit4/plugin.gd.uid @@ -1 +1 @@ -uid://bjat85epf7ofm +uid://bhjos16vxaecd diff --git a/addons/gdUnit4/runtest.cmd b/addons/gdUnit4/runtest.cmd index 3f4c9a82..ad5da4d9 100644 --- a/addons/gdUnit4/runtest.cmd +++ b/addons/gdUnit4/runtest.cmd @@ -2,7 +2,7 @@ setlocal enabledelayedexpansion :: Initialize variables -set "godot_bin=" +set "godot_binary=" set "filtered_args=" :: Process all arguments @@ -10,8 +10,8 @@ set "i=0" :parse_args if "%~1"=="" goto end_parse_args -if "%~1"=="--godot_bin" ( - set "godot_bin=%~2" +if "%~1"=="--godot_binary" ( + set "godot_binary=%~2" shift shift ) else ( @@ -21,28 +21,28 @@ if "%~1"=="--godot_bin" ( goto parse_args :end_parse_args -:: If --godot_bin wasn't provided, fallback to environment variable -if "!godot_bin!"=="" ( - set "godot_bin=%GODOT_BIN%" +:: If --godot_binary wasn't provided, fallback to environment variable +if "!godot_binary!"=="" ( + set "godot_binary=%GODOT_BIN%" ) -:: Check if we have a godot_bin value from any source -if "!godot_bin!"=="" ( +:: Check if we have a godot_binary value from any source +if "!godot_binary!"=="" ( echo Godot binary path is not specified. echo Please either: echo - Set the environment variable: set GODOT_BIN=C:\path\to\godot.exe - echo - Or use the --godot_bin argument: --godot_bin C:\path\to\godot.exe + echo - Or use the --godot_binary argument: --godot_binary C:\path\to\godot.exe exit /b 1 ) :: Check if the Godot binary exists -if not exist "!godot_bin!" ( - echo Error: The specified Godot binary '!godot_bin!' does not exist. +if not exist "!godot_binary!" ( + echo Error: The specified Godot binary '!godot_binary!' does not exist. exit /b 1 ) :: Get Godot version and check if it's a mono build -for /f "tokens=*" %%i in ('"!godot_bin!" --version') do set GODOT_VERSION=%%i +for /f "tokens=*" %%i in ('"!godot_binary!" --version') do set GODOT_VERSION=%%i echo !GODOT_VERSION! | findstr /I "mono" >nul if !errorlevel! equ 0 ( echo Godot .NET detected @@ -52,11 +52,11 @@ if !errorlevel! equ 0 ( ) :: Run the tests with the filtered arguments -"!godot_bin!" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd !filtered_args! +"!godot_binary!" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd !filtered_args! set exit_code=%ERRORLEVEL% echo Run tests ends with %exit_code% :: Run the copy log command -"!godot_bin!" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd !filtered_args! > nul +"!godot_binary!" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd !filtered_args! > nul set exit_code2=%ERRORLEVEL% exit /b %exit_code% diff --git a/addons/gdUnit4/runtest.sh b/addons/gdUnit4/runtest.sh index 9e3e006f..f0269efb 100644 --- a/addons/gdUnit4/runtest.sh +++ b/addons/gdUnit4/runtest.sh @@ -1,49 +1,49 @@ #!/bin/bash # Check for command-line argument -godot_bin="" +godot_binary="" filtered_args="" # Process all arguments with a more compatible approach while [ $# -gt 0 ]; do - if [ "$1" = "--godot_bin" ] && [ $# -gt 1 ]; then + if [ "$1" = "--godot_binary" ] && [ $# -gt 1 ]; then # Get the next argument as the value - godot_bin="$2" + godot_binary="$2" shift 2 else - # Keep non-godot_bin arguments for passing to Godot + # Keep non-godot_binary arguments for passing to Godot filtered_args="$filtered_args $1" shift fi done -# If --godot_bin wasn't provided, fallback to environment variable -if [ -z "$godot_bin" ]; then - godot_bin="$GODOT_BIN" +# If --godot_binary wasn't provided, fallback to environment variable +if [ -z "$godot_binary" ]; then + godot_binary="$GODOT_BIN" fi -# Check if we have a godot_bin value from any source -if [ -z "$godot_bin" ]; then +# Check if we have a godot_binary value from any source +if [ -z "$godot_binary" ]; then echo "Godot binary path is not specified." echo "Please either:" echo " - Set the environment variable: export GODOT_BIN=/path/to/godot" - echo " - Or use the --godot_bin argument: --godot_bin /path/to/godot" + echo " - Or use the --godot_binary argument: --godot_binary /path/to/godot" exit 1 fi # Check if the Godot binary exists and is executable -if [ ! -f "$godot_bin" ]; then - echo "Error: The specified Godot binary '$godot_bin' does not exist." +if [ ! -f "$godot_binary" ]; then + echo "Error: The specified Godot binary '$godot_binary' does not exist." exit 1 fi -if [ ! -x "$godot_bin" ]; then - echo "Error: The specified Godot binary '$godot_bin' is not executable." +if [ ! -x "$godot_binary" ]; then + echo "Error: The specified Godot binary '$godot_binary' is not executable." exit 1 fi # Get Godot version and check if it's a .NET build -GODOT_VERSION=$("$godot_bin" --version) +GODOT_VERSION=$("$godot_binary" --version) if echo "$GODOT_VERSION" | grep -i "mono" > /dev/null; then echo "Godot .NET detected" echo "Compiling c# classes ... Please Wait" @@ -52,11 +52,11 @@ if echo "$GODOT_VERSION" | grep -i "mono" > /dev/null; then fi # Run the tests with the filtered arguments -"$godot_bin" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $filtered_args +"$godot_binary" --path . -s -d res://addons/gdUnit4/bin/GdUnitCmdTool.gd $filtered_args exit_code=$? echo "Run tests ends with $exit_code" # Run the copy log command -"$godot_bin" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd $filtered_args > /dev/null +"$godot_binary" --headless --path . --quiet -s res://addons/gdUnit4/bin/GdUnitCopyLog.gd $filtered_args > /dev/null exit_code2=$? exit $exit_code diff --git a/addons/gdUnit4/src/Comparator.gd.uid b/addons/gdUnit4/src/Comparator.gd.uid index e941641d..8f272a1d 100644 --- a/addons/gdUnit4/src/Comparator.gd.uid +++ b/addons/gdUnit4/src/Comparator.gd.uid @@ -1 +1 @@ -uid://cm41y6w7wthm4 +uid://cxotwx8ih532e diff --git a/addons/gdUnit4/src/Fuzzers.gd b/addons/gdUnit4/src/Fuzzers.gd index f61ba6e6..c8689df3 100644 --- a/addons/gdUnit4/src/Fuzzers.gd +++ b/addons/gdUnit4/src/Fuzzers.gd @@ -1,34 +1,80 @@ -## A fuzzer implementation to provide default implementation +## Factory class providing convenient static methods to create various fuzzer instances.[br] +## +## Fuzzers is a utility class that simplifies the creation of different fuzzer types +## for testing purposes. It provides static factory methods that create pre-configured +## fuzzers with sensible defaults, making it easier to set up fuzz testing in your +## test suites without manually instantiating each fuzzer type.[br] +## +## This class acts as a central access point for all fuzzer types, improving code +## readability and reducing boilerplate in test cases.[br] +## +## @tutorial(Fuzzing Testing): https://en.wikipedia.org/wiki/Fuzzing class_name Fuzzers extends Resource -## Generates an random string with min/max length and given charset -static func rand_str(min_length: int, max_length :int, charset := StringFuzzer.DEFAULT_CHARSET) -> Fuzzer: +## Generates random strings with length between [param min_length] and +## [param max_length] (inclusive), using characters from [param charset]. +## See [StringFuzzer] for detailed documentation and examples. +static func rand_str(min_length: int, max_length: int, charset := StringFuzzer.DEFAULT_CHARSET) -> StringFuzzer: return StringFuzzer.new(min_length, max_length, charset) -## Generates an random integer in a range form to -static func rangei(from: int, to: int) -> Fuzzer: +## Creates a [BoolFuzzer] for generating random boolean values.[br] +## +## See [BoolFuzzer] for detailed documentation and examples. +static func boolean() -> BoolFuzzer: + return BoolFuzzer.new() + + +## Creates an [IntFuzzer] for generating random integers within a range.[br] +## +## Generates random integers between [param from] and [param to] (inclusive) +## using [constant IntFuzzer.NORMAL] mode. +## See [IntFuzzer] for detailed documentation and examples. +static func rangei(from: int, to: int) -> IntFuzzer: return IntFuzzer.new(from, to) -## Generates a randon float within in a given range -static func rangef(from: float, to: float) -> Fuzzer: + +## Creates a [FloatFuzzer] for generating random floats within a range.[br] +## +## Generates random float values between [param from] and [param to] (inclusive). +## See [FloatFuzzer] for detailed documentation and examples. +static func rangef(from: float, to: float) -> FloatFuzzer: return FloatFuzzer.new(from, to) -## Generates an random Vector2 in a range form to -static func rangev2(from: Vector2, to: Vector2) -> Fuzzer: + +## Creates a [Vector2Fuzzer] for generating random 2D vectors within a range.[br] +## +## Generates random Vector2 values where each component is bounded by +## [param from] and [param to] (inclusive). +## See [Vector2Fuzzer] for detailed documentation and examples. +static func rangev2(from: Vector2, to: Vector2) -> Vector2Fuzzer: return Vector2Fuzzer.new(from, to) -## Generates an random Vector3 in a range form to -static func rangev3(from: Vector3, to: Vector3) -> Fuzzer: +## Creates a [Vector3Fuzzer] for generating random 3D vectors within a range.[br] +## +## Generates random Vector3 values where each component is bounded by +## [param from] and [param to] (inclusive). +## See [Vector3Fuzzer] for detailed documentation and examples. +static func rangev3(from: Vector3, to: Vector3) -> Vector3Fuzzer: return Vector3Fuzzer.new(from, to) -## Generates an integer in a range form to that can be divided exactly by 2 -static func eveni(from: int, to: int) -> Fuzzer: + +## Creates an [IntFuzzer] that generates only even integers.[br] +## +## Generates random even integers between [param from] and [param to] (inclusive) +## using [constant IntFuzzer.EVEN] mode. +## See [IntFuzzer] for detailed documentation about even number generation. +static func eveni(from: int, to: int) -> IntFuzzer: return IntFuzzer.new(from, to, IntFuzzer.EVEN) -## Generates an integer in a range form to that cannot be divided exactly by 2 -static func oddi(from: int, to: int) -> Fuzzer: + +## Creates an [IntFuzzer] that generates only odd integers.[br] +## +## Generates random odd integers between [param from] and [param to] (inclusive) +## using [constant IntFuzzer.ODD] mode. +## See [IntFuzzer] for detailed documentation about odd number generation. +static func oddi(from: int, to: int) -> IntFuzzer: return IntFuzzer.new(from, to, IntFuzzer.ODD) diff --git a/addons/gdUnit4/src/Fuzzers.gd.uid b/addons/gdUnit4/src/Fuzzers.gd.uid index c16cd4aa..7ba3ec95 100644 --- a/addons/gdUnit4/src/Fuzzers.gd.uid +++ b/addons/gdUnit4/src/Fuzzers.gd.uid @@ -1 +1 @@ -uid://p6pp3cntrnd +uid://ckd308vgcfi1n diff --git a/addons/gdUnit4/src/GdUnitArrayAssert.gd b/addons/gdUnit4/src/GdUnitArrayAssert.gd index cc2226dd..eeb7ca6b 100644 --- a/addons/gdUnit4/src/GdUnitArrayAssert.gd +++ b/addons/gdUnit4/src/GdUnitArrayAssert.gd @@ -1,111 +1,90 @@ ## An Assertion Tool to verify array values -class_name GdUnitArrayAssert +@abstract class_name GdUnitArrayAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitArrayAssert: - return self +@abstract func is_null() -> GdUnitArrayAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitArrayAssert: - return self +@abstract func is_not_null() -> GdUnitArrayAssert ## Verifies that the current Array is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_equal(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_equal(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array is not equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_not_equal_ignoring_case(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitArrayAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitArrayAssert ## Verifies that the current Array is empty, it has a size of 0. -func is_empty() -> GdUnitArrayAssert: - return self +@abstract func is_empty() -> GdUnitArrayAssert ## Verifies that the current Array is not empty, it has a size of minimum 1. -func is_not_empty() -> GdUnitArrayAssert: - return self +@abstract func is_not_empty() -> GdUnitArrayAssert + ## Verifies that the current Array is the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitArrayAssert ## Verifies that the current Array is NOT the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_not_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitArrayAssert ## Verifies that the current Array has a size of given value. -@warning_ignore("unused_parameter") -func has_size(expectd: int) -> GdUnitArrayAssert: - return self +@abstract func has_size(expectd: int) -> GdUnitArrayAssert ## Verifies that the current Array contains the given values, in any order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same] -@warning_ignore("unused_parameter") -func contains(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly] -@warning_ignore("unused_parameter") -func contains_exactly(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_exactly(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br] ## The values are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_exactly_in_any_order] -@warning_ignore("unused_parameter") -func contains_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains the given values, in any order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains] -@warning_ignore("unused_parameter") -func contains_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in same order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains_exactly] -@warning_ignore("unused_parameter") -func contains_same_exactly(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array contains exactly only the given values and nothing else, in any order.[br] ## The values are compared by object reference, for deep parameter comparision use [method contains_exactly_in_any_order] -@warning_ignore("unused_parameter") -func contains_same_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array do NOT contains the given values, in any order.[br] @@ -113,13 +92,11 @@ func contains_same_exactly_in_any_order(expected :Variant) -> GdUnitArrayAssert: ## [b]Example:[/b] ## [codeblock] ## # will succeed -## assert_array([1, 2, 3, 4, 5]).not_contains([6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(6) ## # will fail -## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6) ## [/codeblock] -@warning_ignore("unused_parameter") -func not_contains(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func not_contains(...expected: Array) -> GdUnitArrayAssert ## Verifies that the current Array do NOT contains the given values, in any order.[br] @@ -127,45 +104,19 @@ func not_contains(expected :Variant) -> GdUnitArrayAssert: ## [b]Example:[/b] ## [codeblock] ## # will succeed -## assert_array([1, 2, 3, 4, 5]).not_contains([6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(6) ## # will fail -## assert_array([1, 2, 3, 4, 5]).not_contains([2, 6]) +## assert_array([1, 2, 3, 4, 5]).not_contains(2, 6) ## [/codeblock] -@warning_ignore("unused_parameter") -func not_contains_same(expected :Variant) -> GdUnitArrayAssert: - return self +@abstract func not_contains_same(...expected: Array) -> GdUnitArrayAssert ## Extracts all values by given function name and optional arguments into a new ArrayAssert. ## If the elements not accessible by `func_name` the value is converted to `"n.a"`, expecting null values -@warning_ignore("unused_parameter") -func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: - return self +@abstract func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert ## Extracts all values by given extractor's into a new ArrayAssert. ## If the elements not extractable than the value is converted to `"n.a"`, expecting null values -@warning_ignore("unused_parameter") -func extractv( - extractor0 :GdUnitValueExtractor, - extractor1 :GdUnitValueExtractor = null, - extractor2 :GdUnitValueExtractor = null, - extractor3 :GdUnitValueExtractor = null, - extractor4 :GdUnitValueExtractor = null, - extractor5 :GdUnitValueExtractor = null, - extractor6 :GdUnitValueExtractor = null, - extractor7 :GdUnitValueExtractor = null, - extractor8 :GdUnitValueExtractor = null, - extractor9 :GdUnitValueExtractor = null) -> GdUnitArrayAssert: - return self - - - -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitArrayAssert: - return self - - -@warning_ignore("unused_parameter") -func append_failure_message(message :String) -> GdUnitArrayAssert: - return self +## -- The argument type is Array[GdUnitValueExtractor] +@abstract func extractv(...extractors: Array) -> GdUnitArrayAssert diff --git a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid index 532347e8..c1db4636 100644 --- a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid @@ -1 +1 @@ -uid://cqcsax3mfwcio +uid://tik13txs8pcf diff --git a/addons/gdUnit4/src/GdUnitAssert.gd b/addons/gdUnit4/src/GdUnitAssert.gd index 6b354750..41382d9f 100644 --- a/addons/gdUnit4/src/GdUnitAssert.gd +++ b/addons/gdUnit4/src/GdUnitAssert.gd @@ -1,49 +1,47 @@ ## Base interface of all GdUnit asserts -class_name GdUnitAssert +@abstract class_name GdUnitAssert extends RefCounted ## Verifies that the current value is null. -@warning_ignore("untyped_declaration") -func is_null(): - return self +@abstract func is_null() -> GdUnitAssert ## Verifies that the current value is not null. -@warning_ignore("untyped_declaration") -func is_not_null(): - return self +@abstract func is_not_null() -> GdUnitAssert ## Verifies that the current value is equal to expected one. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func is_equal(expected: Variant): - return self +@abstract func is_equal(expected: Variant) -> GdUnitAssert ## Verifies that the current value is not equal to expected one. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func is_not_equal(expected: Variant): - return self - - -@warning_ignore("untyped_declaration") -func do_fail(): - return self - - -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func override_failure_message(message :String): - return self - - -## Appends a custom message to the failure message. -## This can be used to add additional infromations to the generated failure message. -@warning_ignore("unused_parameter") -@warning_ignore("untyped_declaration") -func append_failure_message(message :String): - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitAssert + + +## Overrides the default failure message by given custom message.[br] +## This function allows you to replace the automatically generated failure message with a more specific +## or user-friendly message that better describes the test failure context.[br] +## Usage: +## [codeblock] +## # Override with custom context-specific message +## func test_player_inventory(): +## assert_that(player.get_item_count("sword"))\ +## .override_failure_message("Player should have exactly one sword")\ +## .is_equal(1) +## [/codeblock] +@abstract func override_failure_message(message: String) -> GdUnitAssert + + +## Appends a custom message to the failure message.[br] +## This can be used to add additional information to the generated failure message +## while keeping the original assertion details for better debugging context.[br] +## Usage: +## [codeblock] +## # Add context to existing failure message +## func test_player_health(): +## assert_that(player.health)\ +## .append_failure_message("Player was damaged by: %s" % last_damage_source)\ +## .is_greater(0) +## [/codeblock] +@abstract func append_failure_message(message: String) -> GdUnitAssert diff --git a/addons/gdUnit4/src/GdUnitAssert.gd.uid b/addons/gdUnit4/src/GdUnitAssert.gd.uid index 2d716408..e4a6b29e 100644 --- a/addons/gdUnit4/src/GdUnitAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitAssert.gd.uid @@ -1 +1 @@ -uid://b2rci48lrkstq +uid://b8n41qi7whkq6 diff --git a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid index 32ad46eb..83230cab 100644 --- a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid +++ b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid @@ -1 +1 @@ -uid://dmw2wrfn6hq88 +uid://c408wxhovs0jv diff --git a/addons/gdUnit4/src/GdUnitBoolAssert.gd b/addons/gdUnit4/src/GdUnitBoolAssert.gd index de67a941..714f8fc5 100644 --- a/addons/gdUnit4/src/GdUnitBoolAssert.gd +++ b/addons/gdUnit4/src/GdUnitBoolAssert.gd @@ -1,41 +1,35 @@ ## An Assertion Tool to verify boolean values -class_name GdUnitBoolAssert +@abstract class_name GdUnitBoolAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitBoolAssert: - return self +@abstract func is_null() -> GdUnitBoolAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitBoolAssert: - return self +@abstract func is_not_null() -> GdUnitBoolAssert ## Verifies that the current value is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitBoolAssert: - return self +@abstract func is_equal(expected: Variant) -> GdUnitBoolAssert ## Verifies that the current value is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitBoolAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitBoolAssert -## Verifies that the current value is true. -func is_true() -> GdUnitBoolAssert: - return self +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitBoolAssert -## Verifies that the current value is false. -func is_false() -> GdUnitBoolAssert: - return self +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitBoolAssert -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitBoolAssert: - return self +## Verifies that the current value is true. +@abstract func is_true() -> GdUnitBoolAssert + + +## Verifies that the current value is false. +@abstract func is_false() -> GdUnitBoolAssert diff --git a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid index e29a69d4..1a512c62 100644 --- a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid @@ -1 +1 @@ -uid://ba4be0xfbtnmf +uid://dnnkt2oukjkk diff --git a/addons/gdUnit4/src/GdUnitConstants.gd b/addons/gdUnit4/src/GdUnitConstants.gd index 04458947..e43c75ab 100644 --- a/addons/gdUnit4/src/GdUnitConstants.gd +++ b/addons/gdUnit4/src/GdUnitConstants.gd @@ -4,3 +4,7 @@ extends RefCounted const NO_ARG :Variant = "<--null-->" const EXPECT_ASSERT_REPORT_FAILURES := "expect_assert_report_failures" + +## The maximum number of report history files to store +const DEFAULT_REPORT_HISTORY_COUNT = 20 +const REPORT_DIR_PREFIX = "report_" diff --git a/addons/gdUnit4/src/GdUnitConstants.gd.uid b/addons/gdUnit4/src/GdUnitConstants.gd.uid index 456b802b..8cb30580 100644 --- a/addons/gdUnit4/src/GdUnitConstants.gd.uid +++ b/addons/gdUnit4/src/GdUnitConstants.gd.uid @@ -1 +1 @@ -uid://dsfw4040i5wdo +uid://drsaqmhjcbxve diff --git a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd index 49dd3053..45cc62a4 100644 --- a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd +++ b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd @@ -1,105 +1,79 @@ ## An Assertion Tool to verify dictionary -class_name GdUnitDictionaryAssert +@abstract class_name GdUnitDictionaryAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitDictionaryAssert: - return self +@abstract func is_null() -> GdUnitDictionaryAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitDictionaryAssert: - return self +@abstract func is_not_null() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is equal to the given one, ignoring order. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_equal(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is not equal to the given one, ignoring order. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitDictionaryAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is empty, it has a size of 0. -func is_empty() -> GdUnitDictionaryAssert: - return self +@abstract func is_empty() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is not empty, it has a size of minimum 1. -func is_not_empty() -> GdUnitDictionaryAssert: - return self +@abstract func is_not_empty() -> GdUnitDictionaryAssert ## Verifies that the current dictionary is the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary is NOT the same. [br] ## Compares the current by object reference equals -@warning_ignore("unused_parameter") -func is_not_same(expected :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary has a size of given value. -@warning_ignore("unused_parameter") -func has_size(expected: int) -> GdUnitDictionaryAssert: - return self +@abstract func has_size(expected: int) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key(s).[br] ## The keys are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_keys] -@warning_ignore("unused_parameter") -func contains_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func contains_keys(...expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key and value.[br] ## The key and value are compared by deep parameter comparision, for object reference compare you have to use [method contains_same_key_value] -@warning_ignore("unused_parameter") -func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: - return self - - -## Verifies that the current dictionary not contains the given key(s).[br] -## This function is [b]deprecated[/b] you have to use [method not_contains_keys] instead -@warning_ignore("unused_parameter") -func contains_not_keys(expected :Array) -> GdUnitDictionaryAssert: - push_warning("Deprecated: 'contains_not_keys' is deprectated and will be removed soon, use `not_contains_keys` instead!") - return not_contains_keys(expected) +@abstract func contains_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary not contains the given key(s).[br] ## The keys are compared by deep parameter comparision, for object reference compare you have to use [method not_contains_same_keys] -@warning_ignore("unused_parameter") -func not_contains_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key(s).[br] ## The keys are compared by object reference, for deep parameter comparision use [method contains_keys] -@warning_ignore("unused_parameter") -func contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func contains_same_keys(expected: Array) -> GdUnitDictionaryAssert ## Verifies that the current dictionary contains the given key and value.[br] ## The key and value are compared by object reference, for deep parameter comparision use [method contains_key_value] -@warning_ignore("unused_parameter") -func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: - return self +@abstract func contains_same_key_value(key: Variant, value: Variant) -> GdUnitDictionaryAssert ## Verifies that the current dictionary not contains the given key(s). ## The keys are compared by object reference, for deep parameter comparision use [method not_contains_keys] -@warning_ignore("unused_parameter") -func not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: - return self +@abstract func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert diff --git a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid index bfac4d80..d23dd96d 100644 --- a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid @@ -1 +1 @@ -uid://difkcajov8fkg +uid://d3wdqfuw3r2cy diff --git a/addons/gdUnit4/src/GdUnitFailureAssert.gd b/addons/gdUnit4/src/GdUnitFailureAssert.gd index 1ee987ff..6fec1910 100644 --- a/addons/gdUnit4/src/GdUnitFailureAssert.gd +++ b/addons/gdUnit4/src/GdUnitFailureAssert.gd @@ -1,37 +1,52 @@ ## An assertion tool to verify GDUnit asserts. ## This assert is for internal use only, to verify that failed asserts work as expected. -class_name GdUnitFailureAssert +@abstract class_name GdUnitFailureAssert extends GdUnitAssert +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFailureAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFailureAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFailureAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFailureAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFailureAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFailureAssert + + ## Verifies if the executed assert was successful -func is_success() -> GdUnitFailureAssert: - return self +@abstract func is_success() -> GdUnitFailureAssert + ## Verifies if the executed assert has failed -func is_failed() -> GdUnitFailureAssert: - return self +@abstract func is_failed() -> GdUnitFailureAssert ## Verifies the failure line is equal to expected one. -@warning_ignore("unused_parameter") -func has_line(expected :int) -> GdUnitFailureAssert: - return self +@abstract func has_line(expected: int) -> GdUnitFailureAssert ## Verifies the failure message is equal to expected one. -@warning_ignore("unused_parameter") -func has_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func has_message(expected: String) -> GdUnitFailureAssert ## Verifies that the failure message starts with the expected message. -@warning_ignore("unused_parameter") -func starts_with_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func starts_with_message(expected: String) -> GdUnitFailureAssert ## Verifies that the failure message contains the expected message. -@warning_ignore("unused_parameter") -func contains_message(expected: String) -> GdUnitFailureAssert: - return self +@abstract func contains_message(expected: String) -> GdUnitFailureAssert diff --git a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid index 9d50f843..7242cc85 100644 --- a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid @@ -1 +1 @@ -uid://qtqlkea47sy2 +uid://by04f7vxx41p7 diff --git a/addons/gdUnit4/src/GdUnitFileAssert.gd b/addons/gdUnit4/src/GdUnitFileAssert.gd index 21bf21a6..771da906 100644 --- a/addons/gdUnit4/src/GdUnitFileAssert.gd +++ b/addons/gdUnit4/src/GdUnitFileAssert.gd @@ -1,19 +1,38 @@ -class_name GdUnitFileAssert +@abstract class_name GdUnitFileAssert extends GdUnitAssert -func is_file() -> GdUnitFileAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFileAssert -func exists() -> GdUnitFileAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFileAssert -func is_script() -> GdUnitFileAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFileAssert -@warning_ignore("unused_parameter") -func contains_exactly(expected_rows :Array) -> GdUnitFileAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFileAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFileAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFileAssert + + +@abstract func is_file() -> GdUnitFileAssert + + +@abstract func exists() -> GdUnitFileAssert + + +@abstract func is_script() -> GdUnitFileAssert + + +@abstract func contains_exactly(expected_rows :Array) -> GdUnitFileAssert diff --git a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid index 7ee6da68..b5bdfad6 100644 --- a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid @@ -1 +1 @@ -uid://cpangqu3iagtj +uid://tchxm8b2xdb3 diff --git a/addons/gdUnit4/src/GdUnitFloatAssert.gd b/addons/gdUnit4/src/GdUnitFloatAssert.gd index 6ce5f1e8..2695ab0e 100644 --- a/addons/gdUnit4/src/GdUnitFloatAssert.gd +++ b/addons/gdUnit4/src/GdUnitFloatAssert.gd @@ -1,83 +1,75 @@ ## An Assertion Tool to verify float values -class_name GdUnitFloatAssert +@abstract class_name GdUnitFloatAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitFloatAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitFloatAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitFloatAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitFloatAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitFloatAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFloatAssert ## Verifies that the current and expected value are approximately equal. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_equal_approx(expected :float, approx :float) -> GdUnitFloatAssert: - return self +@abstract func is_equal_approx(expected: float, approx: float) -> GdUnitFloatAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFloatAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFloatAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_less(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_less_equal(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_greater(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :float) -> GdUnitFloatAssert: - return self +@abstract func is_greater_equal(expected: float) -> GdUnitFloatAssert ## Verifies that the current value is negative. -func is_negative() -> GdUnitFloatAssert: - return self +@abstract func is_negative() -> GdUnitFloatAssert ## Verifies that the current value is not negative. -func is_not_negative() -> GdUnitFloatAssert: - return self +@abstract func is_not_negative() -> GdUnitFloatAssert ## Verifies that the current value is equal to zero. -func is_zero() -> GdUnitFloatAssert: - return self +@abstract func is_zero() -> GdUnitFloatAssert ## Verifies that the current value is not equal to zero. -func is_not_zero() -> GdUnitFloatAssert: - return self +@abstract func is_not_zero() -> GdUnitFloatAssert ## Verifies that the current value is in the given set of values. -@warning_ignore("unused_parameter") -func is_in(expected :Array) -> GdUnitFloatAssert: - return self +@abstract func is_in(expected: Array) -> GdUnitFloatAssert ## Verifies that the current value is not in the given set of values. -@warning_ignore("unused_parameter") -func is_not_in(expected :Array) -> GdUnitFloatAssert: - return self +@abstract func is_not_in(expected: Array) -> GdUnitFloatAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :float, to :float) -> GdUnitFloatAssert: - return self +@abstract func is_between(from: float, to: float) -> GdUnitFloatAssert diff --git a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid index 9c3983d7..d9e61c42 100644 --- a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid @@ -1 +1 @@ -uid://cii6rn31jx3al +uid://bex2co1aec0f7 diff --git a/addons/gdUnit4/src/GdUnitFuncAssert.gd b/addons/gdUnit4/src/GdUnitFuncAssert.gd index 75e8ebd6..e8a49c51 100644 --- a/addons/gdUnit4/src/GdUnitFuncAssert.gd +++ b/addons/gdUnit4/src/GdUnitFuncAssert.gd @@ -1,56 +1,42 @@ ## An Assertion Tool to verify function callback values -class_name GdUnitFuncAssert +@abstract class_name GdUnitFuncAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_null() -> GdUnitFuncAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_not_null() -> GdUnitFuncAssert ## Verifies that the current value is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_equal(expected: Variant) -> GdUnitFuncAssert -## Verifies that the current value is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitFuncAssert -## Verifies that the current value is true. -func is_true() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitFuncAssert -## Verifies that the current value is false. -func is_false() -> GdUnitFuncAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitFuncAssert -## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitFuncAssert: - return self +## Verifies that the current value is true. +@abstract func is_true() -> GdUnitFuncAssert + + +## Verifies that the current value is false. +@abstract func is_false() -> GdUnitFuncAssert ## Sets the timeout in ms to wait the function returnd the expected value, if the time over a failure is emitted.[br] ## e.g.[br] ## do wait until 5s the function `is_state` is returns 10 [br] ## [code]assert_func(instance, "is_state").wait_until(5000).is_equal(10)[/code] -@warning_ignore("unused_parameter") -func wait_until(timeout :int) -> GdUnitFuncAssert: - return self +@abstract func wait_until(timeout: int) -> GdUnitFuncAssert diff --git a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid index 87e1e06b..30d40cfd 100644 --- a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid @@ -1 +1 @@ -uid://c5xibgs8ux5xy +uid://jpib5o8hs04e diff --git a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd index c5e9f863..01711f9a 100644 --- a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd +++ b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd @@ -1,16 +1,38 @@ ## An assertion tool to verify for Godot runtime errors like assert() and push notifications like push_error(). -class_name GdUnitGodotErrorAssert +@abstract class_name GdUnitGodotErrorAssert extends GdUnitAssert +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitGodotErrorAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitGodotErrorAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitGodotErrorAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitGodotErrorAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitGodotErrorAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitGodotErrorAssert + + ## Verifies if the executed code runs without any runtime errors ## Usage: ## [codeblock] ## await assert_error().is_success() ## [/codeblock] -func is_success() -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_success() -> GdUnitGodotErrorAssert ## Verifies if the executed code runs into a runtime error @@ -18,10 +40,7 @@ func is_success() -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_runtime_error() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert ## Verifies if the executed code has a push_warning() used @@ -29,10 +48,7 @@ func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_push_warning() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert ## Verifies if the executed code has a push_error() used @@ -40,7 +56,4 @@ func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: ## [codeblock] ## await assert_error().is_push_error() ## [/codeblock] -@warning_ignore("unused_parameter") -func is_push_error(expected_error :String) -> GdUnitGodotErrorAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert diff --git a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid index 7a59443c..43208d9d 100644 --- a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid @@ -1 +1 @@ -uid://co2g8c7py1k3e +uid://dck4101xbytqq diff --git a/addons/gdUnit4/src/GdUnitIntAssert.gd b/addons/gdUnit4/src/GdUnitIntAssert.gd index d593edf9..05eb9223 100644 --- a/addons/gdUnit4/src/GdUnitIntAssert.gd +++ b/addons/gdUnit4/src/GdUnitIntAssert.gd @@ -1,86 +1,79 @@ ## An Assertion Tool to verify integer values -class_name GdUnitIntAssert +@abstract class_name GdUnitIntAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitIntAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitIntAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitIntAssert: - return self + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitIntAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitIntAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitIntAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitIntAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitIntAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_less(expected: int) -> GdUnitIntAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_less_equal(expected: int) -> GdUnitIntAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_greater(expected: int) -> GdUnitIntAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :int) -> GdUnitIntAssert: - return self +@abstract func is_greater_equal(expected: int) -> GdUnitIntAssert ## Verifies that the current value is even. -func is_even() -> GdUnitIntAssert: - return self +@abstract func is_even() -> GdUnitIntAssert ## Verifies that the current value is odd. -func is_odd() -> GdUnitIntAssert: - return self +@abstract func is_odd() -> GdUnitIntAssert ## Verifies that the current value is negative. -func is_negative() -> GdUnitIntAssert: - return self +@abstract func is_negative() -> GdUnitIntAssert ## Verifies that the current value is not negative. -func is_not_negative() -> GdUnitIntAssert: - return self +@abstract func is_not_negative() -> GdUnitIntAssert ## Verifies that the current value is equal to zero. -func is_zero() -> GdUnitIntAssert: - return self +@abstract func is_zero() -> GdUnitIntAssert ## Verifies that the current value is not equal to zero. -func is_not_zero() -> GdUnitIntAssert: - return self +@abstract func is_not_zero() -> GdUnitIntAssert ## Verifies that the current value is in the given set of values. -@warning_ignore("unused_parameter") -func is_in(expected :Array) -> GdUnitIntAssert: - return self +@abstract func is_in(expected: Array) -> GdUnitIntAssert ## Verifies that the current value is not in the given set of values. -@warning_ignore("unused_parameter") -func is_not_in(expected :Array) -> GdUnitIntAssert: - return self +@abstract func is_not_in(expected: Array) -> GdUnitIntAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :int, to :int) -> GdUnitIntAssert: - return self +@abstract func is_between(from: int, to: int) -> GdUnitIntAssert diff --git a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid index 17e2b81e..d2b480a8 100644 --- a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid @@ -1 +1 @@ -uid://bpfhi1eheni4h +uid://bvyc31wdus2mb diff --git a/addons/gdUnit4/src/GdUnitObjectAssert.gd b/addons/gdUnit4/src/GdUnitObjectAssert.gd index 9ce234ce..9d7e76ea 100644 --- a/addons/gdUnit4/src/GdUnitObjectAssert.gd +++ b/addons/gdUnit4/src/GdUnitObjectAssert.gd @@ -1,61 +1,51 @@ ## An Assertion Tool to verify Object values -class_name GdUnitObjectAssert +@abstract class_name GdUnitObjectAssert extends GdUnitAssert -## Verifies that the current object is equal to expected one. -@warning_ignore("unused_parameter") -func is_equal(expected: Variant) -> GdUnitObjectAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitObjectAssert -## Verifies that the current object is not equal to expected one. -@warning_ignore("unused_parameter") -func is_not_equal(expected: Variant) -> GdUnitObjectAssert: - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitObjectAssert -## Verifies that the current object is null. -func is_null() -> GdUnitObjectAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitObjectAssert -## Verifies that the current object is not null. -func is_not_null() -> GdUnitObjectAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitObjectAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitObjectAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitObjectAssert ## Verifies that the current object is the same as the given one. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_same(expected: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_same(expected: Variant) -> GdUnitObjectAssert ## Verifies that the current object is not the same as the given one. -@warning_ignore("unused_parameter") -func is_not_same(expected: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_same(expected: Variant) -> GdUnitObjectAssert ## Verifies that the current object is an instance of the given type. -@warning_ignore("unused_parameter") -func is_instanceof(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_instanceof(type: Variant) -> GdUnitObjectAssert ## Verifies that the current object is not an instance of the given type. -@warning_ignore("unused_parameter") -func is_not_instanceof(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_instanceof(type: Variant) -> GdUnitObjectAssert ## Checks whether the current object inherits from the specified type. -@warning_ignore("unused_parameter") -func is_inheriting(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_inheriting(type: Variant) -> GdUnitObjectAssert ## Checks whether the current object does NOT inherit from the specified type. -@warning_ignore("unused_parameter") -func is_not_inheriting(type: Variant) -> GdUnitObjectAssert: - return self +@abstract func is_not_inheriting(type: Variant) -> GdUnitObjectAssert diff --git a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid index e86bd11b..154ee6c0 100644 --- a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid @@ -1 +1 @@ -uid://d2d3bkxt1wcx7 +uid://dcwsww4scdaxk diff --git a/addons/gdUnit4/src/GdUnitResultAssert.gd b/addons/gdUnit4/src/GdUnitResultAssert.gd index 347e6374..01eb8800 100644 --- a/addons/gdUnit4/src/GdUnitResultAssert.gd +++ b/addons/gdUnit4/src/GdUnitResultAssert.gd @@ -1,45 +1,51 @@ ## An Assertion Tool to verify Results -class_name GdUnitResultAssert +@abstract class_name GdUnitResultAssert extends GdUnitAssert ## Verifies that the current value is null. -func is_null() -> GdUnitResultAssert: - return self +@abstract func is_null() -> GdUnitResultAssert ## Verifies that the current value is not null. -func is_not_null() -> GdUnitResultAssert: - return self +@abstract func is_not_null() -> GdUnitResultAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitResultAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitResultAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitResultAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitResultAssert ## Verifies that the result is ends up with empty -func is_empty() -> GdUnitResultAssert: - return self +@abstract func is_empty() -> GdUnitResultAssert ## Verifies that the result is ends up with success -func is_success() -> GdUnitResultAssert: - return self +@abstract func is_success() -> GdUnitResultAssert ## Verifies that the result is ends up with warning -func is_warning() -> GdUnitResultAssert: - return self +@abstract func is_warning() -> GdUnitResultAssert ## Verifies that the result is ends up with error -func is_error() -> GdUnitResultAssert: - return self +@abstract func is_error() -> GdUnitResultAssert ## Verifies that the result contains the given message -@warning_ignore("unused_parameter") -func contains_message(expected :String) -> GdUnitResultAssert: - return self +@abstract func contains_message(expected: String) -> GdUnitResultAssert ## Verifies that the result contains the given value -@warning_ignore("unused_parameter") -func is_value(expected :Variant) -> GdUnitResultAssert: - return self +@abstract func is_value(expected: Variant) -> GdUnitResultAssert diff --git a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid index eb8143b2..bbf3ee56 100644 --- a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid @@ -1 +1 @@ -uid://4cgv3ss1mryp +uid://cvrj44tpselb3 diff --git a/addons/gdUnit4/src/GdUnitSceneRunner.gd b/addons/gdUnit4/src/GdUnitSceneRunner.gd index 184be50a..9b156f3a 100644 --- a/addons/gdUnit4/src/GdUnitSceneRunner.gd +++ b/addons/gdUnit4/src/GdUnitSceneRunner.gd @@ -1,95 +1,95 @@ ## The Scene Runner is a tool used for simulating interactions on a scene. ## With this tool, you can simulate input events such as keyboard or mouse input and/or simulate scene processing over a certain number of frames. ## This tool is typically used for integration testing a scene. -class_name GdUnitSceneRunner +@abstract class_name GdUnitSceneRunner extends RefCounted -const NO_ARG = GdUnitConstants.NO_ARG - ## Simulates that an action has been pressed.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_pressed(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_pressed(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that an action is pressed.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_press(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_press(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that an action has been released.[br] ## [member action] : the action e.g. [code]"ui_up"[/code][br] -@warning_ignore("unused_parameter") -func simulate_action_release(action: String) -> GdUnitSceneRunner: - return self +## [member event_index] : [url=https://docs.godotengine.org/en/4.4/classes/class_inputeventaction.html#class-inputeventaction-property-event-index]default=-1[/url][br] +@abstract func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRunner ## Simulates that a key has been pressed.[br] +## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0 ## [member key_code] : the key code e.g. [constant KEY_ENTER][br] -## [member shift_pressed] : false by default set to true if simmulate shift is press[br] -## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] ## [codeblock] ## func test_key_presssed(): ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_key_pressed(KEY_SPACE) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner -## Simulates that a key is pressed.[br] +## Simulates that a key is pressing.[br] +## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0[br]See `test_key_shift_and_A_presssing` for example using key combinations ## [member key_code] : the key code e.g. [constant KEY_ENTER][br] -## [member shift_pressed] : false by default set to true if simmulate shift is press[br] -## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] -@warning_ignore("unused_parameter") -func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - return self +## [codeblock] +## # Do simulate key pressing A +## func test_key_A_presssing(): +## var runner = scene_runner("res://scenes/simple_scene.tscn") +## await runner.simulate_key_press(KEY_A) +## +## +## # Do simulate keycombination pressing shift+A +## func test_key_shift_and_A_presssing(): +## var runner = scene_runner("res://scenes/simple_scene.tscn") +## runner.simulate_key_press(KEY_SHIFT) +## runner.simulate_key_press(KEY_A) +## await _runner.await_input_processed() +## [/codeblock] +@abstract func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner ## Simulates that a key has been released.[br] ## [member key_code] : the key code e.g. [constant KEY_ENTER][br] -## [member shift_pressed] : false by default set to true if simmulate shift is press[br] -## [member ctrl_pressed] : false by default set to true if simmulate control is press[br] -@warning_ignore("unused_parameter") -func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: - return self - - -## Sets the mouse cursor to given position relative to the viewport. -## @deprecated: Use [set_mouse_position] instead. -@warning_ignore("unused_parameter") -func set_mouse_pos(position: Vector2) -> GdUnitSceneRunner: - return self +## [codeblock] +## # Do simulate releasing key A +## func test_key_A_releasing(): +## var runner = scene_runner("res://scenes/simple_scene.tscn") +## await runner.simulate_key_release(KEY_A) +## +## +## # Do simulate keycombination pressing shift+A +## func test_key_shift_and_A_releasing((): +## var runner = scene_runner("res://scenes/simple_scene.tscn") +## runner.simulate_key_release(KEY_SHIFT) +## runner.simulate_key_release(KEY_A) +## await _runner.await_input_processed() +## [/codeblock] +## @deprecated: the modifier [b]shift_pressed[/b] and [b]ctrl_pressed[/b] will be removed in v7.0[br]See `test_key_shift_and_A_releasing` for example using key combinations +@abstract func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner ## Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window.[br] ## [member position] : The absolute position in pixels as Vector2 -@warning_ignore("unused_parameter") -func set_mouse_position(position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func set_mouse_position(position: Vector2) -> GdUnitSceneRunner ## Returns the mouse's position in this Viewport using the coordinate system of this Viewport. -func get_mouse_position() -> Vector2: - return Vector2.ZERO +@abstract func get_mouse_position() -> Vector2 ## Gets the current global mouse position of the current window -func get_global_mouse_position() -> Vector2: - return Vector2.ZERO +@abstract func get_global_mouse_position() -> Vector2 ## Simulates a mouse moved to final position.[br] ## [member position] : The final mouse position -@warning_ignore("unused_parameter") -func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner ## Simulates a mouse move to the relative coordinates (offset).[br] @@ -103,10 +103,7 @@ func simulate_mouse_move(position: Vector2) -> GdUnitSceneRunner: ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_mouse_move_relative(Vector2(100,100)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a mouse move to the absolute coordinates.[br] @@ -120,59 +117,44 @@ func simulate_mouse_move_relative(relative: Vector2, time: float = 1.0, trans_ty ## var runner = scene_runner("res://scenes/simple_scene.tscn") ## await runner.simulate_mouse_move_absolute(Vector2(100,100)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_mouse_move_absolute(position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a mouse button pressed.[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. ## [member double_click] : Set to true to simulate a double-click -@warning_ignore("unused_parameter") -func simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_pressed(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner ## Simulates a mouse button press (holding)[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. ## [member double_click] : Set to true to simulate a double-click -@warning_ignore("unused_parameter") -func simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_press(button_index: MouseButton, double_click := false) -> GdUnitSceneRunner ## Simulates a mouse button released.[br] ## [member button_index] : The mouse button identifier, one of the [enum MouseButton] or button wheel constants. -@warning_ignore("unused_parameter") -func simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner: - return self +@abstract func simulate_mouse_button_release(button_index: MouseButton) -> GdUnitSceneRunner ## Simulates a screen touch is pressed.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The position to touch the screen.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner ## Simulates a screen touch press without releasing it immediately, effectively simulating a "hold" action.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The position to touch the screen.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner ## Simulates a screen touch is released.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member double_tap] : If true, the touch's state is a double tab. -@warning_ignore("unused_parameter") -func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSceneRunner ## Simulates a touch drag and drop event to a relative position.[br] @@ -190,10 +172,7 @@ func simulate_screen_touch_release(index: int, double_tap := false) -> GdUnitSce ## # and drop it at final at 150,50 relative (50,50 + 100,0) ## await runner.simulate_screen_touch_drag_relative(1, Vector2(100,0)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a touch screen drop to the absolute coordinates (offset).[br] @@ -211,10 +190,7 @@ func simulate_screen_touch_drag_relative(index: int, relative: Vector2, time: fl ## # and drop it at 100,50 ## await runner.simulate_screen_touch_drag_absolute(1, Vector2(100,50)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a complete drag and drop event from one position to another.[br] @@ -232,91 +208,47 @@ func simulate_screen_touch_drag_absolute(index: int, position: Vector2, time: fl ## # start drag at position 50,50 and drop it at 100,50 ## await runner.simulate_screen_touch_drag_drop(1, Vector2(50, 50), Vector2(100,50)) ## [/codeblock] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_screen_touch_drag_drop(index: int, position: Vector2, drop_position: Vector2, time: float = 1.0, trans_type: Tween.TransitionType = Tween.TRANS_LINEAR) -> GdUnitSceneRunner ## Simulates a touch screen drag event to given position.[br] ## [member index] : The touch index in the case of a multi-touch event.[br] ## [member position] : The drag start position, indicating the drag position.[br] -@warning_ignore("unused_parameter") -func simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner: - return self +@abstract func simulate_screen_touch_drag(index: int, position: Vector2) -> GdUnitSceneRunner ## Returns the actual position of the touchscreen drag position by given index. ## [member index] : The touch index in the case of a multi-touch event.[br] -@warning_ignore("unused_parameter") -func get_screen_touch_drag_position(index: int) -> Vector2: - return Vector2.ZERO +@abstract func get_screen_touch_drag_position(index: int) -> Vector2 ## Sets how fast or slow the scene simulation is processed (clock ticks versus the real).[br] ## It defaults to 1.0. A value of 2.0 means the game moves twice as fast as real life, ## whilst a value of 0.5 means the game moves at half the regular speed. - - -## Sets the time factor for the scene simulation. ## [member time_factor] : A float representing the simulation speed.[br] ## - Default is 1.0, meaning the simulation runs at normal speed.[br] ## - A value of 2.0 means the simulation runs twice as fast as real time.[br] ## - A value of 0.5 means the simulation runs at half the regular speed.[br] -@warning_ignore("unused_parameter") -func set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner: - return self +@abstract func set_time_factor(time_factor: float = 1.0) -> GdUnitSceneRunner ## Simulates scene processing for a certain number of frames.[br] ## [member frames] : amount of frames to process[br] ## [member delta_milli] : the time delta between a frame in milliseconds -@warning_ignore("unused_parameter") -func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner ## Simulates scene processing until the given signal is emitted by the scene.[br] ## [member signal_name] : the signal to stop the simulation[br] ## [member args] : optional signal arguments to be matched for stop[br] -@warning_ignore("unused_parameter") -func simulate_until_signal( - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_until_signal(signal_name: String, ...args: Array) -> GdUnitSceneRunner ## Simulates scene processing until the given signal is emitted by the given object.[br] ## [member source] : the object that should emit the signal[br] ## [member signal_name] : the signal to stop the simulation[br] ## [member args] : optional signal arguments to be matched for stop -@warning_ignore("unused_parameter") -func simulate_until_object_signal( - source: Object, - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +@abstract func simulate_until_object_signal(source: Object, signal_name: String, ...args: Array) -> GdUnitSceneRunner ## Waits for all input events to be processed by flushing any buffered input events @@ -331,11 +263,7 @@ func simulate_until_object_signal( ## [codeblock] ## await await_input_processed() # Ensure all inputs are processed before continuing ## [/codeblock] -func await_input_processed() -> void: - if scene() != null and scene().process_mode != Node.PROCESS_MODE_DISABLED: - Input.flush_buffered_events() - await (Engine.get_main_loop() as SceneTree).process_frame - await (Engine.get_main_loop() as SceneTree).physics_frame +@abstract func await_input_processed() -> void ## The await_func function pauses execution until a specified function in the scene returns a value.[br] @@ -348,10 +276,7 @@ func await_input_processed() -> void: ## # Waits for 'calculate_score' function and verifies the result is equal to 100. ## await_func("calculate_score").is_equal(100) ## [/codeblock] -@warning_ignore("unused_parameter") -func await_func(func_name: String, args := []) -> GdUnitFuncAssert: - return null - +@abstract func await_func(func_name: String, ...args: Array) -> GdUnitFuncAssert ## The await_func_on function extends the functionality of await_func by allowing you to specify a source node within the scene.[br] @@ -366,19 +291,14 @@ func await_func(func_name: String, args := []) -> GdUnitFuncAssert: ## var my_instance := ScoreCalculator.new() ## await_func(my_instance, "calculate_score").is_equal(100) ## [/codeblock] -@warning_ignore("unused_parameter") -func await_func_on(source: Object, func_name: String, args := []) -> GdUnitFuncAssert: - return null +@abstract func await_func_on(source: Object, func_name: String, ...args: Array) -> GdUnitFuncAssert ## Waits for the specified signal to be emitted by the scene. If the signal is not emitted within the given timeout, the operation fails.[br] ## [member signal_name] : The name of the signal to wait for[br] ## [member args] : The signal arguments as an array[br] ## [member timeout] : The maximum duration (in milliseconds) to wait for the signal to be emitted before failing -@warning_ignore("unused_parameter") -func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void: - await (Engine.get_main_loop() as SceneTree).process_frame - pass +@abstract func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void ## Waits for the specified signal to be emitted by a particular source node. If the signal is not emitted within the given timeout, the operation fails.[br] @@ -386,69 +306,45 @@ func await_signal(signal_name: String, args := [], timeout := 2000 ) -> void: ## [member signal_name] : The name of the signal to wait for[br] ## [member args] : The signal arguments as an array[br] ## [member timeout] : tThe maximum duration (in milliseconds) to wait for the signal to be emitted before failing -@warning_ignore("unused_parameter") -func await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void: - pass +@abstract func await_signal_on(source: Object, signal_name: String, args := [], timeout := 2000 ) -> void ## Restores the scene window to a windowed mode and brings it to the foreground.[br] ## This ensures that the scene is visible and active during testing, making it easier to observe and interact with. -func move_window_to_foreground() -> GdUnitSceneRunner: - return self +@abstract func move_window_to_foreground() -> GdUnitSceneRunner -## Restores the scene window to a windowed mode and brings it to the foreground.[br] -## This ensures that the scene is visible and active during testing, making it easier to observe and interact with. -## @deprecated: Use [move_window_to_foreground] instead. -func maximize_view() -> GdUnitSceneRunner: - return self +## Minimizes the scene window to a windowed mode and brings it to the background.[br] +## This ensures that the scene is hidden during testing. +@abstract func move_window_to_background() -> GdUnitSceneRunner ## Return the current value of the property with the name .[br] ## [member name] : name of property[br] ## [member return] : the value of the property -@warning_ignore("unused_parameter") -func get_property(name: String) -> Variant: - return null +@abstract func get_property(name: String) -> Variant + ## Set the value of the property with the name .[br] ## [member name] : name of property[br] ## [member value] : value of property[br] ## [member return] : true|false depending on valid property name. -@warning_ignore("unused_parameter") -func set_property(name: String, value: Variant) -> bool: - return false +@abstract func set_property(name: String, value: Variant) -> bool ## executes the function specified by in the scene and returns the result.[br] ## [member name] : the name of the function to execute[br] ## [member args] : optional function arguments[br] ## [member return] : the function result -@warning_ignore("unused_parameter") -func invoke( - name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> Variant: - return null +@abstract func invoke(name: String, ...args: Array) -> Variant ## Searches for the specified node with the name in the current scene and returns it, otherwise null.[br] ## [member name] : the name of the node to find[br] ## [member recursive] : enables/disables seraching recursive[br] ## [member return] : the node if find otherwise null -@warning_ignore("unused_parameter") -func find_child(name: String, recursive: bool = true, owned: bool = false) -> Node: - return null +@abstract func find_child(name: String, recursive: bool = true, owned: bool = false) -> Node ## Access to current running scene -func scene() -> Node: - return null +@abstract func scene() -> Node diff --git a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid index 4457aaa1..5a5c792e 100644 --- a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid +++ b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid @@ -1 +1 @@ -uid://dhdbg6m7ygvou +uid://c4pfjoaemxnpq diff --git a/addons/gdUnit4/src/GdUnitSignalAssert.gd b/addons/gdUnit4/src/GdUnitSignalAssert.gd index 9dbc76d3..2c3d39bb 100644 --- a/addons/gdUnit4/src/GdUnitSignalAssert.gd +++ b/addons/gdUnit4/src/GdUnitSignalAssert.gd @@ -1,38 +1,158 @@ ## An Assertion Tool to verify for emitted signals until a waiting time -class_name GdUnitSignalAssert +@abstract class_name GdUnitSignalAssert extends GdUnitAssert -## Verifies that given signal is emitted until waiting time -@warning_ignore("unused_parameter") -func is_emitted(name :String, args := []) -> GdUnitSignalAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitSignalAssert -## Verifies that given signal is NOT emitted until waiting time -@warning_ignore("unused_parameter") -func is_not_emitted(name :String, args := []) -> GdUnitSignalAssert: - await (Engine.get_main_loop() as SceneTree).process_frame - return self +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitSignalAssert -## Verifies the signal exists checked the emitter -@warning_ignore("unused_parameter") -func is_signal_exists(name :String) -> GdUnitSignalAssert: - return self +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitSignalAssert + + +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitSignalAssert ## Overrides the default failure message by given custom message. -@warning_ignore("unused_parameter") -func override_failure_message(message :String) -> GdUnitSignalAssert: - return self +@abstract func override_failure_message(message: String) -> GdUnitSignalAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitSignalAssert + + +## Verifies that the specified signal is emitted with the expected arguments.[br] +## +## This assertion waits for a signal to be emitted from the object under test and +## validates that it was emitted with the correct arguments. The function supports +## both typed signals (Signal type) and string-based signal names for flexibility +## in different testing scenarios.[br] +## [br] +## [b]Parameters:[/b][br] +## [param signal_name]: The signal to monitor. Can be either:[br] +## • A [Signal] reference (recommended for type safety)[br] +## • A [String] with the signal name +## [param signal_args]: Optional expected signal arguments.[br] +## When provided, verifies the signal was emitted with exactly these values.[br] +## [br] +## [b]Returns:[/b][br] +## [GdUnitSignalAssert] - Returns self for method chaining.[br] +## [br] +## [b]Examples:[/b] +## [codeblock] +## signal signal_a(value: int) +## signal signal_b(name: String, count: int) +## +## # Wait for signal emission without checking arguments +## # Using Signal reference (type-safe) +## await assert_signal(instance).is_emitted(signal_a) +## # Using string name (dynamic) +## await assert_signal(instance).is_emitted("signal_a") +## +## # Wait for signal emission with specific argument +## await assert_signal(instance).is_emitted(signal_a, 10) +## +## # Wait for signal with multiple arguments +## await assert_signal(instance).is_emitted(signal_b, "test", 42) +## +## # Wait max 500ms for signal with argument 10 +## await assert_signal(instance).wait_until(500).is_emitted(signal_a, 10) +## [/codeblock] +## [br] +## [b]Note:[/b] This is an async operation - use [code]await[/code] when calling.[br] +## The assertion fails if the signal is not emitted within the timeout period. +@abstract func is_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert + + +## Verifies that the specified signal is NOT emitted with the expected arguments.[br] +## +## This assertion waits for a specified time period and validates that a signal +## was not emitted with the given arguments. Useful for ensuring certain conditions +## don't trigger unwanted signals or for verifying signal filtering logic.[br] +## [br] +## [b]Parameters:[/b][br] +## [param signal_name]: The signal to monitor. Can be either:[br] +## • A [Signal] reference (recommended for type safety)[br] +## • A [String] with the signal name +## [param signal_args]: Optional expected signal arguments.[br] +## When provided, verifies the signal was not emitted with these specific values.[br] +## If omitted, verifies the signal was not emitted at all.[br] +## [br] +## [b]Returns:[/b][br] +## [GdUnitSignalAssert] - Returns self for method chaining.[br] +## [br] +## [b]Examples:[/b] +## [codeblock] +## signal signal_a(value: int) +## signal signal_b(name: String, count: int) +## +## # Verify signal is not emitted at all (without checking arguments) +## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a) +## await assert_signal(instance).wait_until(500).is_not_emitted("signal_a") +## +## # Verify signal is not emitted with specific argument +## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a, 10) +## +## # Verify signal is not emitted with multiple arguments +## await assert_signal(instance).wait_until(500).is_not_emitted(signal_b, "test", 42) +## +## # Can be emitted with different arguments (this passes) +## instance.emit_signal("signal_a", 20) # Emits with 20, not 10 +## await assert_signal(instance).wait_until(500).is_not_emitted(signal_a, 10) +## [/codeblock] +## [br] +## [b]Note:[/b] This is an async operation - use [code]await[/code] when calling.[br] +## The assertion fails if the signal IS emitted with the specified arguments within the timeout period. +@abstract func is_not_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert + + +## Verifies that the specified signal exists on the emitter object.[br] +## +## This assertion checks if a signal is defined on the object under test, +## regardless of whether it has been emitted. Useful for validating that +## objects have the expected signals before testing their emission.[br] +## [br] +## [b]Parameters:[/b][br] +## [param signal_name]: The signal to check. Can be either:[br] +## • A [Signal] reference (recommended for type safety)[br] +## • A [String] with the signal name +## [br] +## [b]Returns:[/b][br] +## [GdUnitSignalAssert] - Returns self for method chaining.[br] +## [br] +## [b]Examples:[/b] +## [codeblock] +## signal my_signal(value: int) +## signal another_signal() +## +## # Verify signal exists using Signal reference +## assert_signal(instance).is_signal_exists(my_signal) +## +## # Verify signal exists using string name +## assert_signal(instance).is_signal_exists("my_signal") +## +## # Chain with other assertions +## assert_signal(instance) \ +## .is_signal_exists(my_signal) \ +## .is_emitted(my_signal, 42) +## +## [/codeblock] +## [br] +## [b]Note:[/b] This only checks signal definition, not emission.[br] +## The assertion fails if the signal is not defined on the object. +@abstract func is_signal_exists(signal_name: Variant) -> GdUnitSignalAssert ## Sets the assert signal timeout in ms, if the time over a failure is reported.[br] -## e.g.[br] +## Example: +## [codeblock] ## do wait until 5s the instance has emitted the signal `signal_a`[br] -## [code]assert_signal(instance).wait_until(5000).is_emitted("signal_a")[/code] -@warning_ignore("unused_parameter") -func wait_until(timeout :int) -> GdUnitSignalAssert: - return self +## assert_signal(instance).wait_until(5000).is_emitted("signal_a") +## [/codeblock] +@abstract func wait_until(timeout: int) -> GdUnitSignalAssert diff --git a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid index f09743f6..d3610e8d 100644 --- a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid @@ -1 +1 @@ -uid://nqq3ony261ch +uid://did2a5pn3c58e diff --git a/addons/gdUnit4/src/GdUnitStringAssert.gd b/addons/gdUnit4/src/GdUnitStringAssert.gd index 5b4a6a1e..2de698bd 100644 --- a/addons/gdUnit4/src/GdUnitStringAssert.gd +++ b/addons/gdUnit4/src/GdUnitStringAssert.gd @@ -1,79 +1,71 @@ ## An Assertion Tool to verify String values -class_name GdUnitStringAssert +@abstract class_name GdUnitStringAssert extends GdUnitAssert -## Verifies that the current String is equal to the given one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitStringAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitStringAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitStringAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitStringAssert ## Verifies that the current String is equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: - return self +@abstract func is_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert -## Verifies that the current String is not equal to the given one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitStringAssert: - return self +## Verifies that the current value is not equal to expected one. +@abstract func is_not_equal(expected: Variant) -> GdUnitStringAssert ## Verifies that the current String is not equal to the given one, ignoring case considerations. -@warning_ignore("unused_parameter") -func is_not_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: - return self +@abstract func is_not_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitStringAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitStringAssert ## Verifies that the current String is empty, it has a length of 0. -func is_empty() -> GdUnitStringAssert: - return self +@abstract func is_empty() -> GdUnitStringAssert ## Verifies that the current String is not empty, it has a length of minimum 1. -func is_not_empty() -> GdUnitStringAssert: - return self +@abstract func is_not_empty() -> GdUnitStringAssert ## Verifies that the current String contains the given String. -@warning_ignore("unused_parameter") -func contains(expected: String) -> GdUnitStringAssert: - return self +@abstract func contains(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String. -@warning_ignore("unused_parameter") -func not_contains(expected: String) -> GdUnitStringAssert: - return self +@abstract func not_contains(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String, ignoring case considerations. -@warning_ignore("unused_parameter") -func contains_ignoring_case(expected: String) -> GdUnitStringAssert: - return self +@abstract func contains_ignoring_case(expected: String) -> GdUnitStringAssert ## Verifies that the current String does not contain the given String, ignoring case considerations. -@warning_ignore("unused_parameter") -func not_contains_ignoring_case(expected: String) -> GdUnitStringAssert: - return self +@abstract func not_contains_ignoring_case(expected: String) -> GdUnitStringAssert ## Verifies that the current String starts with the given prefix. -@warning_ignore("unused_parameter") -func starts_with(expected: String) -> GdUnitStringAssert: - return self +@abstract func starts_with(expected: String) -> GdUnitStringAssert ## Verifies that the current String ends with the given suffix. -@warning_ignore("unused_parameter") -func ends_with(expected: String) -> GdUnitStringAssert: - return self +@abstract func ends_with(expected: String) -> GdUnitStringAssert ## Verifies that the current String has the expected length by used comparator. -@warning_ignore("unused_parameter") -func has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert: - return self +@abstract func has_length(length: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert diff --git a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid index b90e3a81..e571c171 100644 --- a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid @@ -1 +1 @@ -uid://dcbgythmvh6g4 +uid://1504vhwhqvpa diff --git a/addons/gdUnit4/src/GdUnitTestSuite.gd b/addons/gdUnit4/src/GdUnitTestSuite.gd index b6790efa..5cf269f5 100644 --- a/addons/gdUnit4/src/GdUnitTestSuite.gd +++ b/addons/gdUnit4/src/GdUnitTestSuite.gd @@ -14,14 +14,13 @@ class_name GdUnitTestSuite extends Node -const NO_ARG :Variant = GdUnitConstants.NO_ARG ### internal runtime variables that must not be overwritten!!! @warning_ignore("unused_private_class_variable") var __is_skipped := false @warning_ignore("unused_private_class_variable") -var __skip_reason :String = "Unknow." -var __active_test_case :String +var __skip_reason := "Unknow." +var __active_test_case: String var __awaiter := __gdunit_awaiter() @@ -29,7 +28,7 @@ var __awaiter := __gdunit_awaiter() ### in order to noticeably reduce the loading time of the test suite. # We go this hard way to increase the loading performance to avoid reparsing all the used scripts # for more detailed info -> https://github.com/godotengine/godot/issues/67400 -func __lazy_load(script_path :String) -> GDScript: +func __lazy_load(script_path: String) -> GDScript: return GdUnitAssertions.__lazy_load(script_path) @@ -81,23 +80,23 @@ func after_test() -> void: pass -func is_failure(_expected_failure :String = NO_ARG) -> bool: +func is_failure() -> bool: return Engine.get_meta("GD_TEST_FAILURE") if Engine.has_meta("GD_TEST_FAILURE") else false -func set_active_test_case(test_case :String) -> void: +func set_active_test_case(test_case: String) -> void: __active_test_case = test_case # === Tools ==================================================================== # Mapps Godot error number to a readable error message. See at ERROR # https://docs.godotengine.org/de/stable/classes/class_@globalscope.html#enum-globalscope-error -func error_as_string(error_number :int) -> String: +func error_as_string(error_number: int) -> String: return error_string(error_number) ## A litle helper to auto freeing your created objects after test execution -func auto_free(obj :Variant) -> Variant: +func auto_free(obj: Variant) -> Variant: var execution_context := GdUnitThreadManager.get_current_context().get_execution_context() assert(execution_context != null, "INTERNAL ERROR: The current execution_context is null! Please report this as bug.") @@ -105,7 +104,7 @@ func auto_free(obj :Variant) -> Variant: @warning_ignore("native_method_override") -func add_child(node :Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void: +func add_child(node: Node, force_readable_name := false, internal := Node.INTERNAL_MODE_DISABLED) -> void: super.add_child(node, force_readable_name, internal) var execution_context := GdUnitThreadManager.get_current_context().get_execution_context() if execution_context != null: @@ -123,7 +122,7 @@ func discard_error_interupted_by_timeout() -> void: ## Creates a new directory under the temporary directory *user://tmp*[br] ## Useful for storing data during test execution. [br] ## The directory is automatically deleted after test suite execution -func create_temp_dir(relative_path :String) -> String: +func create_temp_dir(relative_path: String) -> String: @warning_ignore("unsafe_method_access") return __gdunit_file_access().create_temp_dir(relative_path) @@ -138,35 +137,35 @@ func clean_temp_dir() -> void: ## Creates a new file under the temporary directory *user://tmp* + [br] ## with given name and given file (default = File.WRITE)[br] ## If success the returned File is automatically closed after the execution of the test suite -func create_temp_file(relative_path :String, file_name :String, mode := FileAccess.WRITE) -> FileAccess: +func create_temp_file(relative_path: String, file_name: String, mode := FileAccess.WRITE) -> FileAccess: @warning_ignore("unsafe_method_access") return __gdunit_file_access().create_temp_file(relative_path, file_name, mode) ## Reads a resource by given path into a PackedStringArray. -func resource_as_array(resource_path :String) -> PackedStringArray: +func resource_as_array(resource_path: String) -> PackedStringArray: @warning_ignore("unsafe_method_access") return __gdunit_file_access().resource_as_array(resource_path) ## Reads a resource by given path and returned the content as String. -func resource_as_string(resource_path :String) -> String: +func resource_as_string(resource_path: String) -> String: @warning_ignore("unsafe_method_access") return __gdunit_file_access().resource_as_string(resource_path) ## Reads a resource by given path and return Variand translated by str_to_var -func resource_as_var(resource_path :String) -> Variant: +func resource_as_var(resource_path: String) -> Variant: @warning_ignore("unsafe_method_access", "unsafe_cast") return str_to_var(__gdunit_file_access().resource_as_string(resource_path) as String) -## Waits for given signal is emited by the until a specified timeout to fail[br] +## Waits for given signal to be emitted by until a specified timeout to fail[br] ## source: the object from which the signal is emitted[br] ## signal_name: signal name[br] ## args: the expected signal arguments as an array[br] ## timeout: the timeout in ms, default is set to 2000ms -func await_signal_on(source :Object, signal_name :String, args :Array = [], timeout :int = 2000) -> Variant: +func await_signal_on(source: Object, signal_name: String, args: Array = [], timeout: int = 2000) -> Variant: @warning_ignore("unsafe_method_access") return await __awaiter.await_signal_on(source, signal_name, args, timeout) @@ -177,18 +176,57 @@ func await_idle_frame() -> void: await __awaiter.await_idle_frame() -## Waits for for a given amount of milliseconds[br] +## Waits for a given amount of milliseconds[br] ## example:[br] ## [codeblock] ## # waits for 100ms ## await await_millis(myNode, 100).completed ## [/codeblock][br] ## use this waiter and not `await get_tree().create_timer().timeout to prevent errors when a test case is timed out -func await_millis(timeout :int) -> void: +func await_millis(timeout: int) -> void: @warning_ignore("unsafe_method_access") await __awaiter.await_millis(timeout) +## Collects detailed information about orphaned nodes for debugging purposes.[br] +## +## This function gathers comprehensive details about nodes that remain in memory +## after test execution (orphans). It provides debugging information to help +## identify the source of memory leaks in tests. Must be manually called in +## tests when orphan nodes are detected.[br] +## [br] +## [b]When to Use:[/b][br] +## - When GdUnit4 reports orphan nodes after test execution[br] +## - For debugging memory leaks in test scenarios[br] +## - To get detailed information about unreleased nodes[br] +## [br] +## [b]Usage Pattern:[/b][br] +## Add this call at the end of tests that are suspected to create orphans, +## or when the test runner reports orphan detection.[br] +## [br] +## [b]Examples:[/b] +## [codeblock] +## func test_scene_management(): +## # Test code that might create orphan nodes +## var scene = preload("res://TestScene.tscn").instantiate() +## add_child(scene) +## +## # Do test operations +## scene.some_method() +## +## # Clean up (but might miss some nodes) +## scene.queue_free() +## +## # Collect orphan details if any are detected +## collect_orphan_node_details() +## [/codeblock] +## [br] +## [b]Note:[/b] This is a debugging utility function that should be removed +## or commented out once orphan issues are resolved. +func collect_orphan_node_details() -> void: + GdUnitThreadManager.get_current_context().get_execution_context().orphan_monitor_collect() + + ## Creates a new scene runner to allow simulate interactions checked a scene.[br] ## The runner will manage the scene instance and release after the runner is released[br] ## example:[br] @@ -200,7 +238,7 @@ func await_millis(timeout :int) -> void: ## # or simply creates a runner by using the scene resource path ## var runner := scene_runner("res://foo/my_scne.tscn") ## [/codeblock] -func scene_runner(scene :Variant, verbose := false) -> GdUnitSceneRunner: +func scene_runner(scene: Variant, verbose := false) -> GdUnitSceneRunner: return auto_free(__lazy_load("res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd").new(scene, verbose)) @@ -216,13 +254,13 @@ const RETURN_DEEP_STUB = GdUnitMock.RETURN_DEEP_STUB ## Creates a mock for given class name -func mock(clazz :Variant, mock_mode := RETURN_DEFAULTS) -> Variant: +func mock(clazz: Variant, mock_mode := RETURN_DEFAULTS) -> Variant: @warning_ignore("unsafe_method_access") return __lazy_load("res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd").build(clazz, mock_mode) ## Creates a spy checked given object instance -func spy(instance :Variant) -> Variant: +func spy(instance: Variant) -> Variant: @warning_ignore("unsafe_method_access") return __lazy_load("res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd").build(instance) @@ -233,30 +271,30 @@ func spy(instance :Variant) -> Variant: ## # overrides the return value of myMock.is_selected() to false ## do_return(false).on(myMock).is_selected() ## [/codeblock] -func do_return(value :Variant) -> GdUnitMock: +func do_return(value: Variant) -> GdUnitMock: return GdUnitMock.new(value) ## Verifies certain behavior happened at least once or exact number of times -func verify(obj :Variant, times := 1) -> Variant: +func verify(obj: Variant, times := 1) -> Variant: @warning_ignore("unsafe_method_access") return __gdunit_object_interactions().verify(obj, times) ## Verifies no interactions is happen checked this mock or spy -func verify_no_interactions(obj :Variant) -> GdUnitAssert: +func verify_no_interactions(obj: Variant) -> GdUnitAssert: @warning_ignore("unsafe_method_access") return __gdunit_object_interactions().verify_no_interactions(obj) ## Verifies the given mock or spy has any unverified interaction. -func verify_no_more_interactions(obj :Variant) -> GdUnitAssert: +func verify_no_more_interactions(obj: Variant) -> GdUnitAssert: @warning_ignore("unsafe_method_access") return __gdunit_object_interactions().verify_no_more_interactions(obj) ## Resets the saved function call counters checked a mock or spy -func reset(obj :Variant) -> void: +func reset(obj: Variant) -> void: @warning_ignore("unsafe_method_access") __gdunit_object_interactions().reset(obj) @@ -273,12 +311,12 @@ func reset(obj :Variant) -> void: ## # verify the signial is emitted ## await assert_signal(emitter).is_emitted('my_signal') ## [/codeblock] -func monitor_signals(source :Object, _auto_free := true) -> Object: +func monitor_signals(source: Object, _auto_free := true) -> Object: @warning_ignore("unsafe_method_access") __lazy_load("res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd")\ .get_current_context()\ .get_signal_collector()\ - .register_emitter(source) + .register_emitter(source, true) # force recreate to start with a fresh monitoring return auto_free(source) if _auto_free else source @@ -307,7 +345,7 @@ func any_float() -> GdUnitArgumentMatcher: return __gdunit_argument_matchers().by_type(TYPE_FLOAT) -## Argument matcher to match any string value +## Argument matcher to match any String value func any_string() -> GdUnitArgumentMatcher: @warning_ignore("unsafe_method_access") return __gdunit_argument_matchers().by_type(TYPE_STRING) @@ -362,7 +400,7 @@ func any_vector4() -> GdUnitArgumentMatcher: return __gdunit_argument_matchers().by_type(TYPE_VECTOR4) -## Argument matcher to match any Vector3i value +## Argument matcher to match any Vector4i value func any_vector4i() -> GdUnitArgumentMatcher: @warning_ignore("unsafe_method_access") return __gdunit_argument_matchers().by_type(TYPE_VECTOR4I) @@ -502,29 +540,46 @@ func any_class(clazz :Object) -> GdUnitArgumentMatcher: # === value extract utils ====================================================== ## Builds an extractor by given function name and optional arguments -func extr(func_name :String, args := Array()) -> GdUnitValueExtractor: +func extr(func_name: String, args := Array()) -> GdUnitValueExtractor: return __lazy_load("res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd").new(func_name, args) -## Constructs a tuple by given arguments -func tuple(arg0 :Variant, - arg1 :Variant=NO_ARG, - arg2 :Variant=NO_ARG, - arg3 :Variant=NO_ARG, - arg4 :Variant=NO_ARG, - arg5 :Variant=NO_ARG, - arg6 :Variant=NO_ARG, - arg7 :Variant=NO_ARG, - arg8 :Variant=NO_ARG, - arg9 :Variant=NO_ARG) -> GdUnitTuple: - return GdUnitTuple.new(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) +## Creates a GdUnitTuple from the provided arguments for use in test assertions. +## [br] +## This is the primary helper function for creating tuples in GdUnit4 tests. +## It provides a convenient way to group multiple expected values when using +## [method extractv] assertions. The function enforces that tuples must contain +## at least two values, as single-value extractions don't require tuple grouping. +## [br] +## [b]Parameters:[/b] [br] +## - [code]...args[/code]: Variable number of arguments (minimum 2) to group into a tuple. +## Each argument represents a value to be compared in assertions. +## [br] +## [b]Returns:[/b] [br] +## A [GdUnitTuple] containing the provided values, or an empty tuple if fewer than +## 2 arguments are provided (with an error message). +## [br] +## [b]Error Handling:[/b] [br] +## [codeblock] +## # This will push an error and return empty tuple +## var invalid = tuple("single_value") # Error: requires at least 2 arguments +## [br] +## # Correct usage - minimum 2 arguments +## var valid = tuple("name", "value") +## var valid_multi = tuple(1, 2, 3, 4, 5) # Can have many values +## [/codeblock] +func tuple(...args: Array) -> GdUnitTuple: + if args.size() < 2: + push_error("Tuple requires at least two arguments.") + return GdUnitTuple.new() + return GdUnitTuple.new.callv(args) # === Asserts ================================================================== ## The common assertion tool to verify values. ## It checks the given value by type to fit to the best assert -func assert_that(current :Variant) -> GdUnitAssert: +func assert_that(current: Variant) -> GdUnitAssert: match typeof(current): TYPE_BOOL: return assert_bool(current) @@ -549,22 +604,22 @@ func assert_that(current :Variant) -> GdUnitAssert: ## An assertion tool to verify boolean values. -func assert_bool(current :Variant) -> GdUnitBoolAssert: +func assert_bool(current: Variant) -> GdUnitBoolAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd").new(current) ## An assertion tool to verify String values. -func assert_str(current :Variant) -> GdUnitStringAssert: +func assert_str(current: Variant) -> GdUnitStringAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd").new(current) ## An assertion tool to verify integer values. -func assert_int(current :Variant) -> GdUnitIntAssert: +func assert_int(current: Variant) -> GdUnitIntAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd").new(current) ## An assertion tool to verify float values. -func assert_float(current :Variant) -> GdUnitFloatAssert: +func assert_float(current: Variant) -> GdUnitFloatAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd").new(current) @@ -574,41 +629,41 @@ func assert_float(current :Variant) -> GdUnitFloatAssert: ## [codeblock] ## assert_vector(Vector2(1.2, 1.000001)).is_equal(Vector2(1.2, 1.000001)) ## [/codeblock] -func assert_vector(current :Variant, type_check := true) -> GdUnitVectorAssert: +func assert_vector(current: Variant, type_check := true) -> GdUnitVectorAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd").new(current, type_check) ## An assertion tool to verify arrays. -func assert_array(current :Variant, type_check := true) -> GdUnitArrayAssert: +func assert_array(current: Variant, type_check := true) -> GdUnitArrayAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd").new(current, type_check) ## An assertion tool to verify dictionaries. -func assert_dict(current :Variant) -> GdUnitDictionaryAssert: +func assert_dict(current: Variant) -> GdUnitDictionaryAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd").new(current) ## An assertion tool to verify FileAccess. -func assert_file(current :Variant) -> GdUnitFileAssert: +func assert_file(current: Variant) -> GdUnitFileAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd").new(current) ## An assertion tool to verify Objects. -func assert_object(current :Variant) -> GdUnitObjectAssert: +func assert_object(current: Variant) -> GdUnitObjectAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd").new(current) -func assert_result(current :Variant) -> GdUnitResultAssert: +func assert_result(current: Variant) -> GdUnitResultAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd").new(current) ## An assertion tool that waits until a certain time for an expected function return value -func assert_func(instance :Object, func_name :String, args := Array()) -> GdUnitFuncAssert: +func assert_func(instance: Object, func_name: String, args := Array()) -> GdUnitFuncAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd").new(instance, func_name, args) -## An Assertion Tool to verify for emitted signals until a certain time. -func assert_signal(instance :Object) -> GdUnitSignalAssert: +## An assertion tool to verify for emitted signals until a certain time. +func assert_signal(instance: Object) -> GdUnitSignalAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd").new(instance) @@ -619,7 +674,7 @@ func assert_signal(instance :Object) -> GdUnitSignalAssert: ## assert_failure(func(): assert_bool(true).is_not_equal(true)) \ ## .has_message("Expecting:\n 'true'\n not equal to\n 'true'") ## [/codeblock] -func assert_failure(assertion :Callable) -> GdUnitFailureAssert: +func assert_failure(assertion: Callable) -> GdUnitFailureAssert: @warning_ignore("unsafe_method_access") return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute(assertion) @@ -631,33 +686,57 @@ func assert_failure(assertion :Callable) -> GdUnitFailureAssert: ## await assert_failure_await(func(): assert_bool(true).is_not_equal(true)) \ ## .has_message("Expecting:\n 'true'\n not equal to\n 'true'") ## [/codeblock] -func assert_failure_await(assertion :Callable) -> GdUnitFailureAssert: +func assert_failure_await(assertion: Callable) -> GdUnitFailureAssert: @warning_ignore("unsafe_method_access") return await __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd").new().execute_and_await(assertion) -## An assertion tool to verify for Godot errors.[br] -## You can use to verify for certain Godot erros like failing assertions, push_error, push_warn.[br] +## An assertion tool to verify Godot errors.[br] +## You can use to verify certain Godot errors like failing assertions, push_error, push_warn.[br] ## Usage: ## [codeblock] -## # tests no error was occured during execution the code +## # tests no error occurred during execution of the code ## await assert_error(func (): return 0 )\ ## .is_success() ## -## # tests an push_error('test error') was occured during execution the code +## # tests a push_error('test error') occured during execution of the code ## await assert_error(func (): push_error('test error') )\ ## .is_push_error('test error') ## [/codeblock] -func assert_error(current :Callable) -> GdUnitGodotErrorAssert: +func assert_error(current: Callable) -> GdUnitGodotErrorAssert: return __lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd").new(current) +## Explicitly fails the current test indicating that the feature is not yet implemented.[br] +## This function is useful during development when you want to write test cases before implementing the actual functionality.[br] +## It provides a clear indication that the test failure is expected because the feature is still under development.[br] +## Usage: +## [codeblock] +## # Test for a feature that will be implemented later +## func test_advanced_ai_behavior(): +## assert_not_yet_implemented() +## +## [/codeblock] func assert_not_yet_implemented() -> void: @warning_ignore("unsafe_method_access") __gdunit_assert().new(null).do_fail() -func fail(message :String) -> void: +## Explicitly fails the current test with a custom error message.[br] +## This function reports an error but does not terminate test execution automatically.[br] +## You must use 'return' after calling fail() to stop the test since GDScript has no exception support.[br] +## Useful for complex conditional testing scenarios where standard assertions are insufficient.[br] +## Usage: +## [codeblock] +## # Fail test when conditions are not met +## if !custom_check(player): +## fail("Player should be alive but has %d health" % player.health) +## return +## +## # Continue with test if conditions pass +## assert_that(player.health).is_greater(0) +## [/codeblock] +func fail(message: String) -> void: @warning_ignore("unsafe_method_access") __gdunit_assert().new(null).report_error(message) diff --git a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid index faaa948b..9b9bdccd 100644 --- a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid +++ b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid @@ -1 +1 @@ -uid://d27xn7ceg8ta1 +uid://i2un0ck8mesu diff --git a/addons/gdUnit4/src/GdUnitTuple.gd b/addons/gdUnit4/src/GdUnitTuple.gd index 6c910023..9d56dd80 100644 --- a/addons/gdUnit4/src/GdUnitTuple.gd +++ b/addons/gdUnit4/src/GdUnitTuple.gd @@ -1,28 +1,86 @@ -## A tuple implementation to hold two or many values +## A tuple implementation for GdUnit4 test assertions and value extraction. +## @tutorial(GdUnit4 Array Assertions): https://mikeschulze.github.io/gdUnit4/latest/testing/assert-array/#extractv +## @tutorial(GdUnit4 Testing Framework): https://mikeschulze.github.io/gdUnit4/ +## [br] +## The GdUnitTuple class is a utility container designed specifically for the GdUnit4 +## testing framework. It enables advanced assertion operations, particularly when +## extracting and comparing multiple values from complex test results. +## [br] +## [b]Primary Use Cases in Testing:[/b] [br] +## - Extracting multiple properties from test objects with [method extractv]## [br] +## - Grouping related assertion values for comparison## [br] +## - Returning multiple values from test helper methods## [br] +## - Organizing expected vs actual value pairs in assertions## [br] +## [br] +## [b]Example Usage in Tests:[/b] +## [codeblock] +## func test_player_stats_after_level_up(): +## var player = Player.new() +## player.level_up() +## +## # Extract multiple properties using tuple +## assert_array([player]) \ +## .extractv(extr("name"), extr("level"), extr("hp")) \ +## .contains(tuple("Hero", 2, 150)) +## +## func test_enemy_spawn_positions(): +## var enemies: Array = spawn_enemies(3) +## +## # Verify multiple enemies have correct position data +## assert_array(enemies) \ +## .extractv(extr("position.x"), extr("position.y")) \ +## .contains_exactly([ +## tuple(100, 200), +## tuple(150, 200), +## tuple(200, 200) +## ]) +## [/codeblock] +## [br] +## [b]Integration with GdUnit4 Assertions:[/b] [br] +## Tuples work seamlessly with array assertion methods like: [br] +## - [code]contains()[/code] - Check if extracted values contain specific tuples [br] +## - [code]contains_exactly()[/code] - Verify exact tuple matches [br] +## - [code]is_equal()[/code] - Compare tuple equality [br] +## [br] +## [b]Note:[/b] This class is part of the GdUnit4 testing framework's internal +## utilities and is primarily intended for use within test assertions rather +## than production code. class_name GdUnitTuple extends RefCounted -const NO_ARG :Variant = GdUnitConstants.NO_ARG +var _values: Array = [] -var __values :Array = Array() - -func _init(arg0:Variant, - arg1 :Variant=NO_ARG, - arg2 :Variant=NO_ARG, - arg3 :Variant=NO_ARG, - arg4 :Variant=NO_ARG, - arg5 :Variant=NO_ARG, - arg6 :Variant=NO_ARG, - arg7 :Variant=NO_ARG, - arg8 :Variant=NO_ARG, - arg9 :Variant=NO_ARG) -> void: - __values = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +## Initializes a new GdUnitTuple with test values. +## [br] +## Creates a tuple to hold multiple values extracted from test objects +## or expected values for assertions. Commonly used with the [code]tuple()[/code] +## helper function in GdUnit4 tests. +## [br] +## [b]Parameters:[/b] +## - [code]...args[/code]: Variable number of values to store. +func _init(...args: Array) -> void: + _values = args +## Returns the tuple's values as an array for assertion comparisons. +## [br] +## Provides access to the stored test values. Used internally by GdUnit4's +## assertion system when comparing tuples in test validations. +## [br] +## [b]Returns:[/b] +## An [Array] containing all values stored in the tuple. func values() -> Array: - return __values + return _values +## Returns a string representation for test output and debugging. +## [br] +## Formats the tuple for display in test results, error messages, and debug logs. +## This method is automatically called by GdUnit4 when displaying assertion +## failures involving tuples. +## [br] +## [b]Returns:[/b] +## A [String] in the format "tuple([value1, value2, ...])" func _to_string() -> String: - return "tuple(%s)" % str(__values) + return "tuple(%s)" % str(_values) diff --git a/addons/gdUnit4/src/GdUnitTuple.gd.uid b/addons/gdUnit4/src/GdUnitTuple.gd.uid index 9eb02219..c775e975 100644 --- a/addons/gdUnit4/src/GdUnitTuple.gd.uid +++ b/addons/gdUnit4/src/GdUnitTuple.gd.uid @@ -1 +1 @@ -uid://4soxgauogw4g +uid://d0rsmekmp6eu1 diff --git a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid index 9b4d3ee6..c7bcf027 100644 --- a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid +++ b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid @@ -1 +1 @@ -uid://dus26afrslc7c +uid://dhfp20aw56jm5 diff --git a/addons/gdUnit4/src/GdUnitVectorAssert.gd b/addons/gdUnit4/src/GdUnitVectorAssert.gd index 915fd3b6..c186cba2 100644 --- a/addons/gdUnit4/src/GdUnitVectorAssert.gd +++ b/addons/gdUnit4/src/GdUnitVectorAssert.gd @@ -1,57 +1,55 @@ ## An Assertion Tool to verify Vector values -class_name GdUnitVectorAssert +@abstract class_name GdUnitVectorAssert extends GdUnitAssert -## Verifies that the current value is equal to expected one. -@warning_ignore("unused_parameter") -func is_equal(expected :Variant) -> GdUnitVectorAssert: - return self +## Verifies that the current value is null. +@abstract func is_null() -> GdUnitVectorAssert + + +## Verifies that the current value is not null. +@abstract func is_not_null() -> GdUnitVectorAssert + + +## Verifies that the current value is equal to the given one. +@abstract func is_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is not equal to expected one. -@warning_ignore("unused_parameter") -func is_not_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_not_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current and expected value are approximately equal. -@warning_ignore("unused_parameter", "shadowed_global_identifier") -func is_equal_approx(expected :Variant, approx :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_equal_approx(expected: Variant, approx: Variant) -> GdUnitVectorAssert + + +## Overrides the default failure message by given custom message. +@abstract func override_failure_message(message: String) -> GdUnitVectorAssert + + +## Appends a custom message to the failure message. +@abstract func append_failure_message(message: String) -> GdUnitVectorAssert ## Verifies that the current value is less than the given one. -@warning_ignore("unused_parameter") -func is_less(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_less(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is less than or equal the given one. -@warning_ignore("unused_parameter") -func is_less_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_less_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is greater than the given one. -@warning_ignore("unused_parameter") -func is_greater(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_greater(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is greater than or equal the given one. -@warning_ignore("unused_parameter") -func is_greater_equal(expected :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_greater_equal(expected: Variant) -> GdUnitVectorAssert ## Verifies that the current value is between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_between(from :Variant, to :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_between(from: Variant, to: Variant) -> GdUnitVectorAssert ## Verifies that the current value is not between the given boundaries (inclusive). -@warning_ignore("unused_parameter") -func is_not_between(from :Variant, to :Variant) -> GdUnitVectorAssert: - return self +@abstract func is_not_between(from: Variant, to: Variant) -> GdUnitVectorAssert diff --git a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid index 7222e571..9955edca 100644 --- a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid +++ b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid @@ -1 +1 @@ -uid://61ct1w1pqjnk +uid://chacjkt6rrd8q diff --git a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid index 1a3409a7..608b8668 100644 --- a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid @@ -1 +1 @@ -uid://d3a4ypfxdkmru +uid://bk7wki5gq5ynl diff --git a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid index 727b98e6..88268bdf 100644 --- a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid @@ -1 +1 @@ -uid://bd0yn1rsh6307 +uid://dynyh1ew8q1uq diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd b/addons/gdUnit4/src/asserts/GdAssertMessages.gd index 2043e086..5a2fa1c7 100644 --- a/addons/gdUnit4/src/asserts/GdAssertMessages.gd +++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd @@ -4,8 +4,8 @@ extends Resource const WARN_COLOR = "#EFF883" const ERROR_COLOR = "#CD5C5C" const VALUE_COLOR = "#1E90FF" -const SUB_COLOR := Color(1, 0, 0, .3) -const ADD_COLOR := Color(0, 1, 0, .3) +const SUB_COLOR := Color(1, 0, 0, .15) +const ADD_COLOR := Color(0, 1, 0, .15) # Dictionary of control characters and their readable representations @@ -40,35 +40,53 @@ static func input_event_as_text(event :InputEvent) -> String: var text := "" if event is InputEventKey: var key_event := event as InputEventKey - text += "InputEventKey : key='%s', pressed=%s, keycode=%d, physical_keycode=%s" % [ - event.as_text(), key_event.pressed, key_event.keycode, key_event.physical_keycode] + text += """ + InputEventKey : keycode=%s (%s) pressed: %s + physical_keycode: %s + location: %s + echo: %s""" % [ + key_event.keycode, + key_event.as_text_keycode(), + key_event.pressed, + key_event.physical_keycode, + key_event.location, + key_event.echo] else: text += event.as_text() if event is InputEventMouse: var mouse_event := event as InputEventMouse - text += ", global_position %s" % mouse_event.global_position + text += """ + global_position: %s""" % mouse_event.global_position if event is InputEventWithModifiers: var mouse_event := event as InputEventWithModifiers - text += ", shift=%s, alt=%s, control=%s, meta=%s, command=%s" % [ + text += """ + -------- + mods: %s + shift: %s + alt: %s + control: %s + meta: %s + command: %s""" % [ + mouse_event.get_modifiers_mask(), mouse_event.shift_pressed, mouse_event.alt_pressed, mouse_event.ctrl_pressed, mouse_event.meta_pressed, mouse_event.command_or_control_autoremap] - return text + return text.dedent() -static func _colored_string_div(characters :String) -> String: - return colored_array_div(characters.to_utf8_buffer()) +static func _colored_string_div(characters: String) -> String: + return colored_array_div(characters.to_utf32_buffer().to_int32_array()) -static func colored_array_div(characters :PackedByteArray) -> String: +static func colored_array_div(characters: PackedInt32Array) -> String: if characters.is_empty(): return "" - var result := PackedByteArray() + var result := PackedInt32Array() var index := 0 - var missing_chars := PackedByteArray() - var additional_chars := PackedByteArray() + var missing_chars := PackedInt32Array() + var additional_chars := PackedInt32Array() while index < characters.size(): var character := characters[index] @@ -84,17 +102,17 @@ static func colored_array_div(characters :PackedByteArray) -> String: _: if not missing_chars.is_empty(): result.append_array(format_chars(missing_chars, SUB_COLOR)) - missing_chars = PackedByteArray() + missing_chars = PackedInt32Array() if not additional_chars.is_empty(): result.append_array(format_chars(additional_chars, ADD_COLOR)) - additional_chars = PackedByteArray() + additional_chars = PackedInt32Array() @warning_ignore("return_value_discarded") result.append(character) index += 1 result.append_array(format_chars(missing_chars, SUB_COLOR)) result.append_array(format_chars(additional_chars, ADD_COLOR)) - return result.get_string_from_utf8() + return result.to_byte_array().get_string_from_utf32() static func _typed_value(value :Variant) -> String: @@ -119,6 +137,10 @@ static func _nerror(number :Variant) -> String: return "[color=%s]%s[/color]" % [ERROR_COLOR, str(number)] +static func _colored(value: Variant, color: Color) -> String: + return "[color=%s]%s[/color]" % [color.to_html(), value] + + static func _colored_value(value :Variant) -> String: match typeof(value): TYPE_STRING, TYPE_STRING_NAME: @@ -161,19 +183,53 @@ static func _index_report_as_table(index_reports :Array) -> String: return table.replace("$cells", cells) -static func orphan_detected_on_suite_setup(count :int) -> String: - return "%s\n Detected <%d> orphan nodes during test suite setup stage! [b]Check before() and after()![/b]" % [ - _warning("WARNING:"), count] + +static func orphan_warning(orphans_count: int) -> String: + return """ + %s: Found %s possible orphan nodes. + Add %s to the end of the test to collect details.""".dedent().trim_prefix("\n") % [ + _warning("WARNING:"), + _nerror(orphans_count), + _colored_value("collect_orphan_node_details()") + ] + +static func orphan_detected_on_suite_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String: + return """ + %s Detected %s orphan nodes! + [b]Verify your test suite setup.[/b] + %s""".dedent().trim_prefix("\n") % [ + _warning("WARNING:"), + _nerror(orphans.size()), + _build_orphan_node_stacktrace(orphans)] + + +static func orphan_detected_on_test_setup(orphans: Array[GdUnitOrphanNodeInfo]) -> String: + return """ + %s Detected %s orphan nodes on test setup! + [b]Check before_test() and after_test()![/b] + %s""".dedent().trim_prefix("\n") % [ + _warning("WARNING:"), + _nerror(orphans.size()), + _build_orphan_node_stacktrace(orphans) + ] -static func orphan_detected_on_test_setup(count :int) -> String: - return "%s\n Detected <%d> orphan nodes during test setup! [b]Check before_test() and after_test()![/b]" % [ - _warning("WARNING:"), count] +static func orphan_detected_on_test(orphans: Array[GdUnitOrphanNodeInfo]) -> String: + return """ + %s Detected %s orphan nodes! + %s""".dedent().trim_prefix("\n") % [ + _warning("WARNING:"), + _nerror(orphans.size()), + _build_orphan_node_stacktrace(orphans) + ] -static func orphan_detected_on_test(count :int) -> String: - return "%s\n Detected <%d> orphan nodes during test execution!" % [ - _warning("WARNING:"), count] +static func _build_orphan_node_stacktrace(orphans: Array[GdUnitOrphanNodeInfo]) -> String: + var stack_trace := "\n" + for orphan in orphans: + stack_trace += orphan.as_trace(orphan, true) + "\n" + return stack_trace.indent(" ") + static func fuzzer_interuped(iterations: int, error: String) -> String: @@ -188,6 +244,10 @@ static func test_timeout(timeout :int) -> String: return "%s\n %s" % [_error("Timeout !"), _colored_value("Test timed out after %s" % LocalTime.elapsed(timeout))] +static func test_session_terminated() -> String: + return "%s" % _error("Test Session Terminated") + + # gdlint:disable = mixed-tabs-and-spaces static func test_suite_skipped(hint :String, skip_count :int) -> String: return """ @@ -637,12 +697,12 @@ static func error_contains_exactly(current: Array, expected: Array) -> String: return "%s\n %s\n but was\n %s" % [_error("Expecting exactly equal:"), _colored_value(expected), _colored_value(current)] -static func format_chars(characters :PackedByteArray, type :Color) -> PackedByteArray: +static func format_chars(characters: PackedInt32Array, type: Color) -> PackedInt32Array: if characters.size() == 0:# or characters[0] == 10: return characters # Replace each control character with its readable form - var formatted_text := characters.get_string_from_utf8() + var formatted_text := characters.to_byte_array().get_string_from_utf32() for control_char: String in CONTROL_CHARS: var replace_text: String = CONTROL_CHARS[control_char] formatted_text = formatted_text.replace(control_char, replace_text) @@ -664,8 +724,8 @@ static func format_chars(characters :PackedByteArray, type :Color) -> PackedByte ascii_text ] - var result := PackedByteArray() - result.append_array(message.to_utf8_buffer()) + var result := PackedInt32Array() + result.append_array(message.to_utf32_buffer().to_int32_array()) return result diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid index a22e8bef..24349657 100644 --- a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid +++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid @@ -1 +1 @@ -uid://cc3hdk43hh47 +uid://bhemsxum7avn5 diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid index 8660da2b..c21ffbc8 100644 --- a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid +++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid @@ -1 +1 @@ -uid://6kr4lpvi1qwq +uid://c1bs2q50y0pbf diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd index 748e20e6..63b45a07 100644 --- a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd @@ -26,11 +26,13 @@ func _notification(event: int) -> void: func report_success() -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -40,11 +42,13 @@ func failure_message() -> String: func override_failure_message(message: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self func append_failure_message(message: String) -> GdUnitArrayAssert: + @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -71,7 +75,7 @@ func _toPackedStringArray(value: Variant) -> PackedStringArray: return PackedStringArray([str(value)]) -func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array[Array]: +func _array_equals_div(current: Variant, expected: Variant, case_sensitive: bool = false) -> Array: var current_value := _toPackedStringArray(current) var expected_value := _toPackedStringArray(expected) var index_report := Array() @@ -109,76 +113,86 @@ func _array_div(compare_mode: GdObjects.COMPARE_MODE, left: Array[Variant], righ return [not_expect, not_found] -func _contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var by_reference := compare_mode == GdObjects.COMPARE_MODE.OBJECT_REFERENCE var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], expected, by_reference)) + return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], expected_value, by_reference)) @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant]) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant]) #var not_expect := diffs[0] as Array var not_found: Array = diffs[1] if not not_found.is_empty(): - return report_error(GdAssertMessages.error_arr_contains(current_value, expected, [], not_found, by_reference)) + return report_error(GdAssertMessages.error_arr_contains(current_value, expected_value, [], not_found, by_reference)) return report_success() -func _contains_exactly(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains_exactly(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly(null, expected_value, [], expected_value, compare_mode)) # has same content in same order - if _is_equal(current_value, expected, false, compare_mode): + if _is_equal(current_value, expected_value, false, compare_mode): return report_success() # check has same elements but in different order - if _is_equals_sorted(current_value, expected, false, compare_mode): - return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, [], [], compare_mode)) + if _is_equals_sorted(current_value, expected_value, false, compare_mode): + return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, [], [], compare_mode)) # find the difference @warning_ignore("unsafe_cast") var diffs := _array_div(compare_mode, current_value as Array[Variant], - expected as Array[Variant], + expected_value as Array[Variant], GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) var not_expect: Array[Variant] = diffs[0] var not_found: Array[Variant] = diffs[1] - return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected, not_expect, not_found, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly(current_value, expected_value, not_expect, not_found, compare_mode)) -func _contains_exactly_in_any_order(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _contains_exactly_in_any_order(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], + expected_value, compare_mode)) # find the difference @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant], false) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant], false) var not_expect: Array[Variant] = diffs[0] var not_found: Array[Variant] = diffs[1] if not_expect.is_empty() and not_found.is_empty(): return report_success() - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, not_expect, not_found, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, not_expect, + not_found, compare_mode)) -func _not_contains(expected: Variant, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func _not_contains(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) if current_value == null: - return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected, [], expected, compare_mode)) + return report_error(GdAssertMessages.error_arr_contains_exactly_in_any_order(current_value, expected_value, [], + expected_value, compare_mode)) @warning_ignore("unsafe_cast") - var diffs := _array_div(compare_mode, current_value as Array[Variant], expected as Array[Variant]) + var diffs := _array_div(compare_mode, current_value as Array[Variant], expected_value as Array[Variant]) var found: Array[Variant] = diffs[0] @warning_ignore("unsafe_cast") if found.size() == (current_value as Array).size(): return report_success() @warning_ignore("unsafe_cast") - var diffs2 := _array_div(compare_mode, expected as Array[Variant], diffs[1] as Array[Variant]) - return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected, diffs2[0], compare_mode)) + var diffs2 := _array_div(compare_mode, expected_value as Array[Variant], diffs[1] as Array[Variant]) + return report_error(GdAssertMessages.error_arr_not_contains(current_value, expected_value, diffs2[0], compare_mode)) func is_null() -> GdUnitArrayAssert: @@ -193,15 +207,16 @@ func is_not_null() -> GdUnitArrayAssert: return self -# Verifies that the current String is equal to the given one. -func is_equal(expected: Variant) -> GdUnitArrayAssert: - if _type_check and not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_equal(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if current_value == null and expected != null: - return report_error(GdAssertMessages.error_equal(null, expected)) - if not _is_equal(current_value, expected): - var diff := _array_equals_div(current_value, expected) + var expected_value: Variant= _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null and expected_value != null: + return report_error(GdAssertMessages.error_equal(null, expected_value)) + + if not _is_equal(current_value, expected_value): + var diff := _array_equals_div(current_value, expected_value) var expected_as_list := GdArrayTools.as_string(diff[0], false) var current_as_list := GdArrayTools.as_string(diff[1], false) var index_report: Array = diff[2] @@ -210,16 +225,18 @@ func is_equal(expected: Variant) -> GdUnitArrayAssert: # Verifies that the current Array is equal to the given one, ignoring case considerations. -func is_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: - if _type_check and not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if current_value == null and expected != null: + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + if current_value == null and expected_value != null: @warning_ignore("unsafe_cast") - return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected as Array))) - if not _is_equal(current_value, expected, true): + return report_error(GdAssertMessages.error_equal(null, GdArrayTools.as_string(expected_value))) + + if not _is_equal(current_value, expected_value, true): @warning_ignore("unsafe_cast") - var diff := _array_equals_div(current_value as Array[Variant], expected as Array[Variant], true) + var diff := _array_equals_div(current_value, expected_value, true) var expected_as_list := GdArrayTools.as_string(diff[0]) var current_as_list := GdArrayTools.as_string(diff[1]) var index_report: Array = diff[2] @@ -227,24 +244,28 @@ func is_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: return report_success() -func is_not_equal(expected: Variant) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_not_equal(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if _is_equal(current_value, expected): - return report_error(GdAssertMessages.error_not_equal(current_value, expected)) + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + + if _is_equal(current_value, expected_value): + return report_error(GdAssertMessages.error_not_equal(current_value, expected_value)) return report_success() -func is_not_equal_ignoring_case(expected: Variant) -> GdUnitArrayAssert: - if not _validate_value_type(expected): - return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected)) +func is_not_equal_ignoring_case(...expected: Array) -> GdUnitArrayAssert: var current_value: Variant = get_current_value() - if _is_equal(current_value, expected, true): + var expected_value: Variant = _extract_variadic_value(expected) + if not _validate_value_type(expected_value): + return report_error("ERROR: expected value: <%s>\n is not a Array Type!" % GdObjects.typeof_as_string(expected_value)) + + if _is_equal(current_value, expected_value, true): @warning_ignore("unsafe_cast") var c := GdArrayTools.as_string(current_value as Array) @warning_ignore("unsafe_cast") - var e := GdArrayTools.as_string(expected as Array) + var e := GdArrayTools.as_string(expected_value) return report_error(GdAssertMessages.error_not_equal_case_insensetiv(c, e)) return report_success() @@ -294,35 +315,35 @@ func has_size(expected: int) -> GdUnitArrayAssert: return report_success() -func contains(expected: Variant) -> GdUnitArrayAssert: +func contains(...expected: Array) -> GdUnitArrayAssert: return _contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_exactly(expected: Variant) -> GdUnitArrayAssert: +func contains_exactly(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert: +func contains_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func contains_same(expected: Variant) -> GdUnitArrayAssert: +func contains_same(...expected: Array) -> GdUnitArrayAssert: return _contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func contains_same_exactly(expected: Variant) -> GdUnitArrayAssert: +func contains_same_exactly(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func contains_same_exactly_in_any_order(expected: Variant) -> GdUnitArrayAssert: +func contains_same_exactly_in_any_order(...expected: Array) -> GdUnitArrayAssert: return _contains_exactly_in_any_order(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func not_contains(expected: Variant) -> GdUnitArrayAssert: +func not_contains(...expected: Array) -> GdUnitArrayAssert: return _not_contains(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func not_contains_same(expected: Variant) -> GdUnitArrayAssert: +func not_contains_same(...expected: Array) -> GdUnitArrayAssert: return _not_contains(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) @@ -332,9 +353,9 @@ func is_instanceof(expected: Variant) -> GdUnitAssert: return self -func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: +func extract(func_name: String, ...func_args: Array) -> GdUnitArrayAssert: var extracted_elements := Array() - + var args: Array = _extract_variadic_value(func_args) var extractor := GdUnitFuncValueExtractor.new(func_name, args) var current: Variant = get_current_value() if current == null: @@ -346,49 +367,33 @@ func extract(func_name: String, args := Array()) -> GdUnitArrayAssert: return self -func extractv( - extr0: GdUnitValueExtractor, - extr1: GdUnitValueExtractor = null, - extr2: GdUnitValueExtractor = null, - extr3: GdUnitValueExtractor = null, - extr4: GdUnitValueExtractor = null, - extr5: GdUnitValueExtractor = null, - extr6: GdUnitValueExtractor = null, - extr7: GdUnitValueExtractor = null, - extr8: GdUnitValueExtractor = null, - extr9: GdUnitValueExtractor = null) -> GdUnitArrayAssert: - var extractors: Variant = GdArrayTools.filter_value([extr0, extr1, extr2, extr3, extr4, extr5, extr6, extr7, extr8, extr9], null) +func extractv(...extractors: Array) -> GdUnitArrayAssert: var extracted_elements := Array() var current: Variant = get_current_value() if current == null: _current_value_provider = DefaultValueProvider.new(null) else: for element: Variant in current: - var ev: Array[Variant] = [ - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG, - GdUnitTuple.NO_ARG - ] - @warning_ignore("unsafe_cast") - for index: int in (extractors as Array).size(): + var ev: Array[Variant] = [] + for index: int in extractors.size(): var extractor: GdUnitValueExtractor = extractors[index] - ev[index] = extractor.extract_value(element) - @warning_ignore("unsafe_cast") - if (extractors as Array).size() > 1: - extracted_elements.append(GdUnitTuple.new(ev[0], ev[1], ev[2], ev[3], ev[4], ev[5], ev[6], ev[7], ev[8], ev[9])) + ev.append(extractor.extract_value(element)) + if extractors.size() > 1: + extracted_elements.append(GdUnitTuple.new.callv(ev)) else: extracted_elements.append(ev[0]) _current_value_provider = DefaultValueProvider.new(extracted_elements) return self +## Small helper to support the old expected arguments as single array and variadic arguments +func _extract_variadic_value(values: Variant) -> Variant: + @warning_ignore("unsafe_method_access") + if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]): + return values[0] + return values + + @warning_ignore("incompatible_ternary") func _is_equal( left: Variant, diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid index 8fa7f5a1..17d1b4bd 100644 --- a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid @@ -1 +1 @@ -uid://e04hjp5huh74 +uid://k4aaxkfd1egc diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd index c08bc132..9f578c4d 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd @@ -42,30 +42,16 @@ func do_fail() -> GdUnitAssert: return report_error(GdAssertMessages.error_not_implemented()) -func override_failure_message(message :String) -> GdUnitAssert: +func override_failure_message(message: String) -> GdUnitAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitAssert: +func append_failure_message(message: String) -> GdUnitAssert: _additional_failure_message = message return self -func is_equal(expected :Variant) -> GdUnitAssert: - var current :Variant = current_value() - if not GdObjects.equals(current, expected): - return report_error(GdAssertMessages.error_equal(current, expected)) - return report_success() - - -func is_not_equal(expected :Variant) -> GdUnitAssert: - var current :Variant = current_value() - if GdObjects.equals(current, expected): - return report_error(GdAssertMessages.error_not_equal(current, expected)) - return report_success() - - func is_null() -> GdUnitAssert: var current :Variant = current_value() if current != null: @@ -78,3 +64,17 @@ func is_not_null() -> GdUnitAssert: if current == null: return report_error(GdAssertMessages.error_is_not_null()) return report_success() + + +func is_equal(expected: Variant) -> GdUnitAssert: + var current: Variant = current_value() + if not GdObjects.equals(current, expected): + return report_error(GdAssertMessages.error_equal(current, expected)) + return report_success() + + +func is_not_equal(expected: Variant) -> GdUnitAssert: + var current: Variant = current_value() + if GdObjects.equals(current, expected): + return report_error(GdAssertMessages.error_not_equal(current, expected)) + return report_success() diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid index 974376c9..719634c6 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid @@ -1 +1 @@ -uid://ciwobc7f6h46r +uid://deec7jsb3wg6a diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd index 875fb8ed..a5b53c17 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd +++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd @@ -7,6 +7,7 @@ extends RefCounted func _init() -> void: # preload all gdunit assertions to speedup testsuite loading time # gdlint:disable=private-method-call + @warning_ignore_start("return_value_discarded") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd") @@ -22,6 +23,7 @@ func _init() -> void: GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd") GdUnitAssertions.__lazy_load("res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd") + @warning_ignore_restore("return_value_discarded") ### We now load all used asserts and tool scripts into the cache according to the principle of "lazy loading" diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid index 009718e3..c0281a87 100644 --- a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid @@ -1 +1 @@ -uid://mdemt0sdcelx +uid://ceucqq0wap6co diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd index 5daebcec..2fc0ce43 100644 --- a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitBoolAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitBoolAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,26 +39,24 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitBoolAssert: +func override_failure_message(message: String) -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitBoolAssert: +func append_failure_message(message: String) -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self -# Verifies that the current value is null. func is_null() -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.is_null() return self -# Verifies that the current value is not null. func is_not_null() -> GdUnitBoolAssert: @warning_ignore("return_value_discarded") _base.is_not_null() diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid index 7b1d3294..71705119 100644 --- a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid @@ -1 +1 @@ -uid://cuu0go0gd1ul7 +uid://cb0ibnnwdgpys diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd index 17eba6ab..c57bbc23 100644 --- a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd @@ -20,11 +20,13 @@ func _notification(event :int) -> void: func report_success() -> GdUnitDictionaryAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitDictionaryAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -33,13 +35,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitDictionaryAssert: +func override_failure_message(message: String) -> GdUnitDictionaryAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitDictionaryAssert: +func append_failure_message(message: String) -> GdUnitDictionaryAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,21 +63,19 @@ func is_not_null() -> GdUnitDictionaryAssert: return self -func is_equal(expected :Variant) -> GdUnitDictionaryAssert: +func is_equal(expected: Variant) -> GdUnitDictionaryAssert: var current :Variant = current_value() if current == null: return report_error(GdAssertMessages.error_equal(null, GdAssertMessages.format_dict(expected))) if not GdObjects.equals(current, expected): var c := GdAssertMessages.format_dict(current) var e := GdAssertMessages.format_dict(expected) - var diff := GdDiffTool.string_diff(c, e) - var curent_diff := GdAssertMessages.colored_array_div(diff[1]) - return report_error(GdAssertMessages.error_equal(curent_diff, e)) + return report_error(GdAssertMessages.error_equal(c, e)) return report_success() -func is_not_equal(expected :Variant) -> GdUnitDictionaryAssert: - var current :Variant = current_value() +func is_not_equal(expected: Variant) -> GdUnitDictionaryAssert: + var current: Variant = current_value() if GdObjects.equals(current, expected): return report_error(GdAssertMessages.error_not_equal(current, expected)) return report_success() @@ -89,9 +89,7 @@ func is_same(expected :Variant) -> GdUnitDictionaryAssert: if not is_same(current, expected): var c := GdAssertMessages.format_dict(current) var e := GdAssertMessages.format_dict(expected) - var diff := GdDiffTool.string_diff(c, e) - var curent_diff := GdAssertMessages.colored_array_div(diff[1]) - return report_error(GdAssertMessages.error_is_same(curent_diff, e)) + return report_error(GdAssertMessages.error_is_same(c, e)) return report_success() @@ -129,16 +127,18 @@ func has_size(expected: int) -> GdUnitDictionaryAssert: return report_success() -func _contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: +func _contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: var current :Variant = current_value() + var expected_value: Array = _extract_variadic_value(expected) if current == null: return report_error(GdAssertMessages.error_is_not_null()) # find expected keys @warning_ignore("unsafe_cast") - var keys_not_found :Array = expected.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode)) + var keys_not_found :Array = expected_value.filter(_filter_by_key.bind((current as Dictionary).keys(), compare_mode)) if not keys_not_found.is_empty(): @warning_ignore("unsafe_cast") - return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected, keys_not_found, compare_mode)) + return report_error(GdAssertMessages.error_contains_keys((current as Dictionary).keys() as Array, expected_value, + keys_not_found, compare_mode)) return report_success() @@ -156,18 +156,19 @@ func _contains_key_value(key :Variant, value :Variant, compare_mode :GdObjects.C return report_success() -func _not_contains_keys(expected :Array, compare_mode :GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: +func _not_contains_keys(expected: Array, compare_mode: GdObjects.COMPARE_MODE) -> GdUnitDictionaryAssert: var current :Variant = current_value() + var expected_value: Array = _extract_variadic_value(expected) if current == null: return report_error(GdAssertMessages.error_is_not_null()) var dict_current: Dictionary = current - var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected, compare_mode, true)) + var keys_found :Array = dict_current.keys().filter(_filter_by_key.bind(expected_value, compare_mode, true)) if not keys_found.is_empty(): - return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected, keys_found, compare_mode)) + return report_error(GdAssertMessages.error_not_contains_keys(dict_current.keys() as Array, expected_value, keys_found, compare_mode)) return report_success() -func contains_keys(expected :Array) -> GdUnitDictionaryAssert: +func contains_keys(...expected: Array) -> GdUnitDictionaryAssert: return _contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) @@ -175,7 +176,7 @@ func contains_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAssert: return _contains_key_value(key, value, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) -func not_contains_keys(expected :Array) -> GdUnitDictionaryAssert: +func not_contains_keys(...expected: Array) -> GdUnitDictionaryAssert: return _not_contains_keys(expected, GdObjects.COMPARE_MODE.PARAMETER_DEEP_TEST) @@ -187,7 +188,7 @@ func contains_same_key_value(key :Variant, value :Variant) -> GdUnitDictionaryAs return _contains_key_value(key, value, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) -func not_contains_same_keys(expected :Array) -> GdUnitDictionaryAssert: +func not_contains_same_keys(...expected: Array) -> GdUnitDictionaryAssert: return _not_contains_keys(expected, GdObjects.COMPARE_MODE.OBJECT_REFERENCE) @@ -196,3 +197,11 @@ func _filter_by_key(element :Variant, values :Array, compare_mode :GdObjects.COM if GdObjects.equals(key, element, false, compare_mode): return is_not return !is_not + + +## Small helper to support the old expected arguments as single array and variadic arguments +func _extract_variadic_value(values: Variant) -> Variant: + @warning_ignore("unsafe_method_access") + if values != null and values.size() == 1 and GdArrayTools.is_array_type(values[0]): + return values[0] + return values diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid index dd8adbfa..0ef25102 100644 --- a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid @@ -1 +1 @@ -uid://bwrof6ds0dpxt +uid://ccrop3gr3j8lf diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd index 845d5fa9..198624c6 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd @@ -3,7 +3,10 @@ extends GdUnitFailureAssert const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") var _is_failed := false -var _failure_message :String +var _failure_message: String +var _current_failure_message := "" +var _custom_failure_message := "" +var _additional_failure_message := "" func _set_do_expect_fail(enabled :bool = true) -> void: @@ -44,12 +47,10 @@ func _on_test_failed(value :bool) -> void: _is_failed = value -@warning_ignore("unused_parameter") func is_equal(_expected: Variant) -> GdUnitFailureAssert: return _report_error("Not implemented") -@warning_ignore("unused_parameter") func is_not_equal(_expected: Variant) -> GdUnitFailureAssert: return _report_error("Not implemented") @@ -62,6 +63,16 @@ func is_not_null() -> GdUnitFailureAssert: return _report_error("Not implemented") +func override_failure_message(message: String) -> GdUnitFailureAssert: + _custom_failure_message = message + return self + + +func append_failure_message(message: String) -> GdUnitFailureAssert: + _additional_failure_message = message + return self + + func is_success() -> GdUnitFailureAssert: if _is_failed: return _report_error("Expect: assertion ends successfully.") @@ -115,7 +126,8 @@ func starts_with_message(expected :String) -> GdUnitFailureAssert: func _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert: var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number() - GdAssertReports.report_error(error_message, line_number) + _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message) + GdAssertReports.report_error(_current_failure_message, line_number) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid index c0ba7bb3..a1e3a974 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid @@ -1 +1 @@ -uid://4v5w8jahhm04 +uid://bg6q2wsyhg2na diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd index f98bc933..c4f9570e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd @@ -26,11 +26,13 @@ func current_value() -> String: func report_success() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -39,25 +41,37 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitFileAssert: +func override_failure_message(message: String) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitFileAssert: +func append_failure_message(message: String) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self -func is_equal(expected :Variant) -> GdUnitFileAssert: +func is_null() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") + _base.is_null() + return self + + +func is_not_null() -> GdUnitFileAssert: + @warning_ignore("return_value_discarded") + _base.is_not_null() + return self + + +func is_equal(expected: Variant) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitFileAssert: +func is_not_equal(expected: Variant) -> GdUnitFileAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self @@ -97,5 +111,6 @@ func contains_exactly(expected_rows: Array) -> GdUnitFileAssert: if script is GDScript: var source_code := GdScriptParser.to_unix_format(script.source_code) var rows := Array(source_code.split("\n")) + @warning_ignore("return_value_discarded") GdUnitArrayAssertImpl.new(rows).contains_exactly(expected_rows) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid index feea0235..6779f3c1 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid @@ -1 +1 @@ -uid://cmm7sbdnq6ynv +uid://yc3mcvjsiy23 diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd index 05d05b84..83d7e05e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitFloatAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitFloatAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,13 +39,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitFloatAssert: +func override_failure_message(message: String) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitFloatAssert: +func append_failure_message(message: String) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,13 +63,13 @@ func is_not_null() -> GdUnitFloatAssert: return self -func is_equal(expected :Variant) -> GdUnitFloatAssert: +func is_equal(expected: Variant) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitFloatAssert: +func is_not_equal(expected: Variant) -> GdUnitFloatAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid index a9047db4..54802448 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid @@ -1 +1 @@ -uid://ce0tctonwex1f +uid://c1negcrtqrppl diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd index 270119a6..c38acf08 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd @@ -59,12 +59,12 @@ func failure_message() -> String: return _current_failure_message -func override_failure_message(message :String) -> GdUnitFuncAssert: +func override_failure_message(message: String) -> GdUnitFuncAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitFuncAssert: +func append_failure_message(message: String) -> GdUnitFuncAssert: _additional_failure_message = message return self @@ -98,12 +98,12 @@ func is_true() -> GdUnitFuncAssert: return self -func is_equal(expected :Variant) -> GdUnitFuncAssert: +func is_equal(expected: Variant) -> GdUnitFuncAssert: await _validate_callback(cb_is_equal, expected) return self -func is_not_equal(expected :Variant) -> GdUnitFuncAssert: +func is_not_equal(expected: Variant) -> GdUnitFuncAssert: await _validate_callback(cb_is_not_equal, expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid index 05dc97c0..eea17fe9 100644 --- a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid @@ -1 +1 @@ -uid://d0blnbdubapl2 +uid://ddbua8fypypd diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd index adf2a2eb..ce5503de 100644 --- a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd @@ -1,13 +1,13 @@ extends GdUnitGodotErrorAssert -var _current_error_message :String -var _callable :Callable +var _current_failure_message := "" +var _custom_failure_message := "" +var _additional_failure_message := "" +var _callable: Callable +var _logger := GodotGdErrorMonitor.GdUnitLogger.new(true, true) -func _init(callable :Callable) -> void: - # we only support Godot 4.1.x+ because of await issue https://github.com/godotengine/godot/issues/80292 - assert(Engine.get_version_info().hex >= 0x40100, - "This assertion is not supported for Godot 4.0.x. Please upgrade to the minimum version Godot 4.1.0!") +func _init(callable: Callable) -> void: # save the actual assert instance on the current thread context GdUnitThreadManager.get_current_context().set_assert(self) GdAssertReports.reset_last_error_line_number() @@ -15,21 +15,12 @@ func _init(callable :Callable) -> void: func _execute() -> Array[ErrorLogEntry]: - # execute the given code and monitor for runtime errors - if _callable == null or not _callable.is_valid(): - @warning_ignore("return_value_discarded") - _report_error("Invalid Callable '%s'" % _callable) - else: - await _callable.call() - return await _error_monitor().scan(true) - - -func _error_monitor() -> GodotGdErrorMonitor: - return GdUnitThreadManager.get_current_context().get_execution_context().error_monitor + await _callable.call() + return _logger.entries() func failure_message() -> String: - return _current_error_message + return _current_failure_message func _report_success() -> GdUnitAssert: @@ -37,34 +28,61 @@ func _report_success() -> GdUnitAssert: return self -func _report_error(error_message :String, failure_line_number: int = -1) -> GdUnitAssert: +func _report_error(error_message: String, failure_line_number: int = -1) -> GdUnitAssert: var line_number := failure_line_number if failure_line_number != -1 else GdUnitAssertions.get_line_number() - _current_error_message = error_message - GdAssertReports.report_error(error_message, line_number) + _current_failure_message = GdAssertMessages.build_failure_message(error_message, _additional_failure_message, _custom_failure_message) + GdAssertReports.report_error(_current_failure_message, line_number) return self -func _has_log_entry(log_entries :Array[ErrorLogEntry], type :ErrorLogEntry.TYPE, error :String) -> bool: +func _has_log_entry(log_entries: Array[ErrorLogEntry], type: ErrorLogEntry.TYPE, error: Variant) -> bool: for entry in log_entries: - if entry._type == type and entry._message == error: - # Erase the log entry we already handled it by this assertion, otherwise it will report at twice - _error_monitor().erase_log_entry(entry) + if entry._type == type and GdObjects.equals(entry._message, error): + GdUnitThreadManager.get_current_context().get_execution_context().error_monitor.erase_log_entry(entry) return true return false -func _to_list(log_entries :Array[ErrorLogEntry]) -> String: +func _to_list(log_entries: Array[ErrorLogEntry]) -> String: if log_entries.is_empty(): return "no errors" - if log_entries.size() == 1: - return log_entries[0]._message - var value := "" + + var values := [] for entry in log_entries: - value += "'%s'\n" % entry._message - return value + values.append(entry) + return "\n".join(values) + + +func is_null() -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_not_null() -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_equal(_expected: Variant) -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func is_not_equal(_expected: Variant) -> GdUnitGodotErrorAssert: + return _report_error("Not implemented") + + +func override_failure_message(message: String) -> GdUnitGodotErrorAssert: + _custom_failure_message = message + return self + + +func append_failure_message(message: String) -> GdUnitGodotErrorAssert: + _additional_failure_message = message + return self func is_success() -> GdUnitGodotErrorAssert: + if not _validate_callable(): + return self + var log_entries := await _execute() if log_entries.is_empty(): return _report_success() @@ -74,34 +92,60 @@ func is_success() -> GdUnitGodotErrorAssert: """.dedent().trim_prefix("\n") % _to_list(log_entries)) -func is_runtime_error(expected_error :String) -> GdUnitGodotErrorAssert: +func is_runtime_error(expected_error: Variant) -> GdUnitGodotErrorAssert: + if not _validate_callable(): + return self + + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.SCRIPT_ERROR, expected_error): return _report_success() return _report_error(""" Expecting: a runtime error is triggered. - message: '%s' - found: %s + expected: '%s' + current: '%s' """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)]) -func is_push_warning(expected_warning :String) -> GdUnitGodotErrorAssert: +func is_push_warning(expected_warning: Variant) -> GdUnitGodotErrorAssert: + if not _validate_callable(): + return self + + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_warning) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_WARNING, expected_warning): return _report_success() return _report_error(""" Expecting: push_warning() is called. - message: '%s' - found: %s + expected: '%s' + current: '%s' """.dedent().trim_prefix("\n") % [expected_warning, _to_list(log_entries)]) -func is_push_error(expected_error :String) -> GdUnitGodotErrorAssert: +func is_push_error(expected_error: Variant) -> GdUnitGodotErrorAssert: + if not _validate_callable(): + return self + + var result := GdUnitArgumentMatchers.is_variant_string_matching(expected_error) + if result.is_error(): + return _report_error(result.error_message()) var log_entries := await _execute() if _has_log_entry(log_entries, ErrorLogEntry.TYPE.PUSH_ERROR, expected_error): return _report_success() return _report_error(""" Expecting: push_error() is called. - message: '%s' - found: %s + expected: '%s' + current: '%s' """.dedent().trim_prefix("\n") % [expected_error, _to_list(log_entries)]) + + +func _validate_callable() -> bool: + if _callable == null or not _callable.is_valid(): + @warning_ignore("return_value_discarded") + _report_error("Invalid Callable '%s'" % _callable) + return false + return true diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid index 9481101a..a32e1355 100644 --- a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid @@ -1 +1 @@ -uid://csgwjvp1srw01 +uid://bh0ttovqfndn1 diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd index 1527cac5..bdee249e 100644 --- a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd @@ -24,11 +24,13 @@ func current_value() -> Variant: func report_success() -> GdUnitIntAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitIntAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -37,13 +39,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitIntAssert: +func override_failure_message(message: String) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitIntAssert: +func append_failure_message(message: String) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,13 +63,13 @@ func is_not_null() -> GdUnitIntAssert: return self -func is_equal(expected :Variant) -> GdUnitIntAssert: +func is_equal(expected: Variant) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.is_equal(expected) return self -func is_not_equal(expected :Variant) -> GdUnitIntAssert: +func is_not_equal(expected: Variant) -> GdUnitIntAssert: @warning_ignore("return_value_discarded") _base.is_not_equal(expected) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid index ea042aa2..175bd083 100644 --- a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid @@ -1 +1 @@ -uid://b4wyced4y202u +uid://ba3pehqjdm3nt diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd index e6c5289e..955e1ef3 100644 --- a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd @@ -28,11 +28,13 @@ func current_value() -> Variant: func report_success() -> GdUnitObjectAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error: String) -> GdUnitObjectAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid index 2fc264a6..0b12c63d 100644 --- a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid @@ -1 +1 @@ -uid://boiiakwr5lbqu +uid://dqeqbhm6e00ya diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd index 8b6c7f26..98a6768f 100644 --- a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd @@ -28,11 +28,13 @@ func current_value() -> GdUnitResult: func report_success() -> GdUnitResultAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitResultAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -41,13 +43,13 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitResultAssert: +func override_failure_message(message: String) -> GdUnitResultAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitResultAssert: +func append_failure_message(message: String) -> GdUnitResultAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -65,6 +67,18 @@ func is_not_null() -> GdUnitResultAssert: return self +func is_equal(expected: Variant) -> GdUnitResultAssert: + return is_value(expected) + + +func is_not_equal(expected: Variant) -> GdUnitResultAssert: + var result := current_value() + var value :Variant = null if result == null else result.value() + if GdObjects.equals(value, expected): + return report_error(GdAssertMessages.error_not_equal(value, expected)) + return report_success() + + func is_empty() -> GdUnitResultAssert: var result := current_value() if result == null or not result.is_empty(): @@ -106,13 +120,9 @@ func contains_message(expected :String) -> GdUnitResultAssert: return report_success() -func is_value(expected :Variant) -> GdUnitResultAssert: +func is_value(expected: Variant) -> GdUnitResultAssert: var result := current_value() var value :Variant = null if result == null else result.value() if not GdObjects.equals(value, expected): return report_error(GdAssertMessages.error_result_is_value(value, expected)) return report_success() - - -func is_equal(expected :Variant) -> GdUnitResultAssert: - return is_value(expected) diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid index af6b7313..0eac7ba8 100644 --- a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid @@ -1 +1 @@ -uid://dbe4cfms6j7tf +uid://hee7t1conih8 diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd index 7d4f57e4..0da696a3 100644 --- a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd @@ -50,12 +50,12 @@ func failure_message() -> String: return _current_failure_message -func override_failure_message(message :String) -> GdUnitSignalAssert: +func override_failure_message(message: String) -> GdUnitSignalAssert: _custom_failure_message = message return self -func append_failure_message(message :String) -> GdUnitSignalAssert: +func append_failure_message(message: String) -> GdUnitSignalAssert: _additional_failure_message = message return self @@ -70,8 +70,33 @@ func wait_until(timeout := 2000) -> GdUnitSignalAssert: return self +func is_null() -> GdUnitSignalAssert: + if _emitter != null: + return report_error(GdAssertMessages.error_is_null(_emitter)) + return report_success() + + +func is_not_null() -> GdUnitSignalAssert: + if _emitter == null: + return report_error(GdAssertMessages.error_is_not_null()) + return report_success() + + +func is_equal(_expected: Variant) -> GdUnitSignalAssert: + return report_error("Not implemented") + + +func is_not_equal(_expected: Variant) -> GdUnitSignalAssert: + return report_error("Not implemented") + + # Verifies the signal exists checked the emitter -func is_signal_exists(signal_name :String) -> GdUnitSignalAssert: +func is_signal_exists(signal_or_name: Variant) -> GdUnitSignalAssert: + if not (signal_or_name is String or signal_or_name is Signal): + return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_or_name))) + + var signal_name := _to_signal_name(signal_or_name) + if not _emitter.has_signal(signal_name): @warning_ignore("return_value_discarded") report_error("The signal '%s' not exists checked object '%s'." % [signal_name, _emitter.get_class()]) @@ -79,20 +104,39 @@ func is_signal_exists(signal_name :String) -> GdUnitSignalAssert: # Verifies that given signal is emitted until waiting time -func is_emitted(name :String, args := []) -> GdUnitSignalAssert: +func is_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert: _line_number = GdUnitAssertions.get_line_number() - return await _wail_until_signal(name, args, false) + @warning_ignore("unsafe_call_argument") + return await _wail_until_signal( + signal_name, + _wrap_arguments.callv(signal_args), + false) # Verifies that given signal is NOT emitted until waiting time -func is_not_emitted(name :String, args := []) -> GdUnitSignalAssert: +func is_not_emitted(signal_name: Variant, ...signal_args: Array) -> GdUnitSignalAssert: _line_number = GdUnitAssertions.get_line_number() - return await _wail_until_signal(name, args, true) + @warning_ignore("unsafe_call_argument") + return await _wail_until_signal( + signal_name, + _wrap_arguments.callv(signal_args), + true) + +func _wrap_arguments(...args: Array) -> Array: + # Check using old syntax + if not args.is_empty() and args[0] is Array: + return args[0] + return args -func _wail_until_signal(signal_name :String, expected_args :Array, expect_not_emitted: bool) -> GdUnitSignalAssert: + +func _wail_until_signal(signal_or_name: Variant, expected_args: Array, expect_not_emitted: bool) -> GdUnitSignalAssert: if _emitter == null: return report_error("Can't wait for signal checked a NULL object.") + if not (signal_or_name is String or signal_or_name is Signal): + return report_error("Invalid signal_name: expected String or Signal, but is '%s'" % type_string(typeof(signal_or_name))) + + var signal_name := _to_signal_name(signal_or_name) # first verify the signal is defined if not _emitter.has_signal(signal_name): return report_error("Can't wait for non-existion signal '%s' checked object '%s'." % [signal_name,_emitter.get_class()]) @@ -121,3 +165,8 @@ func _wail_until_signal(signal_name :String, expected_args :Array, expect_not_em if is_instance_valid(_emitter): _signal_collector.reset_received_signals(_emitter, signal_name, expected_args) return self + + +func _to_signal_name(signal_or_name: Variant) -> String: + @warning_ignore("unsafe_cast") + return (signal_or_name as Signal).get_name() if signal_or_name is Signal else signal_or_name diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid index 13d71b9c..ca02597b 100644 --- a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid @@ -1 +1 @@ -uid://dc0nxf3g1q6hg +uid://biei5yhyialey diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd index 0f15956c..cdbcdff8 100644 --- a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd @@ -28,22 +28,24 @@ func current_value() -> Variant: func report_success() -> GdUnitStringAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitStringAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self -func override_failure_message(message :String) -> GdUnitStringAssert: +func override_failure_message(message: String) -> GdUnitStringAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self -func append_failure_message(message :String) -> GdUnitStringAssert: +func append_failure_message(message: String) -> GdUnitStringAssert: @warning_ignore("return_value_discarded") _base.append_failure_message(message) return self @@ -61,30 +63,35 @@ func is_not_null() -> GdUnitStringAssert: return self -func is_equal(expected :Variant) -> GdUnitStringAssert: - var current :Variant = current_value() - if current == null: - return report_error(GdAssertMessages.error_equal(current, expected)) - if not GdObjects.equals(current, expected): - var diffs := GdDiffTool.string_diff(current, expected) - var formatted_current := GdAssertMessages.colored_array_div(diffs[1]) - return report_error(GdAssertMessages.error_equal(formatted_current, expected)) - return report_success() +func is_equal(expected: Variant) -> GdUnitStringAssert: + return _is_equal(expected, false, GdAssertMessages.error_equal) -func is_equal_ignoring_case(expected :Variant) -> GdUnitStringAssert: - var current :Variant = current_value() +func is_equal_ignoring_case(expected: Variant) -> GdUnitStringAssert: + return _is_equal(expected, true, GdAssertMessages.error_equal_ignoring_case) + + +@warning_ignore_start("unsafe_call_argument") +func _is_equal(expected: Variant, ignore_case: bool, message_cb: Callable) -> GdUnitStringAssert: + var current: Variant = current_value() if current == null: - return report_error(GdAssertMessages.error_equal_ignoring_case(current, expected)) - if not GdObjects.equals(str(current), expected, true): - var diffs := GdDiffTool.string_diff(current, expected) + return report_error(message_cb.call(current, expected)) + var cur_value := str(current) + if not GdObjects.equals(cur_value, expected, ignore_case): + var exp_value := str(expected) + if contains_bbcode(cur_value): + # mask user bbcode + # https://docs.godotengine.org/en/4.5/tutorials/ui/bbcode_in_richtextlabel.html#handling-user-input-safely + return report_error(message_cb.call(cur_value.replace("[", "[lb]"), exp_value.replace("[", "[lb]"))) + var diffs := GdDiffTool.string_diff(cur_value, exp_value) var formatted_current := GdAssertMessages.colored_array_div(diffs[1]) - return report_error(GdAssertMessages.error_equal_ignoring_case(formatted_current, expected)) + return report_error(message_cb.call(formatted_current, exp_value)) return report_success() +@warning_ignore_restore("unsafe_call_argument") -func is_not_equal(expected :Variant) -> GdUnitStringAssert: - var current :Variant = current_value() +func is_not_equal(expected: Variant) -> GdUnitStringAssert: + var current: Variant = current_value() if GdObjects.equals(current, expected): return report_error(GdAssertMessages.error_not_equal(current, expected)) return report_success() @@ -190,3 +197,12 @@ func has_length(expected :int, comparator := Comparator.EQUAL) -> GdUnitStringAs _: return report_error("Comparator '%d' not implemented!" % comparator) return report_success() + + +func contains_bbcode(value: String) -> bool: + var rtl := RichTextLabel.new() + rtl.bbcode_enabled = true + rtl.parse_bbcode(value) + var has_bbcode := rtl.get_parsed_text() != value + rtl.free() + return has_bbcode diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid index 454ba1dd..bf5e08ac 100644 --- a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid @@ -1 +1 @@ -uid://cgc45q2bbnw6h +uid://6uk5icsgntyp diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd index 7b10d6bb..fbc031a4 100644 --- a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd +++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd @@ -50,11 +50,13 @@ func current_value() -> Variant: func report_success() -> GdUnitVectorAssert: + @warning_ignore("return_value_discarded") _base.report_success() return self func report_error(error :String) -> GdUnitVectorAssert: + @warning_ignore("return_value_discarded") _base.report_error(error) return self @@ -63,7 +65,7 @@ func failure_message() -> String: return _base.failure_message() -func override_failure_message(message :String) -> GdUnitVectorAssert: +func override_failure_message(message: String) -> GdUnitVectorAssert: @warning_ignore("return_value_discarded") _base.override_failure_message(message) return self diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid index 8d5800d4..e907dbc6 100644 --- a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid +++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid @@ -1 +1 @@ -uid://c8prlu08lq4pw +uid://bnolng1csslla diff --git a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid index 44144cf3..b97e5d6a 100644 --- a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid +++ b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid @@ -1 +1 @@ -uid://dmrnx0ievdw00 +uid://ddvk6ymcn88oq diff --git a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid index d711c4d4..1f4fb255 100644 --- a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid @@ -1 +1 @@ -uid://dytw8hhfjgrfo +uid://vt0eg4kbfs0p diff --git a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid index 401b3521..f4c3da77 100644 --- a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid @@ -1 +1 @@ -uid://c3bmxrdyb2s7c +uid://pa0fdl1oticf diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid index e394020c..a73929ad 100644 --- a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid @@ -1 +1 @@ -uid://bv18vku5wy8dh +uid://bvd0lba0q05bl diff --git a/addons/gdUnit4/src/cmd/CmdOption.gd.uid b/addons/gdUnit4/src/cmd/CmdOption.gd.uid index a6e08ca7..a3bb770e 100644 --- a/addons/gdUnit4/src/cmd/CmdOption.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdOption.gd.uid @@ -1 +1 @@ -uid://docx18wjoc50t +uid://c3bucsdgwko02 diff --git a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid index 5efaea1b..33171e4e 100644 --- a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid +++ b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid @@ -1 +1 @@ -uid://d1rr3hrhxwpcx +uid://dok71cphw24et diff --git a/addons/gdUnit4/src/core/GdArrayTools.gd b/addons/gdUnit4/src/core/GdArrayTools.gd index 39b35b76..74f0e175 100644 --- a/addons/gdUnit4/src/core/GdArrayTools.gd +++ b/addons/gdUnit4/src/core/GdArrayTools.gd @@ -14,11 +14,12 @@ const ARRAY_TYPES := [ TYPE_PACKED_STRING_ARRAY, TYPE_PACKED_VECTOR2_ARRAY, TYPE_PACKED_VECTOR3_ARRAY, + TYPE_PACKED_VECTOR4_ARRAY, TYPE_PACKED_COLOR_ARRAY ] -static func is_array_type(value :Variant) -> bool: +static func is_array_type(value: Variant) -> bool: return is_type_array(typeof(value)) diff --git a/addons/gdUnit4/src/core/GdArrayTools.gd.uid b/addons/gdUnit4/src/core/GdArrayTools.gd.uid index d31fe723..337a89ce 100644 --- a/addons/gdUnit4/src/core/GdArrayTools.gd.uid +++ b/addons/gdUnit4/src/core/GdArrayTools.gd.uid @@ -1 +1 @@ -uid://dysbpliok4jy3 +uid://bmwyd818tcjob diff --git a/addons/gdUnit4/src/core/GdDiffTool.gd b/addons/gdUnit4/src/core/GdDiffTool.gd index 16e567b0..5131df71 100644 --- a/addons/gdUnit4/src/core/GdDiffTool.gd +++ b/addons/gdUnit4/src/core/GdDiffTool.gd @@ -1,4 +1,5 @@ -# A tool to find differences between two objects +# Myers' Diff Algorithm implementation +# Based on "An O(ND) Difference Algorithm and Its Variations" by Eugene W. Myers class_name GdDiffTool extends RefCounted @@ -7,79 +8,144 @@ const DIV_ADD :int = 214 const DIV_SUB :int = 215 -static func _diff(lb: PackedByteArray, rb: PackedByteArray, lookup: Array[Array], ldiff: Array, rdiff: Array) -> void: - var loffset := lb.size() - var roffset := rb.size() - - while true: - #if last character of X and Y matches - if loffset > 0 && roffset > 0 && lb[loffset - 1] == rb[roffset - 1]: - loffset -= 1 - roffset -= 1 - ldiff.push_front(lb[loffset]) - rdiff.push_front(rb[roffset]) - continue - #current character of Y is not present in X - else: if (roffset > 0 && (loffset == 0 || lookup[loffset][roffset - 1] >= lookup[loffset - 1][roffset])): - roffset -= 1 - ldiff.push_front(rb[roffset]) - ldiff.push_front(DIV_ADD) - rdiff.push_front(rb[roffset]) - rdiff.push_front(DIV_SUB) - continue - #current character of X is not present in Y - else: if (loffset > 0 && (roffset == 0 || lookup[loffset][roffset - 1] < lookup[loffset - 1][roffset])): - loffset -= 1 - ldiff.push_front(lb[loffset]) - ldiff.push_front(DIV_SUB) - rdiff.push_front(lb[loffset]) - rdiff.push_front(DIV_ADD) - continue - break - - -# lookup[i][j] stores the length of LCS of substring X[0..i-1], Y[0..j-1] -static func _createLookUp(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]: - var lookup: Array[Array] = [] - @warning_ignore("return_value_discarded") - lookup.resize(lb.size() + 1) - for i in lookup.size(): - var x := [] - @warning_ignore("return_value_discarded") - x.resize(rb.size() + 1) - lookup[i] = x - return lookup - - -static func _buildLookup(lb: PackedByteArray, rb: PackedByteArray) -> Array[Array]: - var lookup := _createLookUp(lb, rb) - # first column of the lookup table will be all 0 - for i in lookup.size(): - lookup[i][0] = 0 - # first row of the lookup table will be all 0 - for j :int in lookup[0].size(): - lookup[0][j] = 0 - - # fill the lookup table in bottom-up manner - for i in range(1, lookup.size()): - for j in range(1, lookup[0].size()): - # if current character of left and right matches - if lb[i - 1] == rb[j - 1]: - lookup[i][j] = lookup[i - 1][j - 1] + 1; - # else if current character of left and right don't match +class Edit: + enum Type { EQUAL, INSERT, DELETE } + var type: Type + var character: int + + func _init(t: Type, chr: int) -> void: + type = t + character = chr + + +# Main entry point - returns [ldiff, rdiff] +static func string_diff(left: Variant, right: Variant) -> Array[PackedInt32Array]: + var lb := PackedInt32Array() if left == null else str(left).to_utf32_buffer().to_int32_array() + var rb := PackedInt32Array() if right == null else str(right).to_utf32_buffer().to_int32_array() + + # Early exit for identical strings + if lb == rb: + return [lb.duplicate(), rb.duplicate()] + + var edits := _myers_diff(lb, rb) + return _edits_to_diff_format(edits) + + +# Core Myers' algorithm +static func _myers_diff(a: PackedInt32Array, b: PackedInt32Array) -> Array[Edit]: + var n := a.size() + var m := b.size() + var max_d := n + m + + # V array stores the furthest reaching x coordinate for each k-line + # We need indices from -max_d to max_d, so we offset by max_d + var v := PackedInt32Array() + v.resize(2 * max_d + 1) + v.fill(-1) + v[max_d + 1] = 0 # k=1 starts at x=0 + + var trace := [] # Store V arrays for each d to backtrack later + + # Find the edit distance + for d in range(0, max_d + 1): + # Store current V for backtracking + trace.append(v.duplicate()) + + for k in range(-d, d + 1, 2): + var k_offset := k + max_d + + # Decide whether to move down or right + var x: int + if k == -d or (k != d and v[k_offset - 1] < v[k_offset + 1]): + x = v[k_offset + 1] # Move down (insert from b) + else: + x = v[k_offset - 1] + 1 # Move right (delete from a) + + var y := x - k + + # Follow diagonal as far as possible (matching characters) + while x < n and y < m and a[x] == b[y]: + x += 1 + y += 1 + + v[k_offset] = x + + # Check if we've reached the end + if x >= n and y >= m: + return _backtrack(a, b, trace, d, max_d) + + # Should never reach here for valid inputs + return [] + + +# Backtrack through the edit graph to build the edit script +static func _backtrack(a: PackedInt32Array, b: PackedInt32Array, trace: Array, d: int, max_d: int) -> Array[Edit]: + var edits: Array[Edit] = [] + var x := a.size() + var y := b.size() + + # Walk backwards through each d value + for depth in range(d, -1, -1): + var v: PackedInt32Array = trace[depth] + var k := x - y + var k_offset := k + max_d + + # Determine previous k + var prev_k: int + if k == -depth or (k != depth and v[k_offset - 1] < v[k_offset + 1]): + prev_k = k + 1 + else: + prev_k = k - 1 + + var prev_k_offset := prev_k + max_d + var prev_x := v[prev_k_offset] + var prev_y := prev_x - prev_k + + # Extract diagonal (equal) characters + while x > prev_x and y > prev_y: + x -= 1 + y -= 1 + #var char_array := PackedInt32Array([a[x]]) + edits.insert(0, Edit.new(Edit.Type.EQUAL, a[x])) + + # Record the edit operation + if depth > 0: + if x == prev_x: + # Insert from b + y -= 1 + #var char_array := PackedInt32Array([b[y]]) + edits.insert(0, Edit.new(Edit.Type.INSERT, b[y])) else: - lookup[i][j] = max(lookup[i - 1][j], lookup[i][j - 1]); - return lookup - - -static func string_diff(left :Variant, right :Variant) -> Array[PackedByteArray]: - var lb := PackedByteArray() if left == null else str(left).to_utf8_buffer() - var rb := PackedByteArray() if right == null else str(right).to_utf8_buffer() - var ldiff := Array() - var rdiff := Array() - var lookup := _buildLookup(lb, rb); - _diff(lb, rb, lookup, ldiff, rdiff) - return [PackedByteArray(ldiff), PackedByteArray(rdiff)] + # Delete from a + x -= 1 + #var char_array := PackedInt32Array([a[x]]) + edits.insert(0, Edit.new(Edit.Type.DELETE, a[x])) + + return edits + + +# Convert edit script to the DIV_ADD/DIV_SUB format +static func _edits_to_diff_format(edits: Array[Edit]) -> Array[PackedInt32Array]: + var ldiff := PackedInt32Array() + var rdiff := PackedInt32Array() + + for edit in edits: + match edit.type: + Edit.Type.EQUAL: + ldiff.append(edit.character) + rdiff.append(edit.character) + Edit.Type.INSERT: + ldiff.append(DIV_ADD) + ldiff.append(edit.character) + rdiff.append(DIV_SUB) + rdiff.append(edit.character) + Edit.Type.DELETE: + ldiff.append(DIV_SUB) + ldiff.append(edit.character) + rdiff.append(DIV_ADD) + rdiff.append(edit.character) + + return [ldiff, rdiff] # prototype diff --git a/addons/gdUnit4/src/core/GdDiffTool.gd.uid b/addons/gdUnit4/src/core/GdDiffTool.gd.uid index 39495dfb..63eae2b6 100644 --- a/addons/gdUnit4/src/core/GdDiffTool.gd.uid +++ b/addons/gdUnit4/src/core/GdDiffTool.gd.uid @@ -1 +1 @@ -uid://b15cajmjouv4w +uid://butt5b8m3aiv diff --git a/addons/gdUnit4/src/core/GdObjects.gd b/addons/gdUnit4/src/core/GdObjects.gd index 358c21a8..279e5188 100644 --- a/addons/gdUnit4/src/core/GdObjects.gd +++ b/addons/gdUnit4/src/core/GdObjects.gd @@ -6,8 +6,6 @@ const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") # introduced with Godot 4.3.beta1 -const TYPE_PACKED_VECTOR4_ARRAY = 38 #TYPE_PACKED_VECTOR4_ARRAY - const TYPE_VOID = 1000 const TYPE_VARARG = 1001 const TYPE_VARIANT = 1002 @@ -20,10 +18,6 @@ const TYPE_CANVAS = 2003 const TYPE_ENUM = 2004 -# used as default value for varargs -const TYPE_VARARG_PLACEHOLDER_VALUE = "__null__" - - const TYPE_AS_STRING_MAPPINGS := { TYPE_NIL: "null", TYPE_BOOL: "bool", @@ -72,11 +66,18 @@ const TYPE_AS_STRING_MAPPINGS := { } +class EditorNotifications: + # NOTE: Hardcoding to avoid runtime errors in exported projects when editor + # classes are not available. These values are unlikely to change. + # See: EditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED + const NOTIFICATION_EDITOR_SETTINGS_CHANGED := 10000 + + const NOTIFICATION_AS_STRING_MAPPINGS := { TYPE_OBJECT: { Object.NOTIFICATION_POSTINITIALIZE : "POSTINITIALIZE", Object.NOTIFICATION_PREDELETE: "PREDELETE", - EditorSettings.NOTIFICATION_EDITOR_SETTINGS_CHANGED: "EDITOR_SETTINGS_CHANGED", + EditorNotifications.NOTIFICATION_EDITOR_SETTINGS_CHANGED: "EDITOR_SETTINGS_CHANGED", }, TYPE_NODE: { Node.NOTIFICATION_ENTER_TREE : "ENTER_TREE", diff --git a/addons/gdUnit4/src/core/GdObjects.gd.uid b/addons/gdUnit4/src/core/GdObjects.gd.uid index fb2475d9..e0413ceb 100644 --- a/addons/gdUnit4/src/core/GdObjects.gd.uid +++ b/addons/gdUnit4/src/core/GdObjects.gd.uid @@ -1 +1 @@ -uid://sdoq2leo0wh7 +uid://c8qw0jfxm3kll diff --git a/addons/gdUnit4/src/core/GdUnit4Version.gd b/addons/gdUnit4/src/core/GdUnit4Version.gd index 777eb92c..3e6a334e 100644 --- a/addons/gdUnit4/src/core/GdUnit4Version.gd +++ b/addons/gdUnit4/src/core/GdUnit4Version.gd @@ -59,3 +59,7 @@ static func init_version_label(label :Control) -> void: func _to_string() -> String: return "v%d.%d.%d" % [_major, _minor, _patch] + + +func documentation_version() -> String: + return "v%d.%d.x" % [_major, _minor] diff --git a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid index 597caf56..78474ad3 100644 --- a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid +++ b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid @@ -1 +1 @@ -uid://db2lxsj6mco0s +uid://btllif2qcnjol diff --git a/addons/gdUnit4/src/core/GdUnitFileAccess.gd b/addons/gdUnit4/src/core/GdUnitFileAccess.gd index cf8663c0..f4ba0035 100644 --- a/addons/gdUnit4/src/core/GdUnitFileAccess.gd +++ b/addons/gdUnit4/src/core/GdUnitFileAccess.gd @@ -92,6 +92,7 @@ static func copy_directory(from_dir :String, to_dir :String, recursive :bool = f static func delete_directory(path :String, only_content := false) -> void: var dir := DirAccess.open(path) if dir != null: + dir.include_hidden = true @warning_ignore("return_value_discarded") dir.list_dir_begin() var file_name := "." @@ -154,11 +155,18 @@ static func find_last_path_index(path :String, prefix :String) -> int: return last_iteration +static func as_resource_path(value: String) -> String: + if value.begins_with("res://"): + return value + return "res://" + value.trim_prefix("//").trim_prefix("/").trim_suffix("/") + + static func scan_dir(path :String) -> PackedStringArray: var dir := DirAccess.open(path) if dir == null or not dir.dir_exists(path): return PackedStringArray() var content := PackedStringArray() + dir.include_hidden = true @warning_ignore("return_value_discarded") dir.list_dir_begin() var next := "." @@ -188,7 +196,7 @@ static func resource_as_string(resource_path :String) -> String: if file == null: push_error("ERROR: Can't read resource '%s'. %s" % [resource_path, error_string(FileAccess.get_open_error())]) return "" - return file.get_as_text(true) + return file.get_as_text() static func make_qualified_path(path :String) -> String: diff --git a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid index 678e5c13..4728499e 100644 --- a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid @@ -1 +1 @@ -uid://c3frtlg7eexve +uid://dw715oklxhdc6 diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd b/addons/gdUnit4/src/core/GdUnitProperty.gd index 138dd9f7..3d050b19 100644 --- a/addons/gdUnit4/src/core/GdUnitProperty.gd +++ b/addons/gdUnit4/src/core/GdUnitProperty.gd @@ -31,6 +31,9 @@ func value() -> Variant: return _value +func int_value() -> int: + return _value + func value_as_string() -> String: return _value @@ -43,16 +46,18 @@ func is_selectable_value() -> bool: return not _value_set.is_empty() -func set_value(p_value :Variant) -> void: +func set_value(p_value: Variant) -> void: match _type: TYPE_STRING: _value = str(p_value) TYPE_BOOL: - _value = convert(p_value, TYPE_BOOL) + _value = type_convert(p_value, TYPE_BOOL) TYPE_INT: - _value = convert(p_value, TYPE_INT) + _value = type_convert(p_value, TYPE_INT) TYPE_FLOAT: - _value = convert(p_value, TYPE_FLOAT) + _value = type_convert(p_value, TYPE_FLOAT) + TYPE_DICTIONARY: + _value = type_convert(p_value, TYPE_DICTIONARY) _: _value = p_value diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid index a1ea4b8c..632653c3 100644 --- a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid @@ -1 +1 @@ -uid://bvcue10yckc55 +uid://duk1c0hfm65p3 diff --git a/addons/gdUnit4/src/core/GdUnitResult.gd b/addons/gdUnit4/src/core/GdUnitResult.gd index c7187d48..42392a5e 100644 --- a/addons/gdUnit4/src/core/GdUnitResult.gd +++ b/addons/gdUnit4/src/core/GdUnitResult.gd @@ -20,7 +20,7 @@ static func empty() -> GdUnitResult: return result -static func success(p_value :Variant) -> GdUnitResult: +static func success(p_value: Variant = "") -> GdUnitResult: assert(p_value != null, "The value must not be NULL") var result := GdUnitResult.new() result._value = p_value @@ -28,7 +28,7 @@ static func success(p_value :Variant) -> GdUnitResult: return result -static func warn(p_warn_message :String, p_value :Variant = null) -> GdUnitResult: +static func warn(p_warn_message: String, p_value: Variant = null) -> GdUnitResult: assert(not p_warn_message.is_empty()) #,"The message must not be empty") var result := GdUnitResult.new() result._value = p_value @@ -37,7 +37,7 @@ static func warn(p_warn_message :String, p_value :Variant = null) -> GdUnitResul return result -static func error(p_error_message :String) -> GdUnitResult: +static func error(p_error_message: String) -> GdUnitResult: assert(not p_error_message.is_empty(), "The message must not be empty") var result := GdUnitResult.new() result._value = null @@ -70,7 +70,7 @@ func value_as_string() -> String: return _value -func or_else(p_value :Variant) -> Variant: +func or_else(p_value: Variant) -> Variant: if not is_success(): return p_value return value() @@ -88,7 +88,7 @@ func _to_string() -> String: return str(GdUnitResult.serialize(self)) -static func serialize(result :GdUnitResult) -> Dictionary: +static func serialize(result: GdUnitResult) -> Dictionary: if result == null: push_error("Can't serialize a Null object from type GdUnitResult") return { @@ -99,7 +99,7 @@ static func serialize(result :GdUnitResult) -> Dictionary: } -static func deserialize(config :Dictionary) -> GdUnitResult: +static func deserialize(config: Dictionary) -> GdUnitResult: var result := GdUnitResult.new() var cfg_value: String = config.get("value", "") result._value = str_to_var(cfg_value) diff --git a/addons/gdUnit4/src/core/GdUnitResult.gd.uid b/addons/gdUnit4/src/core/GdUnitResult.gd.uid index 985e9878..7e112096 100644 --- a/addons/gdUnit4/src/core/GdUnitResult.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitResult.gd.uid @@ -1 +1 @@ -uid://diw4j3mxoow71 +uid://41csxavwvd50 diff --git a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd index 149893f2..0d1a42d7 100644 --- a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd +++ b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd @@ -18,7 +18,10 @@ var _config := { TESTS : Array([], TYPE_OBJECT, "RefCounted", GdUnitTestCase), # the port of running test server for this session - SERVER_PORT : -1 + SERVER_PORT : -1, + + # Exit on first failure + EXIT_FAIL_FAST : false } @@ -40,6 +43,15 @@ func server_port() -> int: return _config.get(SERVER_PORT, -1) +func do_fail_fast(fail_fast: bool) -> GdUnitRunnerConfig: + _config[EXIT_FAIL_FAST] = fail_fast + return self + + +func is_fail_fast() -> bool: + return _config.get(EXIT_FAIL_FAST, false) + + func add_test_cases(tests: Array[GdUnitTestCase]) -> GdUnitRunnerConfig: test_cases().append_array(tests) return self @@ -57,7 +69,8 @@ func save_config(path: String = CONFIG_FILE) -> GdUnitResult: var to_save := { VERSION : CONFIG_VERSION, - SERVER_PORT : _config.get(SERVER_PORT), + EXIT_FAIL_FAST : is_fail_fast(), + SERVER_PORT : server_port(), TESTS : Array() } @@ -70,7 +83,7 @@ func save_config(path: String = CONFIG_FILE) -> GdUnitResult: func load_config(path: String = CONFIG_FILE) -> GdUnitResult: if not FileAccess.file_exists(path): - return GdUnitResult.error("Can't find test runner configuration '%s'! Please select a test to run." % path) + return GdUnitResult.warn("Can't find test runner configuration '%s'! Please select a test to run." % path) var file := FileAccess.open(path, FileAccess.READ) if file == null: var error := FileAccess.get_open_error() diff --git a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid index 3c0608d7..84e8dc1c 100644 --- a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid @@ -1 +1 @@ -uid://8t4ughvls7x4 +uid://cqab1ch266w2r diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd index c70be022..5c852525 100644 --- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd +++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd @@ -72,17 +72,17 @@ func _init(p_scene: Variant, p_verbose: bool, p_hide_push_errors := false) -> vo return _scene_tree().root.add_child(_current_scene) + Engine.set_meta("GdUnitSceneRunner", self) # do finally reset all open input events when the scene is removed @warning_ignore("return_value_discarded") - _scene_tree().root.child_exiting_tree.connect(func f(child :Node) -> void: + _scene_tree().root.child_exiting_tree.connect(func f(child: Node) -> void: if child == _current_scene: - # we need to disable the processing to avoid input flush buffer errors - _current_scene.process_mode = Node.PROCESS_MODE_DISABLED _reset_input_to_default() ) _simulate_start_time = LocalTime.now() # we need to set inital a valid window otherwise the warp_mouse() is not handled - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + move_window_to_foreground() + # set inital mouse pos to 0,0 var max_iteration_to_wait := 0 while get_global_mouse_position() != Vector2.ZERO and max_iteration_to_wait < 100: @@ -95,51 +95,57 @@ func _notification(what: int) -> void: # reset time factor to normal __deactivate_time_factor() if is_instance_valid(_current_scene): + move_window_to_background() _scene_tree().root.remove_child(_current_scene) # do only free scenes instanciated by this runner if _scene_auto_free: _current_scene.free() _is_disposed = true _current_scene = null + Engine.remove_meta("GdUnitSceneRunner") func _scene_tree() -> SceneTree: return Engine.get_main_loop() as SceneTree +func await_input_processed() -> void: + if scene() != null and scene().process_mode != Node.PROCESS_MODE_DISABLED: + Input.flush_buffered_events() + await (Engine.get_main_loop() as SceneTree).process_frame + await (Engine.get_main_loop() as SceneTree).physics_frame + + @warning_ignore("return_value_discarded") -func simulate_action_pressed(action: String) -> GdUnitSceneRunner: - simulate_action_press(action) - simulate_action_release(action) +func simulate_action_pressed(action: String, event_index := -1) -> GdUnitSceneRunner: + simulate_action_press(action, event_index) + simulate_action_release(action, event_index) return self -func simulate_action_press(action: String) -> GdUnitSceneRunner: +func simulate_action_press(action: String, event_index := -1) -> GdUnitSceneRunner: __print_current_focus() var event := InputEventAction.new() event.pressed = true event.action = action - if Engine.get_version_info().hex >= 0x40300: - @warning_ignore("unsafe_property_access") - event.event_index = InputMap.get_actions().find(action) + event.event_index = event_index _action_on_press.append(action) return _handle_input_event(event) -func simulate_action_release(action: String) -> GdUnitSceneRunner: +func simulate_action_release(action: String, event_index := -1) -> GdUnitSceneRunner: __print_current_focus() var event := InputEventAction.new() event.pressed = false event.action = action - if Engine.get_version_info().hex >= 0x40300: - @warning_ignore("unsafe_property_access") - event.event_index = InputMap.get_actions().find(action) + event.event_index = event_index _action_on_press.erase(action) return _handle_input_event(event) @warning_ignore("return_value_discarded") func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: + _push_warning_deprecated_arguments(shift_pressed, ctrl_pressed) simulate_key_press(key_code, shift_pressed, ctrl_pressed) await _scene_tree().process_frame simulate_key_release(key_code, shift_pressed, ctrl_pressed) @@ -147,37 +153,38 @@ func simulate_key_pressed(key_code: int, shift_pressed := false, ctrl_pressed := func simulate_key_press(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: + _push_warning_deprecated_arguments(shift_pressed, ctrl_pressed) __print_current_focus() var event := InputEventKey.new() event.pressed = true event.keycode = key_code as Key event.physical_keycode = key_code as Key - event.alt_pressed = key_code == KEY_ALT - event.shift_pressed = shift_pressed or key_code == KEY_SHIFT - event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL + event.unicode = key_code + event.set_alt_pressed(key_code == KEY_ALT) + event.set_shift_pressed(shift_pressed) + event.set_ctrl_pressed(ctrl_pressed) + event.get_modifiers_mask() _apply_input_modifiers(event) _key_on_press.append(key_code) return _handle_input_event(event) func simulate_key_release(key_code: int, shift_pressed := false, ctrl_pressed := false) -> GdUnitSceneRunner: + _push_warning_deprecated_arguments(shift_pressed, ctrl_pressed) __print_current_focus() var event := InputEventKey.new() event.pressed = false event.keycode = key_code as Key event.physical_keycode = key_code as Key - event.alt_pressed = key_code == KEY_ALT - event.shift_pressed = shift_pressed or key_code == KEY_SHIFT - event.ctrl_pressed = ctrl_pressed or key_code == KEY_CTRL + event.unicode = key_code + event.set_alt_pressed(key_code == KEY_ALT) + event.set_shift_pressed(shift_pressed) + event.set_ctrl_pressed(ctrl_pressed) _apply_input_modifiers(event) _key_on_press.erase(key_code) return _handle_input_event(event) -func set_mouse_pos(pos: Vector2) -> GdUnitSceneRunner: - return set_mouse_position(pos) - - func set_mouse_position(pos: Vector2) -> GdUnitSceneRunner: var event := InputEventMouseMotion.new() event.position = pos @@ -277,7 +284,7 @@ func simulate_screen_touch_pressed(index: int, position: Vector2, double_tap := func simulate_screen_touch_press(index: int, position: Vector2, double_tap := false) -> GdUnitSceneRunner: if is_emulate_mouse_from_touch(): # we need to simulate in addition to the touch the mouse events - set_mouse_pos(position) + set_mouse_position(position) simulate_mouse_button_press(MOUSE_BUTTON_LEFT) # push touch press event at position var event := InputEventScreenTouch.new() @@ -413,46 +420,21 @@ func simulate_frames(frames: int, delta_milli: int = -1) -> GdUnitSceneRunner: return self -func simulate_until_signal( - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func simulate_until_signal(signal_name: String, ...args: Array) -> GdUnitSceneRunner: await _awaiter.await_signal_idle_frames(scene(), signal_name, args, 10000) return self -func simulate_until_object_signal( - source: Object, - signal_name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> GdUnitSceneRunner: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func simulate_until_object_signal(source: Object, signal_name: String, ...args: Array) -> GdUnitSceneRunner: await _awaiter.await_signal_idle_frames(source, signal_name, args, 10000) return self -func await_func(func_name: String, args := []) -> GdUnitFuncAssert: +func await_func(func_name: String, ...args: Array) -> GdUnitFuncAssert: return GdUnitFuncAssertImpl.new(scene(), func_name, args) -func await_func_on(instance: Object, func_name: String, args := []) -> GdUnitFuncAssert: +func await_func_on(instance: Object, func_name: String, ...args: Array) -> GdUnitFuncAssert: return GdUnitFuncAssertImpl.new(instance, func_name, args) @@ -464,9 +446,17 @@ func await_signal_on(source: Object, signal_name: String, args := [], timeout := await _awaiter.await_signal_on(source, signal_name, args, timeout) -func maximize_view() -> GdUnitSceneRunner: - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) - DisplayServer.window_move_to_foreground() +func move_window_to_foreground() -> GdUnitSceneRunner: + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + DisplayServer.window_move_to_foreground() + return self + + +func move_window_to_background() -> GdUnitSceneRunner: + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) return self @@ -488,21 +478,9 @@ func set_property(name: String, value: Variant) -> bool: return true -func invoke( - name: String, - arg0: Variant = NO_ARG, - arg1: Variant = NO_ARG, - arg2: Variant = NO_ARG, - arg3: Variant = NO_ARG, - arg4: Variant = NO_ARG, - arg5: Variant = NO_ARG, - arg6: Variant = NO_ARG, - arg7: Variant = NO_ARG, - arg8: Variant = NO_ARG, - arg9: Variant = NO_ARG) -> Variant: - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG) +func invoke(name: String, ...args: Array) -> Variant: if scene().has_method(name): - return scene().callv(name, args) + return await scene().callv(name, args) return "The method '%s' not exist checked loaded scene." % name @@ -511,6 +489,8 @@ func find_child(name: String, recursive: bool = true, owned: bool = false) -> No func _scene_name() -> String: + if scene() == null: + return "unknown" var scene_script :GDScript = scene().get_script() var scene_name :String = scene().get_name() if not scene_script: @@ -541,6 +521,13 @@ func _apply_input_modifiers(event: InputEvent) -> void: _event.ctrl_pressed = _event.ctrl_pressed or last_input_event.ctrl_pressed # this line results into reset the control_pressed state!!! #event.command_or_control_autoremap = event.command_or_control_autoremap or _last_input_event.command_or_control_autoremap + if _last_input_event is InputEventKey and event is InputEventWithModifiers: + var last_input_event := _last_input_event as InputEventKey + var _event := event as InputEventWithModifiers + _event.shift_pressed = _event.shift_pressed or last_input_event.keycode == KEY_SHIFT + _event.alt_pressed = _event.alt_pressed or last_input_event.keycode == KEY_ALT + _event.ctrl_pressed = _event.ctrl_pressed or last_input_event.keycode == KEY_CTRL + _event.meta_pressed = _event.meta_pressed or last_input_event.keycode == KEY_META # copy over current active mouse mask and combine with curren mask @@ -569,7 +556,7 @@ func _handle_actions(event: InputEventAction) -> bool: return false __print(" process action %s (%s) <- %s" % [scene(), _scene_name(), event.as_text()]) if event.is_pressed(): - Input.action_press(event.action, InputMap.action_get_deadzone(event.action)) + Input.action_press(event.action, event.get_strength()) else: Input.action_release(event.action) return true @@ -646,3 +633,10 @@ func scene() -> Node: if not _is_disposed: push_error("The current scene instance is not valid anymore! check your test is valid. e.g. check for missing awaits.") return null + + +func _push_warning_deprecated_arguments(shift_pressed: bool, ctrl_pressed: bool) -> void: + if shift_pressed: + push_warning("Deprecated! Don't use 'shift_pressed' it will be removed in v7.0, checkout the documentaion how to use key combinations.") + if ctrl_pressed: + push_warning("Deprecated! Don't use 'ctrl_pressed' it will be removed in v7.0, checkout the documentaion how to use key combinations.") diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid index 66c5dd62..50b5dc21 100644 --- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid @@ -1 +1 @@ -uid://d28jctgpw3ia4 +uid://dgnjqmdsn6at1 diff --git a/addons/gdUnit4/src/core/GdUnitSettings.gd b/addons/gdUnit4/src/core/GdUnitSettings.gd index 2fc1cff2..c4eaa962 100644 --- a/addons/gdUnit4/src/core/GdUnitSettings.gd +++ b/addons/gdUnit4/src/core/GdUnitSettings.gd @@ -11,6 +11,9 @@ const GROUP_COMMON = COMMON_SETTINGS + "/common" const UPDATE_NOTIFICATION_ENABLED = GROUP_COMMON + "/update_notification_enabled" const SERVER_TIMEOUT = GROUP_COMMON + "/server_connection_timeout_minutes" +const GROUP_HOOKS = MAIN_CATEGORY + "/hooks" +const SESSION_HOOKS = GROUP_HOOKS + "/session_hooks" + const GROUP_TEST = COMMON_SETTINGS + "/test" const TEST_TIMEOUT = GROUP_TEST + "/test_timeout_seconds" const TEST_LOOKUP_FOLDER = GROUP_TEST + "/test_lookup_folder" @@ -18,6 +21,7 @@ const TEST_SUITE_NAMING_CONVENTION = GROUP_TEST + "/test_suite_naming_convention const TEST_DISCOVER_ENABLED = GROUP_TEST + "/test_discovery" const TEST_FLAKY_CHECK = GROUP_TEST + "/flaky_check_enable" const TEST_FLAKY_MAX_RETRIES = GROUP_TEST + "/flaky_max_retries" +const TEST_RERUN_UNTIL_FAILURE_RETRIES = GROUP_TEST + "/rerun_until_failure_retries" # Report Setiings @@ -59,6 +63,7 @@ const SHORTCUT_INSPECTOR_RERUN_TEST = GROUP_SHORTCUT_INSPECTOR + "/rerun_test" const SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG = GROUP_SHORTCUT_INSPECTOR + "/rerun_test_debug" const SHORTCUT_INSPECTOR_RUN_TEST_OVERALL = GROUP_SHORTCUT_INSPECTOR + "/run_test_overall" const SHORTCUT_INSPECTOR_RUN_TEST_STOP = GROUP_SHORTCUT_INSPECTOR + "/run_test_stop" +const SHORTCUT_INSPECTOR_RERUN_TEST_UNTIL_FAILURE = GROUP_SHORTCUT_INSPECTOR + "/rerun_test_until_failure" const GROUP_SHORTCUT_EDITOR = SHORTCUT_SETTINGS + "/editor" const SHORTCUT_EDITOR_RUN_TEST = GROUP_SHORTCUT_EDITOR + "/run_test" @@ -74,6 +79,10 @@ const SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG = GROUP_SHORTCUT_FILESYSTEM + "/run_tes const GROUP_UI_TOOLBAR = UI_SETTINGS + "/toolbar" const INSPECTOR_TOOLBAR_BUTTON_RUN_OVERALL = GROUP_UI_TOOLBAR + "/run_overall" +# Feature flags +const GROUP_FEATURE = MAIN_CATEGORY + "/feature" + + # defaults # server connection timeout in minutes const DEFAULT_SERVER_TIMEOUT :int = 30 @@ -105,6 +114,7 @@ static func setup() -> void: create_property_if_need(TEST_DISCOVER_ENABLED, false, "Automatically detect new tests in test lookup folders at runtime") create_property_if_need(TEST_FLAKY_CHECK, false, "Rerun tests on failure and mark them as FLAKY") create_property_if_need(TEST_FLAKY_MAX_RETRIES, 3, "Sets the number of retries for rerunning a flaky test") + create_property_if_need(TEST_RERUN_UNTIL_FAILURE_RETRIES, 10, "The number of reruns until the test fails.") # report settings create_property_if_need(REPORT_PUSH_ERRORS, false, "Report push_error() as failure") create_property_if_need(REPORT_SCRIPT_ERRORS, true, "Report script errors as failure") @@ -123,6 +133,7 @@ static func setup() -> void: "Show 'Run overall Tests' button in the inspector toolbar") create_property_if_need(TEMPLATE_TS_GD, GdUnitTestSuiteTemplate.default_GD_template(), "Test suite template to use") create_shortcut_properties_if_need() + create_property_if_need(SESSION_HOOKS, {} as Dictionary[String,bool]) migrate_properties() @@ -140,6 +151,7 @@ static func create_shortcut_properties_if_need() -> void: # inspector create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS), "Rerun the most recently executed tests") create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG), "Rerun the most recently executed tests (Debug mode)") + create_property_if_need(SHORTCUT_INSPECTOR_RERUN_TEST_UNTIL_FAILURE, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RERUN_TESTS_UNTIL_FAILURE), "Rerun tests until failure occurs") create_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_OVERALL, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL), "Runs all tests (Debug mode)") create_property_if_need(SHORTCUT_INSPECTOR_RUN_TEST_STOP, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.STOP_TEST_RUN), "Stop the current test execution") # script editor @@ -147,8 +159,8 @@ static func create_shortcut_properties_if_need() -> void: create_property_if_need(SHORTCUT_EDITOR_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG), "Run the currently selected test (Debug mode).") create_property_if_need(SHORTCUT_EDITOR_CREATE_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.CREATE_TEST), "Create a new test case for the currently selected function") # filesystem - create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), "Run all test suites in the selected folder or file") - create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.NONE), "Run all test suites in the selected folder or file (Debug)") + create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTSUITE), "Run all test suites in the selected folder or file") + create_property_if_need(SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG, GdUnitShortcut.default_keys(GdUnitShortcut.ShortCut.RUN_TESTSUITE_DEBUG), "Run all test suites in the selected folder or file (Debug)") static func create_property_if_need(name :String, default :Variant, help :="", value_set := PackedStringArray()) -> void: @@ -199,6 +211,20 @@ static func set_log_path(path :String) -> void: ProjectSettings.save() +static func get_session_hooks() -> Dictionary[String, bool]: + var property := get_property(SESSION_HOOKS) + if property == null: + return {} + var hooks: Dictionary[String, bool] = property.value() + return hooks + + +static func set_session_hooks(hooks: Dictionary[String, bool]) -> void: + var property := get_property(SESSION_HOOKS) + property.set_value(hooks) + update_property(property) + + static func set_inspector_tree_sort_mode(sort_mode: GdUnitInspectorTreeConstants.SORT_MODE) -> void: var property := get_property(INSPECTOR_TREE_SORT_MODE) property.set_value(sort_mode) @@ -276,10 +302,18 @@ static func is_test_flaky_check_enabled() -> bool: return get_setting(TEST_FLAKY_CHECK, false) +static func is_feature_enabled(feature: String) -> bool: + return get_setting(feature, false) + + static func get_flaky_max_retries() -> int: return get_setting(TEST_FLAKY_MAX_RETRIES, 3) +static func get_rerun_max_retries() -> int: + return get_setting(TEST_RERUN_UNTIL_FAILURE_RETRIES, 10) + + static func set_test_discover_enabled(enable :bool) -> void: var property := get_property(TEST_DISCOVER_ENABLED) property.set_value(enable) diff --git a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid index a937b3a4..5508fdbd 100644 --- a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid @@ -1 +1 @@ -uid://b41o5s7w6ruau +uid://cog3ks7lx2wwt diff --git a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid index 5411baae..54aeef92 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid @@ -1 +1 @@ -uid://vdafterb2ngh +uid://bn6o33755rrw0 diff --git a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd index 10bacc4a..0cb6ad71 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd +++ b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd @@ -23,11 +23,16 @@ func clear() -> void: # connect to all possible signals defined by the emitter # prepares the signal collection to store received signals and arguments -func register_emitter(emitter :Object) -> void: +func register_emitter(emitter: Object, force_recreate := false) -> void: if is_instance_valid(emitter): # check emitter is already registerd if _collected_signals.has(emitter): - return + if not force_recreate: + return + # If the flag recreate is set to true, emitters that are already registered must be deregistered before recreating, + # otherwise signals that have already been collected will be evaluated. + unregister_emitter(emitter) + _collected_signals[emitter] = Dictionary() # connect to 'tree_exiting' of the emitter to finally release all acquired resources/connections. if emitter is Node and !(emitter as Node).tree_exiting.is_connected(unregister_emitter): @@ -82,22 +87,27 @@ func _on_signal_emmited( (_collected_signals[emitter][signal_name] as Array).append(signal_args) -func reset_received_signals(emitter :Object, signal_name: String, signal_args :Array) -> void: +func reset_received_signals(emitter: Object, signal_name: String, signal_args: Array) -> void: #_debug_signal_list("before claer"); if _collected_signals.has(emitter): var signals_by_emitter :Dictionary = _collected_signals[emitter] if signals_by_emitter.has(signal_name): - @warning_ignore("unsafe_cast") - (_collected_signals[emitter][signal_name] as Array).erase(signal_args) + var received_args: Array = _collected_signals[emitter][signal_name] + # We iterate backwarts over to received_args to remove matching args. + # This will avoid array corruption see comment on `erase` otherwise we need a timeconsuming duplicate before + for arg_pos: int in range(received_args.size()-1, -1, -1): + var arg: Variant = received_args[arg_pos] + if GdObjects.equals(arg, signal_args): + received_args.remove_at(arg_pos) #_debug_signal_list("after claer"); -func is_signal_collecting(emitter :Object, signal_name :String) -> bool: +func is_signal_collecting(emitter: Object, signal_name: String) -> bool: @warning_ignore("unsafe_cast") return _collected_signals.has(emitter) and (_collected_signals[emitter] as Dictionary).has(signal_name) -func match(emitter :Object, signal_name :String, args :Array) -> bool: +func match(emitter: Object, signal_name: String, args: Array) -> bool: #prints("match", signal_name, _collected_signals[emitter][signal_name]); if _collected_signals.is_empty() or not _collected_signals.has(emitter): return false diff --git a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid index ca0929b6..84b3188b 100644 --- a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid @@ -1 +1 @@ -uid://cpi5b0k7xnw4j +uid://da8triu340yug diff --git a/addons/gdUnit4/src/core/GdUnitSignals.gd b/addons/gdUnit4/src/core/GdUnitSignals.gd index 53aafe99..f0a900dc 100644 --- a/addons/gdUnit4/src/core/GdUnitSignals.gd +++ b/addons/gdUnit4/src/core/GdUnitSignals.gd @@ -33,9 +33,9 @@ signal gdunit_client_connected(client_id: int) signal gdunit_client_disconnected(client_id: int) -## Emitted when a client terminates unexpectedly. +## Emitted when a the user stops (terminates) the current test session @warning_ignore("unused_signal") -signal gdunit_client_terminated() +signal gdunit_test_session_terminate() ## Emitted when a test execution event occurs.[br] diff --git a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid index 4a7fd80d..635537cb 100644 --- a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid @@ -1 +1 @@ -uid://c3gfd1at2bm81 +uid://dx1wmtgl3dq65 diff --git a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid index 07a94f76..6e920a4d 100644 --- a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid @@ -1 +1 @@ -uid://bc5fybsywnuya +uid://bhjswe4jjqka8 diff --git a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd index 43618525..33b80e28 100644 --- a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd +++ b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd @@ -40,7 +40,7 @@ static func load_test_suite_gd(resource_path: String) -> GdUnitTestSuite: static func load_test_suite_cs(resource_path: String) -> Node: - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return null var script :Script = ClassDB.instantiate("CSharpScript") script.source_code = GdUnitFileAccess.resource_as_string(resource_path) @@ -50,7 +50,7 @@ static func load_test_suite_cs(resource_path: String) -> Node: static func load_cs_script(resource_path: String, debug_write := false) -> Script: - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return null var script :Script = ClassDB.instantiate("CSharpScript") script.source_code = GdUnitFileAccess.resource_as_string(resource_path) diff --git a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid index 31912a1e..5998e737 100644 --- a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid @@ -1 +1 @@ -uid://dtinkm1otfo2y +uid://dwxgnyob6ouab diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid index a2b1341e..eea9068d 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid @@ -1 +1 @@ -uid://dm1kreoovutly +uid://cinixum4tw13k diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd index b5d27349..345b7ed3 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd @@ -70,8 +70,14 @@ func scan_directory(resource_path: String) -> Array[Script]: func _scan_test_suites_scripts(dir: DirAccess, collected_suites: Array[Script]) -> Array[Script]: + # Skip excluded directories + if dir.file_exists(".gdignore"): + prints("Exclude directory %s, containing .gdignore file" % dir.get_current_dir()) + return [] + if exclude_scan_directories.has(dir.get_current_dir()): return collected_suites + var err := dir.list_dir_begin() if err != OK: push_error("Error on scanning directory %s" % dir.get_current_dir(), error_string(err)) @@ -166,9 +172,10 @@ func _build_test_attribute(script: GDScript, fd: GdFunctionDescriptor) -> TestCa if arg.type() == GdObjects.TYPE_FUZZER: attribute.fuzzers.append(arg) else: - match arg.name(): + # We allow underscore as prefix to prevent unused argument warnings + match arg.name().trim_prefix("_"): ARGUMENT_TIMEOUT: - attribute.timeout = convert(arg.default(), TYPE_INT) + attribute.timeout = type_convert(arg.default(), TYPE_INT) ARGUMENT_SKIP: var result: Variant = _expression_runner.execute(script, arg.plain_value()) if result is bool: @@ -178,9 +185,9 @@ func _build_test_attribute(script: GDScript, fd: GdFunctionDescriptor) -> TestCa ARGUMENT_SKIP_REASON: attribute.skip_reason = arg.plain_value() Fuzzer.ARGUMENT_ITERATIONS: - attribute.fuzzer_iterations = convert(arg.default(), TYPE_INT) + attribute.fuzzer_iterations = type_convert(arg.default(), TYPE_INT) Fuzzer.ARGUMENT_SEED: - attribute.test_seed = convert(arg.default(), TYPE_INT) + attribute.test_seed = type_convert(arg.default(), TYPE_INT) ARGUMENT_PARAMETER_SET: collected_unknown_aruments.clear() pass @@ -224,7 +231,7 @@ static func is_test_suite(script: Script) -> bool: return true stack.push_back(base) elif script != null and script.get_class() == "CSharpScript": - return GdUnit4CSharpApiLoader.is_test_suite(script) + return true return false @@ -239,7 +246,8 @@ static func parse_test_suite_name(script: Script) -> String: func _handle_test_suite_arguments(test_suite: GdUnitTestSuite, script: GDScript, fd: GdFunctionDescriptor) -> void: for arg in fd.args(): - match arg.name(): + # We allow underscore as prefix to prevent unused argument warnings + match arg.name().trim_prefix("_"): ARGUMENT_SKIP: var result: Variant = _expression_runner.execute(script, arg.plain_value()) if result is bool: @@ -355,9 +363,14 @@ static func add_test_case(resource_path: String, func_name: String) -> GdUnitRe var line_number := count_lines(script) + 2 var func_body := TEST_FUNC_TEMPLATE.replace("${func_name}", func_name) if Engine.is_editor_hint(): - var settings := EditorInterface.get_editor_settings() - var ident_type :int = settings.get_setting("text_editor/behavior/indent/type") - var ident_size :int = settings.get_setting("text_editor/behavior/indent/size") + # NOTE: Avoid using EditorInterface and EditorSettings directly, + # as it causes compilation errors in exported projects. + @warning_ignore_start("unsafe_method_access") + var editor_interface: Object = Engine.get_singleton("EditorInterface") + var settings: Object = editor_interface.get_editor_settings() + var ident_type: int = settings.get_setting("text_editor/behavior/indent/type") + var ident_size: int = settings.get_setting("text_editor/behavior/indent/size") + @warning_ignore_restore("unsafe_method_access") if ident_type == 1: func_body = func_body.replace(" ", "".lpad(ident_size, " ")) script.source_code += func_body diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid index f3d0fd6d..fd61f33d 100644 --- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid @@ -1 +1 @@ -uid://d05tmi5e02oqi +uid://depwhgo7tmdw1 diff --git a/addons/gdUnit4/src/core/GdUnitTools.gd.uid b/addons/gdUnit4/src/core/GdUnitTools.gd.uid index 7a94c3ae..a27899be 100644 --- a/addons/gdUnit4/src/core/GdUnitTools.gd.uid +++ b/addons/gdUnit4/src/core/GdUnitTools.gd.uid @@ -1 +1 @@ -uid://bb5gyqgycvniq +uid://clx6q4t32ypsx diff --git a/addons/gdUnit4/src/core/GodotVersionFixures.gd b/addons/gdUnit4/src/core/GodotVersionFixures.gd index 9d5b6bb3..52993251 100644 --- a/addons/gdUnit4/src/core/GodotVersionFixures.gd +++ b/addons/gdUnit4/src/core/GodotVersionFixures.gd @@ -3,16 +3,6 @@ class_name GodotVersionFixures extends RefCounted -@warning_ignore("shadowed_global_identifier") -static func type_convert(value: Variant, type: int) -> Variant: - return convert(value, type) - - -@warning_ignore("shadowed_global_identifier") -static func convert(value: Variant, type: int) -> Variant: - return type_convert(value, type) - - # handle global_position fixed by https://github.com/godotengine/godot/pull/88473 static func set_event_global_position(event: InputEventMouseMotion, global_position: Vector2) -> void: if Engine.get_version_info().hex >= 0x40202 or Engine.get_version_info().hex == 0x40104: diff --git a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid index 6c3d5a06..c4a375a2 100644 --- a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid +++ b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid @@ -1 +1 @@ -uid://5ihmoi7vjmd +uid://cs6ciov4fipgf diff --git a/addons/gdUnit4/src/core/LocalTime.gd.uid b/addons/gdUnit4/src/core/LocalTime.gd.uid index 9332291f..ce4bae8a 100644 --- a/addons/gdUnit4/src/core/LocalTime.gd.uid +++ b/addons/gdUnit4/src/core/LocalTime.gd.uid @@ -1 +1 @@ -uid://bkm1q7yvoxy7j +uid://dv514qr0dmgdf diff --git a/addons/gdUnit4/src/core/_TestCase.gd b/addons/gdUnit4/src/core/_TestCase.gd index 58409e79..2ef07dca 100644 --- a/addons/gdUnit4/src/core/_TestCase.gd +++ b/addons/gdUnit4/src/core/_TestCase.gd @@ -9,7 +9,8 @@ var _attribute: TestCaseAttribute var _current_iteration: int = -1 var _expect_to_interupt := false var _timer: Timer -var _interupted: bool = false +var _interupted := false +var _terminated := false var _failed := false var _parameter_set_resolver: GdUnitTestParameterSetResolver var _is_disposed := false @@ -123,7 +124,7 @@ func do_interrupt() -> void: # We need to dispose manually the function state here GdObjects.dispose_function_state(_func_state) if not is_expect_interupted(): - var execution_context:= GdUnitThreadManager.get_current_context().get_execution_context() + var execution_context := GdUnitThreadManager.get_current_context().get_execution_context() if is_fuzzed(): execution_context.add_report(GdUnitReport.new()\ .create(GdUnitReport.INTERUPTED, line_number(), GdAssertMessages.fuzzer_interuped(_current_iteration, "timedout"))) @@ -133,6 +134,16 @@ func do_interrupt() -> void: completed.emit() +func do_terminate() -> void: + _terminated = true + # We need to dispose manually the function state here + GdObjects.dispose_function_state(_func_state) + var execution_context := GdUnitThreadManager.get_current_context().get_execution_context() + execution_context.add_report(GdUnitReport.new()\ + .create(GdUnitReport.TERMINATED, line_number(), GdAssertMessages.test_session_terminated())) + completed.emit() + + func _set_failure_handler() -> void: if not GdUnitSignals.instance().gdunit_set_test_failed.is_connected(_failure_received): @warning_ignore("return_value_discarded") @@ -172,6 +183,10 @@ func is_expect_interupted() -> bool: return _expect_to_interupt +func is_terminated() -> bool: + return _terminated + + func is_parameterized() -> bool: return _parameter_set_resolver.is_parameterized() @@ -192,11 +207,6 @@ func test_name() -> String: return _test_case.test_name -@warning_ignore("native_method_override") -func get_name() -> StringName: - return _test_case.test_name - - func line_number() -> int: return _test_case.line_number diff --git a/addons/gdUnit4/src/core/_TestCase.gd.uid b/addons/gdUnit4/src/core/_TestCase.gd.uid index 15288372..b1eccfca 100644 --- a/addons/gdUnit4/src/core/_TestCase.gd.uid +++ b/addons/gdUnit4/src/core/_TestCase.gd.uid @@ -1 +1 @@ -uid://srhv4b4s7cl6 +uid://ccylvyb0mi226 diff --git a/addons/gdUnit4/src/core/assets/touch-button.png.import b/addons/gdUnit4/src/core/assets/touch-button.png.import index a84ce13b..2dd49aa4 100644 --- a/addons/gdUnit4/src/core/assets/touch-button.png.import +++ b/addons/gdUnit4/src/core/assets/touch-button.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://btx5kcrsngasl" +uid="uid://cwajpo8jq7q3j" path="res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043f641.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/touch-button.png-2fff40c8520d8e97a57db1b2b043 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid b/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid index 61fc47c9..ccdfbce8 100644 --- a/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid +++ b/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid @@ -1 +1 @@ -uid://dgxgnjr51711k +uid://7yc1uyikhaqe diff --git a/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd b/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd new file mode 100644 index 00000000..f5c5ffb3 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd @@ -0,0 +1,64 @@ +@abstract class_name GdUnitBaseCommand +extends Node + + +var id: String +var icon: Texture2D +var shortcut: Shortcut = null +var shortcut_type: GdUnitShortcut.ShortCut + + +func _init(p_id: String, p_shortcut: GdUnitShortcut.ShortCut = GdUnitShortcut.ShortCut.NONE) -> void: + id = p_id + shortcut_type = p_shortcut + _set_shortcut() + + +func _shortcut_input(event: InputEvent) -> void: + if is_running(): + return + + if shortcut and shortcut.matches_event(event): + execute() + get_viewport().set_input_as_handled() + + +func update_shortcut() -> void: + _set_shortcut() + + +func _set_shortcut() -> void: + if shortcut_type == GdUnitShortcut.ShortCut.NONE: + return + + var property_name := GdUnitShortcut.as_property(shortcut_type) + var property := GdUnitSettings.get_property(property_name) + var keys := GdUnitShortcut.default_keys(shortcut_type) + if property != null: + keys = property.value() + var inputEvent := _create_shortcut_input_even(keys) + + shortcut = Shortcut.new() + shortcut.set_events([inputEvent]) + + +func _create_shortcut_input_even(key_codes: PackedInt32Array) -> InputEventKey: + var inputEvent := InputEventKey.new() + inputEvent.pressed = true + for key_code in key_codes: + match key_code: + KEY_ALT: + inputEvent.alt_pressed = true + KEY_SHIFT: + inputEvent.shift_pressed = true + KEY_CTRL: + inputEvent.ctrl_pressed = true + _: + inputEvent.keycode = key_code as Key + inputEvent.physical_keycode = key_code as Key + return inputEvent + + +@abstract func is_running() -> bool + +@abstract func execute(...parameters: Array) -> void diff --git a/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd.uid b/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd.uid new file mode 100644 index 00000000..23bbd549 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitBaseCommand.gd.uid @@ -0,0 +1 @@ +uid://bqvh5067ju753 diff --git a/addons/gdUnit4/src/core/command/GdUnitCommand.gd b/addons/gdUnit4/src/core/command/GdUnitCommand.gd deleted file mode 100644 index 659b6a3f..00000000 --- a/addons/gdUnit4/src/core/command/GdUnitCommand.gd +++ /dev/null @@ -1,41 +0,0 @@ -class_name GdUnitCommand -extends RefCounted - - -func _init(p_name :String, p_is_enabled: Callable, p_runnable: Callable, p_shortcut :GdUnitShortcut.ShortCut = GdUnitShortcut.ShortCut.NONE) -> void: - assert(p_name != null, "(%s) missing parameter 'name'" % p_name) - assert(p_is_enabled != null, "(%s) missing parameter 'is_enabled'" % p_name) - assert(p_runnable != null, "(%s) missing parameter 'runnable'" % p_name) - assert(p_shortcut != null, "(%s) missing parameter 'shortcut'" % p_name) - self.name = p_name - self.is_enabled = p_is_enabled - self.shortcut = p_shortcut - self.runnable = p_runnable - - -var name: String: - set(value): - name = value - get: - return name - - -var shortcut: GdUnitShortcut.ShortCut: - set(value): - shortcut = value - get: - return shortcut - - -var is_enabled: Callable: - set(value): - is_enabled = value - get: - return is_enabled - - -var runnable: Callable: - set(value): - runnable = value - get: - return runnable diff --git a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid deleted file mode 100644 index 41fca155..00000000 --- a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b5x0oc5ngf8lj diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd new file mode 100644 index 00000000..6a173c50 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd @@ -0,0 +1,42 @@ +@abstract class_name GdUnitCommandFileSystem +extends GdUnitBaseCommand + + +var _test_session_command: GdUnitCommandTestSession + +func _init(p_id: String, p_shortcut: GdUnitShortcut.ShortCut, test_session_command: GdUnitCommandTestSession) -> void: + super(p_id, p_shortcut) + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute_tests(paths: PackedStringArray, with_debug: bool) -> void: + var suite_scaner := GdUnitTestSuiteScanner.new() + var scripts: Array[Script] + + for resource_path in paths: + # directories and test-suites are valid to enable the menu + if DirAccess.dir_exists_absolute(resource_path): + scripts.append_array(suite_scaner.scan_directory(resource_path)) + continue + + var file_type := resource_path.get_extension() + if file_type == "gd" or file_type == "cs": + var script := GdUnitTestSuiteScanner.load_with_disabled_warnings(resource_path) + + if GdUnitTestSuiteScanner.is_test_suite(script): + scripts.append(script) + + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) + var tests_to_execute: Array[GdUnitTestCase] = [] + for script in scripts: + GdUnitTestDiscoverer.discover_tests(script, func(test_case: GdUnitTestCase) -> void: + tests_to_execute.append(test_case) + GdUnitTestDiscoverSink.discover(test_case) + ) + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) + GdUnitTestDiscoverer.console_log_discover_results(tests_to_execute) + _test_session_command.execute(tests_to_execute, with_debug) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd.uid new file mode 100644 index 00000000..824a244f --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystem.gd.uid @@ -0,0 +1 @@ +uid://dyop2p7ooadh6 diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd new file mode 100644 index 00000000..c4c553c5 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd @@ -0,0 +1,17 @@ +class_name GdUnitCommandFileSystemDebugTests +extends GdUnitCommandFileSystem + + +const ID := "Debug FileSystem Tests" + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RUN_TESTSUITE_DEBUG, test_session_command) + icon = GdUnitUiTools.get_icon("PlayStart") + + +func execute(...parameters: Array) -> void: + if parameters.is_empty(): + return + var selected_paths: PackedStringArray = parameters[0] + execute_tests(selected_paths, true) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd.uid new file mode 100644 index 00000000..408d7415 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemDebugTests.gd.uid @@ -0,0 +1 @@ +uid://cjwa27bm8mr1c diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd new file mode 100644 index 00000000..d17413fc --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd @@ -0,0 +1,15 @@ +class_name GdUnitCommandFileSystemRunTests +extends GdUnitCommandFileSystem + + +const ID := "Run FileSystem Tests" + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RUN_TESTSUITE, test_session_command) + icon = GdUnitUiTools.get_icon("Play") + + +func execute(...parameters: Array) -> void: + var selected_paths: PackedStringArray = parameters[0] + execute_tests(selected_paths, false) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd.uid new file mode 100644 index 00000000..d9599265 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandFileSystemRunTests.gd.uid @@ -0,0 +1 @@ +uid://tgjqklcplw4w diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd index bf688df5..8e2b2f3e 100644 --- a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd +++ b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd @@ -1,50 +1,11 @@ class_name GdUnitCommandHandler extends Object -signal gdunit_runner_start() -signal gdunit_runner_stop(client_id :int) - const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") -const CMD_RUN_OVERALL = "Debug Overall TestSuites" -const CMD_RUN_TESTCASE = "Run TestCases" -const CMD_RUN_TESTCASE_DEBUG = "Run TestCases (Debug)" -const CMD_RUN_TESTSUITE = "Run TestSuites" -const CMD_RUN_TESTSUITE_DEBUG = "Run TestSuites (Debug)" -const CMD_RERUN_TESTS = "ReRun Tests" -const CMD_RERUN_TESTS_DEBUG = "ReRun Tests (Debug)" -const CMD_STOP_TEST_RUN = "Stop Test Run" -const CMD_CREATE_TESTCASE = "Create TestCase" - -const SETTINGS_SHORTCUT_MAPPING := { - "N/A" : GdUnitShortcut.ShortCut.NONE, - GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST : GdUnitShortcut.ShortCut.RERUN_TESTS, - GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG, - GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_OVERALL : GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL, - GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_STOP : GdUnitShortcut.ShortCut.STOP_TEST_RUN, - GdUnitSettings.SHORTCUT_EDITOR_RUN_TEST : GdUnitShortcut.ShortCut.RUN_TESTCASE, - GdUnitSettings.SHORTCUT_EDITOR_RUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG, - GdUnitSettings.SHORTCUT_EDITOR_CREATE_TEST : GdUnitShortcut.ShortCut.CREATE_TEST, - GdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST : GdUnitShortcut.ShortCut.RUN_TESTSUITE, - GdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG : GdUnitShortcut.ShortCut.RUN_TESTSUITE_DEBUG -} - -# the current test runner config -var _runner_config := GdUnitRunnerConfig.new() - -# holds the current connected gdUnit runner client id -var _client_id: int -# if no debug mode we have an process id -var _current_runner_process_id: int = 0 -# hold is current an test running -var _is_running: bool = false -# holds if the current running tests started in debug mode -var _running_debug_mode: bool - -var _commands := {} -var _shortcuts := {} - +var _commnand_mappings: Dictionary[String, GdUnitBaseCommand]= {} +var test_session_command := GdUnitCommandTestSession.new() static func instance() -> GdUnitCommandHandler: return GdUnitSingleton.instance("GdUnitCommandHandler", func() -> GdUnitCommandHandler: return GdUnitCommandHandler.new()) @@ -52,28 +13,23 @@ static func instance() -> GdUnitCommandHandler: @warning_ignore("return_value_discarded") func _init() -> void: - assert_shortcut_mappings(SETTINGS_SHORTCUT_MAPPING) - GdUnitSignals.instance().gdunit_event.connect(_on_event) - GdUnitSignals.instance().gdunit_client_connected.connect(_on_client_connected) GdUnitSignals.instance().gdunit_client_disconnected.connect(_on_client_disconnected) GdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed) - # preload previous test execution - @warning_ignore("return_value_discarded") - _runner_config.load_config() - init_shortcuts() - var is_running := func(_script :Script) -> bool: return _is_running - var is_not_running := func(_script :Script) -> bool: return !_is_running - register_command(GdUnitCommand.new(CMD_RUN_OVERALL, is_not_running, cmd_run_overall.bind(true), GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL)) - register_command(GdUnitCommand.new(CMD_RUN_TESTCASE, is_not_running, cmd_editor_run_test.bind(false), GdUnitShortcut.ShortCut.RUN_TESTCASE)) - register_command(GdUnitCommand.new(CMD_RUN_TESTCASE_DEBUG, is_not_running, cmd_editor_run_test.bind(true), GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG)) - register_command(GdUnitCommand.new(CMD_RUN_TESTSUITE, is_not_running, cmd_run_test_suites.bind(false), GdUnitShortcut.ShortCut.RUN_TESTSUITE)) - register_command(GdUnitCommand.new(CMD_RUN_TESTSUITE_DEBUG, is_not_running, cmd_run_test_suites.bind(true), GdUnitShortcut.ShortCut.RUN_TESTSUITE_DEBUG)) - register_command(GdUnitCommand.new(CMD_RERUN_TESTS, is_not_running, cmd_run.bind(false), GdUnitShortcut.ShortCut.RERUN_TESTS)) - register_command(GdUnitCommand.new(CMD_RERUN_TESTS_DEBUG, is_not_running, cmd_run.bind(true), GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG)) - register_command(GdUnitCommand.new(CMD_CREATE_TESTCASE, is_not_running, cmd_create_test, GdUnitShortcut.ShortCut.CREATE_TEST)) - register_command(GdUnitCommand.new(CMD_STOP_TEST_RUN, is_running, cmd_stop.bind(_client_id), GdUnitShortcut.ShortCut.STOP_TEST_RUN)) + _register_command(test_session_command) + _register_command(GdUnitCommandStopTestSession.new(test_session_command)) + _register_command(GdUnitCommandInspectorRunTests.new(test_session_command)) + _register_command(GdUnitCommandInspectorDebugTests.new(test_session_command)) + _register_command(GdUnitCommandInspectorRerunTestsUntilFailure.new(test_session_command)) + _register_command(GdUnitCommandInspectorTreeCollapse.new()) + _register_command(GdUnitCommandInspectorTreeExpand.new()) + _register_command(GdUnitCommandScriptEditorRunTests.new(test_session_command)) + _register_command(GdUnitCommandScriptEditorDebugTests.new(test_session_command)) + _register_command(GdUnitCommandScriptEditorCreateTest.new()) + _register_command(GdUnitCommandFileSystemRunTests.new(test_session_command)) + _register_command(GdUnitCommandFileSystemDebugTests.new(test_session_command)) + _register_command(GdUnitCommandRunTestsOverall.new(test_session_command)) # schedule discover tests if enabled and running inside the editor if Engine.is_editor_hint() and GdUnitSettings.is_test_discover_enabled(): @@ -84,332 +40,79 @@ func _init() -> void: func _notification(what: int) -> void: if what == NOTIFICATION_PREDELETE: - _commands.clear() - _shortcuts.clear() + for command: GdUnitBaseCommand in _commnand_mappings.values(): + if Engine.is_editor_hint(): + EditorInterface.get_command_palette().remove_command("GdUnit4/"+command.id) + command.free() + _commnand_mappings.clear() func _do_process() -> void: - check_test_run_stopped_manually() - - -# is checking if the user has press the editor stop scene -func check_test_run_stopped_manually() -> void: - if is_test_running_but_stop_pressed(): + # Do stop test execution when the user has stoped the main scene manually + if test_session_command._is_debug and test_session_command.is_running() and not EditorInterface.is_playing_scene(): if GdUnitSettings.is_verbose_assert_warnings(): - push_warning("Test Runner scene was stopped manually, force stopping the current test run!") - cmd_stop(_client_id) - - -func is_test_running_but_stop_pressed() -> bool: - return _running_debug_mode and _is_running and not EditorInterface.is_playing_scene() - - -func assert_shortcut_mappings(mappings: Dictionary) -> void: - for shortcut: int in GdUnitShortcut.ShortCut.values(): - assert(mappings.values().has(shortcut), "missing settings mapping for shortcut '%s'!" % GdUnitShortcut.ShortCut.keys()[shortcut]) - - -func init_shortcuts() -> void: - for shortcut: int in GdUnitShortcut.ShortCut.values(): - if shortcut == GdUnitShortcut.ShortCut.NONE: - continue - var property_name: String = SETTINGS_SHORTCUT_MAPPING.find_key(shortcut) - var property := GdUnitSettings.get_property(property_name) - var keys := GdUnitShortcut.default_keys(shortcut) - if property != null: - keys = property.value() - var inputEvent := create_shortcut_input_even(keys) - register_shortcut(shortcut, inputEvent) - - -func create_shortcut_input_even(key_codes: PackedInt32Array) -> InputEventKey: - var inputEvent := InputEventKey.new() - inputEvent.pressed = true - for key_code in key_codes: - match key_code: - KEY_ALT: - inputEvent.alt_pressed = true - KEY_SHIFT: - inputEvent.shift_pressed = true - KEY_CTRL: - inputEvent.ctrl_pressed = true - _: - inputEvent.keycode = key_code as Key - inputEvent.physical_keycode = key_code as Key - return inputEvent - - -func register_shortcut(p_shortcut: GdUnitShortcut.ShortCut, p_input_event: InputEvent) -> void: - GdUnitTools.prints_verbose("register shortcut: '%s' to '%s'" % [GdUnitShortcut.ShortCut.keys()[p_shortcut], p_input_event.as_text()]) - var shortcut := Shortcut.new() - shortcut.set_events([p_input_event]) - var command_name := get_shortcut_command(p_shortcut) - _shortcuts[p_shortcut] = GdUnitShortcutAction.new(p_shortcut, shortcut, command_name) - - -func get_shortcut(shortcut_type: GdUnitShortcut.ShortCut) -> Shortcut: - return get_shortcut_action(shortcut_type).shortcut - - -func get_shortcut_action(shortcut_type: GdUnitShortcut.ShortCut) -> GdUnitShortcutAction: - return _shortcuts.get(shortcut_type) - - -func get_shortcut_command(p_shortcut: GdUnitShortcut.ShortCut) -> String: - return GdUnitShortcut.CommandMapping.get(p_shortcut, "unknown command") - - -func register_command(p_command: GdUnitCommand) -> void: - _commands[p_command.name] = p_command - - -func command(cmd_name: String) -> GdUnitCommand: - return _commands.get(cmd_name) - - -func cmd_run_test_suites(scripts: Array[Script], debug: bool, rerun := false) -> void: - # Update test discovery - GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) - var tests_to_execute: Array[GdUnitTestCase] = [] - for script in scripts: - GdUnitTestDiscoverer.discover_tests(script, func(test_case: GdUnitTestCase) -> void: - tests_to_execute.append(test_case) - GdUnitTestDiscoverSink.discover(test_case) - ) - GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) - GdUnitTestDiscoverer.console_log_discover_results(tests_to_execute) - - # create new runner runner_config for fresh run otherwise use saved one - if not rerun: - var result := _runner_config.clear()\ - .add_test_cases(tests_to_execute)\ - .save_config() - if result.is_error(): - push_error(result.error_message()) - return - cmd_run(debug) - - -func cmd_run_test_case(script: Script, test_case: String, test_param_index: int, debug: bool, rerun := false) -> void: - # Update test discovery - var tests_to_execute: Array[GdUnitTestCase] = [] - GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) - GdUnitTestDiscoverer.discover_tests(script, func(test: GdUnitTestCase) -> void: - # We filter for a single test - if test.test_name == test_case: - # We only add selected parameterized test to the execution list - if test_param_index == -1: - tests_to_execute.append(test) - elif test.attribute_index == test_param_index: - tests_to_execute.append(test) - GdUnitTestDiscoverSink.discover(test) - ) - GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) - GdUnitTestDiscoverer.console_log_discover_results(tests_to_execute) - - # create new runner config for fresh run otherwise use saved one - if not rerun: - var result := _runner_config.clear()\ - .add_test_cases(tests_to_execute)\ - .save_config() - if result.is_error(): - push_error(result.error_message()) - return - cmd_run(debug) + print_debug("Test Runner scene was stopped manually, force stopping the current test run!") + command_execute(GdUnitCommandStopTestSession.ID) -func cmd_run_tests(tests_to_execute: Array[GdUnitTestCase], debug: bool) -> void: - # Save tests to runner config before execute - var result := _runner_config.clear()\ - .add_test_cases(tests_to_execute)\ - .save_config() - if result.is_error(): - push_error(result.error_message()) +func command_icon(command_id: String) -> Texture2D: + if not _commnand_mappings.has(command_id): + push_error("GdUnitCommandHandler:command_icon(): No command id '%s' is registered." % command_id) + print_stack() return - cmd_run(debug) + return _commnand_mappings[command_id].icon -func cmd_run_overall(debug: bool) -> void: - var tests_to_execute := await GdUnitTestDiscoverer.run() - var result := _runner_config.clear()\ - .add_test_cases(tests_to_execute)\ - .save_config() - if result.is_error(): - push_error(result.error_message()) +func command_shortcut(command_id: String) -> Shortcut: + if not _commnand_mappings.has(command_id): + push_error("GdUnitCommandHandler:command_shortcut(): No command id '%s' is registered." % command_id) + print_stack() return - cmd_run(debug) + return _commnand_mappings[command_id].shortcut -func cmd_run(debug: bool) -> void: - # don't start is already running - if _is_running: +func command_execute(...parameters: Array) -> void: + if parameters.is_empty(): + push_error("Invalid arguments used on CommandHandler:execute()! Expecting []") + print_stack() return - GdUnitSignals.instance().gdunit_event.emit(GdUnitInit.new()) - # save current selected excution config - var server_port: int = Engine.get_meta("gdunit_server_port") - var result := _runner_config.set_server_port(server_port).save_config() - if result.is_error(): - push_error(result.error_message()) + var command_id: String = parameters.pop_front() + if not _commnand_mappings.has(command_id): + push_error("GdUnitCommandHandler:command_execute(): No command id '%s' is registered." % command_id) + print_stack() return - # before start we have to save all changes - ScriptEditorControls.save_all_open_script() - gdunit_runner_start.emit() - _current_runner_process_id = -1 - _running_debug_mode = debug - if debug: - run_debug_mode() - else: - run_release_mode() + await _commnand_mappings[command_id].callv("execute", parameters) -func cmd_stop(client_id: int) -> void: - # don't stop if is already stopped - if not _is_running: +func _register_command(command: GdUnitBaseCommand) -> void: + # first verify the command is not already registerd + if _commnand_mappings.has(command.id): + push_error("GdUnitCommandHandler:_register_command(): Command with id '%s' is already registerd!" % command.id) return - _is_running = false - gdunit_runner_stop.emit(client_id) - if _running_debug_mode: - EditorInterface.stop_playing_scene() - elif _current_runner_process_id > 0: - if OS.is_process_running(_current_runner_process_id): - var result := OS.kill(_current_runner_process_id) - if result != OK: - push_error("ERROR checked stopping GdUnit Test Runner. error code: %s" % result) - _current_runner_process_id = -1 - -func cmd_editor_run_test(debug: bool) -> void: - if is_active_script_editor(): - var cursor_line := active_base_editor().get_caret_line() - #run test case? - var regex := RegEx.new() - @warning_ignore("return_value_discarded") - regex.compile("(^func[ ,\t])(test_[a-zA-Z0-9_]*)") - var result := regex.search(active_base_editor().get_line(cursor_line)) - if result: - var func_name := result.get_string(2).strip_edges() - if func_name.begins_with("test_"): - cmd_run_test_case(active_script(), func_name, -1, debug) - return - # otherwise run the full test suite - var selected_test_suites: Array[Script] = [active_script()] - cmd_run_test_suites(selected_test_suites, debug) - - -func cmd_create_test() -> void: - if not is_active_script_editor(): - return - var cursor_line := active_base_editor().get_caret_line() - var result := GdUnitTestSuiteBuilder.create(active_script(), cursor_line) - if result.is_error(): - # show error dialog - push_error("Failed to create test case: %s" % result.error_message()) - return - var info: Dictionary = result.value() - var script_path: String = info.get("path") - var script_line: int = info.get("line") - ScriptEditorControls.edit_script(script_path, script_line) + _commnand_mappings[command.id] = command + if Engine.is_editor_hint(): + EditorInterface.get_base_control().add_child(command) + EditorInterface.get_command_palette().add_command(command.id, "GdUnit4/"+command.id, command.execute, command.shortcut.get_as_text() if command.shortcut else "None") func cmd_discover_tests() -> void: await GdUnitTestDiscoverer.run() -static func scan_all_test_directories(root: String) -> PackedStringArray: - var base_directory := "res://" - # If the test root folder is configured as blank, "/", or "res://", use the root folder as described in the settings panel - if root.is_empty() or root == "/" or root == base_directory: - return [base_directory] - return scan_test_directories(base_directory, root, []) - - -static func scan_test_directories(base_directory: String, test_directory: String, test_suite_paths: PackedStringArray) -> PackedStringArray: - print_verbose("Scannning for test directory '%s' at %s" % [test_directory, base_directory]) - for directory in DirAccess.get_directories_at(base_directory): - if directory.begins_with("."): - continue - var current_directory := normalize_path(base_directory + "/" + directory) - if GdUnitTestSuiteScanner.exclude_scan_directories.has(current_directory): - continue - if match_test_directory(directory, test_directory): - @warning_ignore("return_value_discarded") - test_suite_paths.append(current_directory) - else: - @warning_ignore("return_value_discarded") - scan_test_directories(current_directory, test_directory, test_suite_paths) - return test_suite_paths - - -static func normalize_path(path: String) -> String: - return path.replace("///", "//") - - -static func match_test_directory(directory: String, test_directory: String) -> bool: - return directory == test_directory or test_directory.is_empty() or test_directory == "/" or test_directory == "res://" - - -func run_debug_mode() -> void: - EditorInterface.play_custom_scene("res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn") - _is_running = true - - -func run_release_mode() -> void: - var arguments := Array() - if OS.is_stdout_verbose(): - arguments.append("--verbose") - arguments.append("--no-window") - arguments.append("--path") - arguments.append(ProjectSettings.globalize_path("res://")) - arguments.append("res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn") - _current_runner_process_id = OS.create_process(OS.get_executable_path(), arguments, false); - _is_running = true - - -func is_active_script_editor() -> bool: - return EditorInterface.get_script_editor().get_current_editor() != null - - -func active_base_editor() -> TextEdit: - return EditorInterface.get_script_editor().get_current_editor().get_base_editor() - - -func active_script() -> Script: - return EditorInterface.get_script_editor().get_current_script() - - - ################################################################################ # signals handles ################################################################################ func _on_event(event: GdUnitEvent) -> void: - if event.type() == GdUnitEvent.STOP: - cmd_stop(_client_id) - - -func _on_stop_pressed() -> void: - cmd_stop(_client_id) - - -func _on_run_pressed(debug := false) -> void: - cmd_run(debug) - - -func _on_run_overall_pressed(_debug := false) -> void: - cmd_run_overall(true) + if event.type() == GdUnitEvent.SESSION_CLOSE: + command_execute(GdUnitCommandStopTestSession.ID) func _on_settings_changed(property: GdUnitProperty) -> void: - if SETTINGS_SHORTCUT_MAPPING.has(property.name()): - var shortcut :GdUnitShortcut.ShortCut = SETTINGS_SHORTCUT_MAPPING.get(property.name()) - var value: PackedInt32Array = property.value() - var input_event := create_shortcut_input_even(value) - prints("Shortcut changed: '%s' to '%s'" % [GdUnitShortcut.ShortCut.keys()[shortcut], input_event.as_text()]) - var action := get_shortcut_action(shortcut) - if action != null: - action.update_shortcut(input_event) - else: - register_shortcut(shortcut, input_event) + for command: GdUnitBaseCommand in _commnand_mappings.values(): + command.update_shortcut() + if property.name() == GdUnitSettings.TEST_DISCOVER_ENABLED: var timer :SceneTreeTimer = (Engine.get_main_loop() as SceneTree).create_timer(3) @warning_ignore("return_value_discarded") @@ -419,12 +122,5 @@ func _on_settings_changed(property: GdUnitProperty) -> void: ################################################################################ # Network stuff ################################################################################ -func _on_client_connected(client_id: int) -> void: - _client_id = client_id - - -func _on_client_disconnected(client_id: int) -> void: - # only stops is not in debug mode running and the current client - if not _running_debug_mode and _client_id == client_id: - cmd_stop(client_id) - _client_id = -1 +func _on_client_disconnected(_client_id: int) -> void: + command_execute(GdUnitCommandStopTestSession.ID) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid index 8c138b6d..2cd9b7f0 100644 --- a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid @@ -1 +1 @@ -uid://c0sifu2vquucy +uid://dnbnaa6c5peha diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd new file mode 100644 index 00000000..cb4a6343 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd @@ -0,0 +1,27 @@ +class_name GdUnitCommandInspectorDebugTests +extends GdUnitBaseCommand + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") +const ID := "Debug Inspector Tests" + + +var _test_session_command: GdUnitCommandTestSession + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG) + icon = GdUnitUiTools.get_icon("PlayStart") + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute(..._parameters: Array) -> void: + var base_control := EditorInterface.get_base_control() + var inspector: InspectorTreeMainPanel = base_control.get_meta("GdUnit4Inspector") + var selected_item := inspector._tree.get_selected() + var tests_to_execute := inspector.collect_test_cases(selected_item) + + _test_session_command.execute(tests_to_execute, true) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd.uid new file mode 100644 index 00000000..7189c505 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorDebugTests.gd.uid @@ -0,0 +1 @@ +uid://b4nt2t7rdwx4i diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd new file mode 100644 index 00000000..e2ec5e4a --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd @@ -0,0 +1,57 @@ +class_name GdUnitCommandInspectorRerunTestsUntilFailure +extends GdUnitBaseCommand + + +signal session_closed() + + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") +const ID := "Rerun Inspector Tests Until Failure" + + +var _test_session_command: GdUnitCommandTestSession +var _current_execution_count := 0 + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RERUN_TESTS_UNTIL_FAILURE) + icon = GdUnitUiTools.get_icon("Play") + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute(..._parameters: Array) -> void: + var base_control := EditorInterface.get_base_control() + var inspector: InspectorTreeMainPanel = base_control.get_meta("GdUnit4Inspector") + var selected_item := inspector._tree.get_selected() + var tests_to_execute := inspector.collect_test_cases(selected_item) + var rerun_until_failure_count := GdUnitSettings.get_rerun_max_retries() + var saved_settings: bool = ProjectSettings.get_setting(GdUnitSettings.TEST_FLAKY_CHECK) + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_CHECK, false) + + GdUnitSignals.instance().gdunit_event.connect(_on_test_event) + _current_execution_count = 0 + + _test_session_command._is_fail_fast = true + while _current_execution_count < rerun_until_failure_count: + _test_session_command.execute(tests_to_execute, true) + await session_closed + _test_session_command._is_fail_fast = false + + ProjectSettings.set_setting(GdUnitSettings.TEST_FLAKY_CHECK, saved_settings) + GdUnitSignals.instance().gdunit_event.disconnect(_on_test_event) + + +func _on_test_event(event: GdUnitEvent) -> void: + if event.type() == GdUnitEvent.SESSION_START: + _current_execution_count += 1 + GdUnitSignals.instance().gdunit_message.emit("[color=RED]Execution Mode: ReRun until failure! (iteration %d)[/color]" % _current_execution_count) + if event.type() == GdUnitEvent.SESSION_CLOSE: + session_closed.emit() + if event.type() == GdUnitEvent.TESTCASE_AFTER: + if not event.is_success(): + GdUnitSignals.instance().gdunit_message.emit(" [color=RED](iteration: %d)[/color]" % _current_execution_count) + _current_execution_count = 9999 diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd.uid new file mode 100644 index 00000000..a298cae1 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRerunTestsUntilFailure.gd.uid @@ -0,0 +1 @@ +uid://vy805idik6iu diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd new file mode 100644 index 00000000..c0cb733f --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd @@ -0,0 +1,26 @@ +class_name GdUnitCommandInspectorRunTests +extends GdUnitBaseCommand + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") +const ID := "Run Inspector Tests" + + +var _test_session_command: GdUnitCommandTestSession + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RERUN_TESTS) + icon = GdUnitUiTools.get_icon("Play") + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute(..._parameters: Array) -> void: + var base_control := EditorInterface.get_base_control() + var inspector: InspectorTreeMainPanel = base_control.get_meta("GdUnit4Inspector") + var selected_item := inspector._tree.get_selected() + var tests_to_execute := inspector.collect_test_cases(selected_item) + _test_session_command.execute(tests_to_execute, false) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd.uid new file mode 100644 index 00000000..ee2ae0b0 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorRunTests.gd.uid @@ -0,0 +1 @@ +uid://4q5i3ffn62jj diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd new file mode 100644 index 00000000..fa784f8a --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd @@ -0,0 +1,26 @@ +class_name GdUnitCommandInspectorTreeCollapse +extends GdUnitBaseCommand + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") +const ID := "Inspector Tree Collapse" + + +func _init() -> void: + super(ID, GdUnitShortcut.ShortCut.NONE) + icon = GdUnitUiTools.get_icon("CollapseTree") + + +func is_running() -> bool: + return false + + +func execute(..._parameters: Array) -> void: + var inspector: InspectorTreeMainPanel = EditorInterface.get_base_control().get_meta("GdUnit4Inspector") + var selected_item := inspector._tree.get_selected() + if selected_item == null: + selected_item = inspector._tree.get_root() + else: + selected_item = selected_item.get_parent() + + inspector.do_collapse_all(false, selected_item) + inspector.do_collapse_all(true, selected_item) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd.uid new file mode 100644 index 00000000..a3ad97d8 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeCollapse.gd.uid @@ -0,0 +1 @@ +uid://cdg1qwl7n6pns diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd new file mode 100644 index 00000000..76f8d0bd --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd @@ -0,0 +1,25 @@ +class_name GdUnitCommandInspectorTreeExpand +extends GdUnitBaseCommand + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") +const ID := "Inspector Tree Expand" + + +func _init() -> void: + super(ID, GdUnitShortcut.ShortCut.NONE) + icon = GdUnitUiTools.get_icon("ExpandTree") + + +func is_running() -> bool: + return false + + +func execute(..._parameters: Array) -> void: + var inspector: InspectorTreeMainPanel = EditorInterface.get_base_control().get_meta("GdUnit4Inspector") + var selected_item := inspector._tree.get_selected() + if selected_item == null: + selected_item = inspector._tree.get_root() + else: + selected_item = selected_item.get_parent() + + inspector.do_collapse_all(false, selected_item) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd.uid new file mode 100644 index 00000000..0e030953 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandInspectorTreeExpand.gd.uid @@ -0,0 +1 @@ +uid://c2wlsd2wvw8bj diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd b/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd new file mode 100644 index 00000000..daf534b6 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd @@ -0,0 +1,22 @@ +class_name GdUnitCommandRunTestsOverall +extends GdUnitBaseCommand + +const ID := "Run Tests Overall" + + +var _test_session_command: GdUnitCommandTestSession + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL) + icon = GdUnitUiTools.get_run_overall_icon() + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute(..._parameters: Array) -> void: + var tests_to_execute := await GdUnitTestDiscoverer.run() + _test_session_command.execute(tests_to_execute, true) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd.uid new file mode 100644 index 00000000..54fbf322 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandRunTestsOverall.gd.uid @@ -0,0 +1 @@ +uid://dqdo40ohnqsya diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd new file mode 100644 index 00000000..5504fa73 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd @@ -0,0 +1,56 @@ +@abstract class_name GdUnitCommandScriptEditor +extends GdUnitBaseCommand + +var _test_session_command: GdUnitCommandTestSession + +func _init(p_id: String, p_shortcut: GdUnitShortcut.ShortCut, test_session_command: GdUnitCommandTestSession) -> void: + super(p_id, p_shortcut) + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute_tests(with_debug: bool) -> void: + var selected_tests := PackedStringArray() + if _is_active_script_editor(): + var cursor_line := _active_base_editor().get_caret_line() + #run test case? + var regex := RegEx.new() + @warning_ignore("return_value_discarded") + regex.compile("(^func[ ,\t])(test_[a-zA-Z0-9_]*)") + var result := regex.search(_active_base_editor().get_line(cursor_line)) + if result: + var func_name := result.get_string(2).strip_edges() + if func_name.begins_with("test_"): + selected_tests.append(func_name) + + var tests_to_execute := _collect_tests(_active_script(), selected_tests) + _test_session_command.execute(tests_to_execute, with_debug) + + +func _collect_tests(script: Script, tests: PackedStringArray) -> Array[GdUnitTestCase]: + # Update test discovery + var tests_to_execute: Array[GdUnitTestCase] = [] + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) + GdUnitTestDiscoverer.discover_tests(script, func(test: GdUnitTestCase) -> void: + if tests.is_empty() or tests.has(test.test_name): + tests_to_execute.append(test) + GdUnitTestDiscoverSink.discover(test) + ) + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) + GdUnitTestDiscoverer.console_log_discover_results(tests_to_execute) + return tests_to_execute + + +func _is_active_script_editor() -> bool: + return EditorInterface.get_script_editor().get_current_editor() != null + + +func _active_base_editor() -> TextEdit: + return EditorInterface.get_script_editor().get_current_editor().get_base_editor() + + +func _active_script() -> Script: + return EditorInterface.get_script_editor().get_current_script() diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd.uid new file mode 100644 index 00000000..53fd6241 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditor.gd.uid @@ -0,0 +1 @@ +uid://crdrcgnjd1gx2 diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd new file mode 100644 index 00000000..17876ca9 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd @@ -0,0 +1,41 @@ +class_name GdUnitCommandScriptEditorCreateTest +extends GdUnitBaseCommand + + +const ID := "Create Test" + + +func _init() -> void: + super(ID, GdUnitShortcut.ShortCut.CREATE_TEST) + icon = GdUnitUiTools.get_icon("New") + + +func is_running() -> bool: + return false + + +func execute(..._parameters: Array) -> void: + if not _is_active_script_editor(): + return + var cursor_line := _active_base_editor().get_caret_line() + var result := GdUnitTestSuiteBuilder.create(_active_script(), cursor_line) + if result.is_error(): + # show error dialog + push_error("Failed to create test case: %s" % result.error_message()) + return + var info: Dictionary = result.value() + var script_path: String = info.get("path") + var script_line: int = info.get("line") + ScriptEditorControls.edit_script(script_path, script_line) + + +func _is_active_script_editor() -> bool: + return EditorInterface.get_script_editor().get_current_editor() != null + + +func _active_base_editor() -> TextEdit: + return EditorInterface.get_script_editor().get_current_editor().get_base_editor() + + +func _active_script() -> Script: + return EditorInterface.get_script_editor().get_current_script() diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd.uid new file mode 100644 index 00000000..ab6e7f07 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorCreateTest.gd.uid @@ -0,0 +1 @@ +uid://bjwsxqh8xca1m diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd new file mode 100644 index 00000000..8ab9f24b --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd @@ -0,0 +1,14 @@ +class_name GdUnitCommandScriptEditorDebugTests +extends GdUnitCommandScriptEditor + + +const ID := "Debug ScriptEditor Tests" + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RUN_TESTCASE_DEBUG, test_session_command) + icon = GdUnitUiTools.get_icon("PlayStart") + + +func execute(..._parameters: Array) -> void: + execute_tests(true) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd.uid new file mode 100644 index 00000000..91709eb3 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorDebugTests.gd.uid @@ -0,0 +1 @@ +uid://biqs60xojaurf diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd new file mode 100644 index 00000000..a18851a7 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd @@ -0,0 +1,14 @@ +class_name GdUnitCommandScriptEditorRunTests +extends GdUnitCommandScriptEditor + + +const ID := "Run ScriptEditor Tests" + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.RUN_TESTCASE, test_session_command) + icon = GdUnitUiTools.get_icon("Play") + + +func execute(..._parameters: Array) -> void: + execute_tests(false) diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd.uid new file mode 100644 index 00000000..c54922ea --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandScriptEditorRunTests.gd.uid @@ -0,0 +1 @@ +uid://d36im2psrht7p diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd b/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd new file mode 100644 index 00000000..50a6df7b --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd @@ -0,0 +1,21 @@ +class_name GdUnitCommandStopTestSession +extends GdUnitBaseCommand + +const ID := "Stop Test Session" + + +var _test_session_command: GdUnitCommandTestSession + + +func _init(test_session_command: GdUnitCommandTestSession) -> void: + super(ID, GdUnitShortcut.ShortCut.STOP_TEST_RUN) + icon = GdUnitUiTools.get_icon("Stop") + _test_session_command = test_session_command + + +func is_running() -> bool: + return _test_session_command.is_running() + + +func execute(..._parameters: Array) -> void: + await _test_session_command.stop() diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd.uid new file mode 100644 index 00000000..cc707479 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandStopTestSession.gd.uid @@ -0,0 +1 @@ +uid://btnow3s0apvx0 diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd b/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd new file mode 100644 index 00000000..53d74ed8 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd @@ -0,0 +1,124 @@ +class_name GdUnitCommandTestSession +extends GdUnitBaseCommand + + +const ID := "Start Test Session" + + +var _current_runner_process_id: int +var _is_running: bool +var _is_debug: bool +var _is_fail_fast: bool + + +func _init() -> void: + super(ID, GdUnitShortcut.ShortCut.NONE) + _is_running = false + _is_fail_fast = false + + +func is_running() -> bool: + return _is_running + + +func stop() -> void: + if not is_running(): + return + _is_running = false + + if _is_debug: + force_pause_scene() + + GdUnitSignals.instance().gdunit_test_session_terminate.emit() + # Give the API time to commit terminate to the client + await get_tree().create_timer(.5).timeout + + if _is_debug and EditorInterface.is_playing_scene(): + EditorInterface.stop_playing_scene() + # We need finaly to send the test session close event because the current run is hard aborted. + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionClose.new()) + elif OS.is_process_running(_current_runner_process_id): + var result := OS.kill(_current_runner_process_id) + if result != OK: + push_error("ERROR checked stopping GdUnit Test Runner. error code: %s" % result) + _current_runner_process_id = -1 + # We need finaly to send the test session close event because the current run is hard aborted. + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionClose.new()) + + +## Forces the running scene to unpause when the debugger hits a breakpoint.[br] +## [br] +## When the Godot debugger stops at a breakpoint during test execution, it blocks[br] +## the main thread. This prevents signals and TCP communications from being processed,[br] +## which can cause GdUnit4 tests to hang or fail to communicate properly with the[br] +## test runner. This function programmatically unpauses the scene to restore[br] +## main thread execution while maintaining debugger functionality. [br] +## [br] +## [b]Technical Background:[/b][br] +## - Debugger breakpoints freeze the main thread to allow inspection[br] +## - Frozen main thread blocks signal processing and network communications[br] +## - GdUnit4 requires active signal/TCP processing for test coordination[br] +## - This function finds and triggers the editor's pause button to resume execution[br] +## [br] +## [b]How It Works:[/b][br] +## 1. Locates the EditorRunBar in the Godot editor UI hierarchy[br] +## 2. Searches for the pause button by matching its icon[br] +## 3. Unpresses the button if it's currently pressed (paused state)[br] +## 4. Manually triggers the button's connected callbacks to resume execution[br] +func force_pause_scene() -> bool: + var nodes := EditorInterface.get_base_control().find_children("*", "EditorRunBar", true, false) + if nodes.size() != 1: + push_error("GdUnitCommandTestSession:force_pause_scene() Can't find Editor component 'EditorRunBar'") + return false + var editor_run_bar := nodes[0] + var containers := editor_run_bar.find_children("*", "HBoxContainer", true, false) + var pause_icon := GdUnitUiTools.get_icon("Pause") + + for container in containers: + for child in container.get_children(): + if child is Button: + var button: Button = child + if pause_icon == button.icon: + button.set_pressed(false) + + var connected_signals := button.get_signal_connection_list("pressed") + if not connected_signals.is_empty(): + for signal_ in connected_signals: + var cb: Callable = signal_["callable"] + cb.call() + return true + push_error("GdUnitCommandTestSession:force_pause_scene() Can't find Editor component 'EditorRunBar'") + return false + + +func execute(...parameters: Array) -> void: + var tests_to_execute: Array[GdUnitTestCase] = parameters[0] + _is_debug = parameters[1] + + _prepare_test_session(tests_to_execute) + if _is_debug: + EditorInterface.play_custom_scene("res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn") + else: + var arguments := Array() + if OS.is_stdout_verbose(): + arguments.append("--verbose") + arguments.append("--no-window") + arguments.append("--path") + arguments.append(ProjectSettings.globalize_path("res://")) + arguments.append("res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn") + _current_runner_process_id = OS.create_process(OS.get_executable_path(), arguments, false); + _is_running = true + + +func _prepare_test_session(tests_to_execute: Array[GdUnitTestCase]) -> void: + var server_port: int = Engine.get_meta("gdunit_server_port") + var result := GdUnitRunnerConfig.new() \ + .set_server_port(server_port) \ + .do_fail_fast(_is_fail_fast) \ + .add_test_cases(tests_to_execute) \ + .save_config() + if result.is_error(): + push_error(result.error_message()) + return + # before start we have to save all scrpt changes + ScriptEditorControls.save_all_open_script() diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd.uid new file mode 100644 index 00000000..2c9f6b38 --- /dev/null +++ b/addons/gdUnit4/src/core/command/GdUnitCommandTestSession.gd.uid @@ -0,0 +1 @@ +uid://dvchhyjuhq24q diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd index 4a8aa292..33af0823 100644 --- a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd +++ b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd @@ -11,52 +11,59 @@ enum ShortCut { RUN_TESTSUITE_DEBUG, RERUN_TESTS, RERUN_TESTS_DEBUG, + RERUN_TESTS_UNTIL_FAILURE, STOP_TEST_RUN, CREATE_TEST, } - -const CommandMapping = { - ShortCut.RUN_TESTS_OVERALL: GdUnitCommandHandler.CMD_RUN_OVERALL, - ShortCut.RUN_TESTCASE: GdUnitCommandHandler.CMD_RUN_TESTCASE, - ShortCut.RUN_TESTCASE_DEBUG: GdUnitCommandHandler.CMD_RUN_TESTCASE_DEBUG, - ShortCut.RUN_TESTSUITE: GdUnitCommandHandler.CMD_RUN_TESTSUITE, - ShortCut.RUN_TESTSUITE_DEBUG: GdUnitCommandHandler.CMD_RUN_TESTSUITE_DEBUG, - ShortCut.RERUN_TESTS: GdUnitCommandHandler.CMD_RERUN_TESTS, - ShortCut.RERUN_TESTS_DEBUG: GdUnitCommandHandler.CMD_RERUN_TESTS_DEBUG, - ShortCut.STOP_TEST_RUN: GdUnitCommandHandler.CMD_STOP_TEST_RUN, - ShortCut.CREATE_TEST: GdUnitCommandHandler.CMD_CREATE_TESTCASE, -} - - const DEFAULTS_MACOS := { ShortCut.NONE : [], ShortCut.RUN_TESTCASE : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F5], ShortCut.RUN_TESTCASE_DEBUG : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F6], - ShortCut.RUN_TESTSUITE : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F5], - ShortCut.RUN_TESTSUITE_DEBUG : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F6], - ShortCut.RUN_TESTS_OVERALL : [Key.KEY_META, Key.KEY_F7], - ShortCut.STOP_TEST_RUN : [Key.KEY_META, Key.KEY_F8], - ShortCut.RERUN_TESTS : [Key.KEY_META, Key.KEY_F5], - ShortCut.RERUN_TESTS_DEBUG : [Key.KEY_META, Key.KEY_F6], + ShortCut.RUN_TESTSUITE : [Key.KEY_ALT, Key.KEY_META, Key.KEY_F5], + ShortCut.RUN_TESTSUITE_DEBUG : [Key.KEY_ALT, Key.KEY_META, Key.KEY_F6], + ShortCut.RUN_TESTS_OVERALL : [Key.KEY_ALT, Key.KEY_F7], + ShortCut.STOP_TEST_RUN : [Key.KEY_ALT, Key.KEY_F8], + ShortCut.RERUN_TESTS : [Key.KEY_ALT, Key.KEY_F5], + ShortCut.RERUN_TESTS_DEBUG : [Key.KEY_ALT, Key.KEY_F6], + ShortCut.RERUN_TESTS_UNTIL_FAILURE : [Key.KEY_ALT, Key.KEY_META, Key.KEY_F5], ShortCut.CREATE_TEST : [Key.KEY_META, Key.KEY_ALT, Key.KEY_F10], } const DEFAULTS_WINDOWS := { ShortCut.NONE : [], ShortCut.RUN_TESTCASE : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F5], - ShortCut.RUN_TESTCASE_DEBUG : [Key.KEY_CTRL,Key.KEY_ALT, Key.KEY_F6], - ShortCut.RUN_TESTSUITE : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F5], - ShortCut.RUN_TESTSUITE_DEBUG : [Key.KEY_CTRL,Key.KEY_ALT, Key.KEY_F6], - ShortCut.RUN_TESTS_OVERALL : [Key.KEY_CTRL, Key.KEY_F7], - ShortCut.STOP_TEST_RUN : [Key.KEY_CTRL, Key.KEY_F8], - ShortCut.RERUN_TESTS : [Key.KEY_CTRL, Key.KEY_F5], - ShortCut.RERUN_TESTS_DEBUG : [Key.KEY_CTRL, Key.KEY_F6], + ShortCut.RUN_TESTCASE_DEBUG : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F6], + ShortCut.RUN_TESTSUITE : [Key.KEY_ALT, Key.KEY_SHIFT, Key.KEY_F5], + ShortCut.RUN_TESTSUITE_DEBUG : [Key.KEY_ALT, Key.KEY_SHIFT, Key.KEY_F6], + ShortCut.RUN_TESTS_OVERALL : [Key.KEY_ALT, Key.KEY_F7], + ShortCut.STOP_TEST_RUN : [Key.KEY_ALT, Key.KEY_F8], + ShortCut.RERUN_TESTS : [Key.KEY_ALT, Key.KEY_F5], + ShortCut.RERUN_TESTS_DEBUG : [Key.KEY_ALT, Key.KEY_F6], + ShortCut.RERUN_TESTS_UNTIL_FAILURE : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F5], ShortCut.CREATE_TEST : [Key.KEY_CTRL, Key.KEY_ALT, Key.KEY_F10], } -static func default_keys(shortcut :ShortCut) -> PackedInt32Array: +const SETTINGS_MAPPING: Dictionary[ShortCut, String] = { + ShortCut.RUN_TESTCASE : GdUnitSettings.SHORTCUT_EDITOR_RUN_TEST, + ShortCut.RUN_TESTCASE_DEBUG : GdUnitSettings.SHORTCUT_EDITOR_RUN_TEST_DEBUG, + ShortCut.CREATE_TEST : GdUnitSettings.SHORTCUT_EDITOR_CREATE_TEST, + ShortCut.RERUN_TESTS : GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST, + ShortCut.RERUN_TESTS_DEBUG : GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG, + ShortCut.RERUN_TESTS_UNTIL_FAILURE : GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_UNTIL_FAILURE, + ShortCut.STOP_TEST_RUN : GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_STOP, + ShortCut.RUN_TESTS_OVERALL : GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_OVERALL, + ShortCut.RUN_TESTSUITE : GdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST, + ShortCut.RUN_TESTSUITE_DEBUG : GdUnitSettings.SHORTCUT_FILESYSTEM_RUN_TEST_DEBUG +} + + +static func as_property(sortcut: ShortCut) -> String: + return SETTINGS_MAPPING[sortcut] + + +static func default_keys(shortcut: ShortCut) -> PackedInt32Array: match OS.get_name().to_lower(): 'windows': return DEFAULTS_WINDOWS[shortcut] diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid index af3594b3..a79fdcaf 100644 --- a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid +++ b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid @@ -1 +1 @@ -uid://do4egyy4scgfs +uid://cf8tpvyg801du diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd deleted file mode 100644 index c49e83e5..00000000 --- a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd +++ /dev/null @@ -1,40 +0,0 @@ -class_name GdUnitShortcutAction -extends RefCounted - - -func _init(p_type :GdUnitShortcut.ShortCut, p_shortcut :Shortcut, p_command :String) -> void: - assert(p_type != null, "missing parameter 'type'") - assert(p_shortcut != null, "missing parameter 'shortcut'") - assert(p_command != null, "missing parameter 'command'") - self.type = p_type - self.shortcut = p_shortcut - self.command = p_command - - -var type: GdUnitShortcut.ShortCut: - set(value): - type = value - get: - return type - - -var shortcut: Shortcut: - set(value): - shortcut = value - get: - return shortcut - - -var command: String: - set(value): - command = value - get: - return command - - -func update_shortcut(input_event: InputEventKey) -> void: - shortcut.set_events([input_event]) - - -func _to_string() -> String: - return "GdUnitShortcutAction: %s (%s) -> %s" % [GdUnitShortcut.ShortCut.keys()[type], shortcut.get_as_text(), command] diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid deleted file mode 100644 index b0fbd4ee..00000000 --- a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bt74gepjg7ekn diff --git a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid index 713df158..8a471092 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid @@ -1 +1 @@ -uid://d0151xs4n3iyb +uid://mv5uwxxyb0cb diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd index d833e3bb..766f9eab 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd @@ -9,6 +9,9 @@ extends RefCounted ## A unique identifier for the test case. Used to track and reference specific test instances. var guid := GdUnitGUID.new() +## The resource path to the test suite +var suite_resource_path: String + ## The name of the test method/function. Should start with "test_" prefix. var test_name: String @@ -52,6 +55,7 @@ var metadata: Dictionary = {} static func from_dict(dict: Dictionary) -> GdUnitTestCase: var test := GdUnitTestCase.new() test.guid = GdUnitGUID.new(str(dict["guid"])) + test.suite_resource_path = dict["suite_resource_path"] if dict.has("suite_resource_path") else dict["source_file"] test.suite_name = dict["managed_type"] test.test_name = dict["test_name"] test.display_name = dict["simple_name"] @@ -67,6 +71,7 @@ static func from_dict(dict: Dictionary) -> GdUnitTestCase: static func to_dict(test: GdUnitTestCase) -> Dictionary: return { "guid": test.guid._guid, + "suite_resource_path": test.suite_resource_path, "managed_type": test.suite_name, "test_name" : test.test_name, "simple_name" : test.display_name, @@ -79,7 +84,7 @@ static func to_dict(test: GdUnitTestCase) -> Dictionary: } -static func from(_source_file: String, _line_number: int, _test_name: String, _attribute_index := -1, _test_parameters := "") -> GdUnitTestCase: +static func from(_suite_resource_path: String, _source_file: String, _line_number: int, _test_name: String, _attribute_index := -1, _test_parameters := "") -> GdUnitTestCase: if(_source_file == null or _source_file.is_empty()): prints(_test_name) @@ -87,13 +92,14 @@ static func from(_source_file: String, _line_number: int, _test_name: String, _a assert(_source_file != null and not _source_file.is_empty(), "Precondition: The parameter 'source_file' is not set") var test := GdUnitTestCase.new() + test.suite_resource_path = _suite_resource_path test.test_name = _test_name test.source_file = _source_file test.line_number = _line_number test.attribute_index = _attribute_index test._build_suite_name() test._build_display_name(_test_parameters) - test._build_fully_qualified_name() + test._build_fully_qualified_name(_suite_resource_path) return test @@ -109,8 +115,8 @@ func _build_display_name(_test_parameters: String) -> void: display_name = "%s:%d (%s)" % [test_name, attribute_index, _test_parameters.trim_prefix("[").trim_suffix("]").replace('"', "'")] -func _build_fully_qualified_name() -> void: - var name_space := source_file.trim_prefix("res://").trim_suffix(".gd").trim_suffix(".cs").replace("/", ".") +func _build_fully_qualified_name(_resource_path: String) -> void: + var name_space := _resource_path.trim_prefix("res://").trim_suffix(".gd").trim_suffix(".cs").replace("/", ".") if attribute_index == -1: fully_qualified_name = "%s.%s" % [name_space, test_name] diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid index cee0b72d..0ff30c5f 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid @@ -1 +1 @@ -uid://xluthcvn10cq +uid://qkckj2eljgkm diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd index f4103787..6af8255a 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd @@ -143,6 +143,13 @@ func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: return null +func get_discovered_tests() -> Array[GdUnitTestCase]: + var discovered_tests: Array[GdUnitTestCase] = [] + for test_sets: Array[GdUnitTestCase] in _discover_cache.values(): + discovered_tests.append_array(test_sets) + return discovered_tests + + ## Discovers tests in a script and tracks changes.[br] ## [br] ## Handles both GDScript and C# test suites.[br] @@ -151,6 +158,10 @@ func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: ## [param script] The test script to analyze[br] ## [param discover_sink] Optional callback for test discovery events func discover(script: Script, discover_sink: Callable = default_discover_sink) -> void: + # Verify the script has no errors before run test discovery + var result := script.reload(true) + if result != OK: + return if _is_debug: _discovered_changes["changed_tests"] = Array([], TYPE_OBJECT, "RefCounted", GdUnitTestCase) diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid index 43ec72d5..064a242e 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid @@ -1 +1 @@ -uid://bjxpngc0i437j +uid://db7ho0leopoxw diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid index 06658467..4b0376e8 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid @@ -1 +1 @@ -uid://di3w8b6us6l0p +uid://bks8ud2m170xf diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd index f7a672b6..cb3e4071 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd @@ -11,7 +11,11 @@ static func run() -> Array[GdUnitTestCase]: var t:= Thread.new() @warning_ignore("return_value_discarded") t.start(func () -> Array[GdUnitTestCase]: - var test_suite_directories :PackedStringArray = GdUnitCommandHandler.scan_all_test_directories(GdUnitSettings.test_root_folder()) + # Loading previous test session + var runner_config := GdUnitRunnerConfig.new() + runner_config.load_config() + var recovered_tests := runner_config.test_cases() + var test_suite_directories := scan_all_test_directories(GdUnitSettings.test_root_folder()) var scanner := GdUnitTestSuiteScanner.new() var collected_tests: Array[GdUnitTestCase] = [] @@ -24,11 +28,15 @@ static func run() -> Array[GdUnitTestCase]: await (Engine.get_main_loop() as SceneTree).process_frame for test_suites_script in collected_test_suites: discover_tests(test_suites_script, func(test_case: GdUnitTestCase) -> void: + # Sync test uid from last test session + recover_test_guid(test_case, recovered_tests) collected_tests.append(test_case) GdUnitTestDiscoverSink.discover(test_case) ) console_log_discover_results(collected_tests) + if !recovered_tests.is_empty(): + console_log("Recovered last test session successfully, %d tests restored." % recovered_tests.size(), true) return collected_tests ) # wait unblocked to the tread is finished @@ -40,20 +48,61 @@ static func run() -> Array[GdUnitTestCase]: return test_to_execute +## Restores the last test run session by loading the test run config file and rediscover the tests +static func restore_last_session() -> void: + if GdUnitSettings.is_test_discover_enabled(): + return + + var runner_config := GdUnitRunnerConfig.new() + var result := runner_config.load_config() + # Report possible config loading errors + if result.is_error(): + console_log("Recovery of the last test session failed: %s" % result.error_message(), true) + # If no config file found, skip test recovery + if result.is_warn(): + return + + # If no tests recorded, skip test recovery + var test_cases := runner_config.test_cases() + if test_cases.size() == 0: + return + + # We run the test session restoring in an extra thread so that the main thread is not blocked + var t:= Thread.new() + t.start(func () -> void: + # Do sync the main thread before emit the discovered test suites to the inspector + await (Engine.get_main_loop() as SceneTree).process_frame + console_log("Recovering last test session ..", true) + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverStart.new()) + for test_case in test_cases: + GdUnitTestDiscoverSink.discover(test_case) + GdUnitSignals.instance().gdunit_event.emit(GdUnitEventTestDiscoverEnd.new(0, 0)) + console_log("Recovered last test session successfully, %d tests restored." % test_cases.size(), true) + ) + t.wait_to_finish() + + +static func recover_test_guid(current: GdUnitTestCase, recovered_tests: Array[GdUnitTestCase]) -> void: + for recovered_test in recovered_tests: + if recovered_test.fully_qualified_name == current.fully_qualified_name: + current.guid = recovered_test.guid + + static func console_log_discover_results(tests: Array[GdUnitTestCase]) -> void: var grouped_by_suites := GdArrayTools.group_by(tests, func(test: GdUnitTestCase) -> String: return test.source_file ) for suite_tests: Array in grouped_by_suites.values(): var test_case: GdUnitTestCase = suite_tests[0] - console_log("Discover: TestSuite %s with %d tests fount" % [test_case.source_file, suite_tests.size()]) + console_log("Discover: TestSuite %s with %d tests found" % [test_case.source_file, suite_tests.size()]) console_log("Discover tests done, %d TestSuites and total %d Tests found. " % [grouped_by_suites.size(), tests.size()]) console_log("") -static func console_log(message: String) -> void: +static func console_log(message: String, on_console := false) -> void: prints(message) - #GdUnitSignals.instance().gdunit_message.emit(message) + if on_console: + GdUnitSignals.instance().gdunit_message.emit(message) static func filter_tests(method: Dictionary) -> bool: @@ -81,7 +130,42 @@ static func discover_tests(source_script: Script, discover_sink := default_disco for test_case in resolver.resolve_test_cases(source_script as GDScript): discover_sink.call(test_case) elif source_script.get_class() == "CSharpScript": - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return for test_case in GdUnit4CSharpApiLoader.discover_tests(source_script): discover_sink.call(test_case) + + +static func scan_all_test_directories(root: String) -> PackedStringArray: + var base_directory := "res://" + # If the test root folder is configured as blank, "/", or "res://", use the root folder as described in the settings panel + if root.is_empty() or root == "/" or root == base_directory: + return [base_directory] + return scan_test_directories(base_directory, root, []) + + +static func scan_test_directories(base_directory: String, test_directory: String, test_suite_paths: PackedStringArray) -> PackedStringArray: + print_verbose("Scannning for test directory '%s' at %s" % [test_directory, base_directory]) + for directory in DirAccess.get_directories_at(base_directory): + if directory.begins_with("."): + continue + var current_directory := normalize_path(base_directory + "/" + directory) + if FileAccess.file_exists(current_directory + "/.gdignore"): + continue + if GdUnitTestSuiteScanner.exclude_scan_directories.has(current_directory): + continue + if match_test_directory(directory, test_directory): + @warning_ignore("return_value_discarded") + test_suite_paths.append(current_directory) + else: + @warning_ignore("return_value_discarded") + scan_test_directories(current_directory, test_directory, test_suite_paths) + return test_suite_paths + + +static func normalize_path(path: String) -> String: + return path.replace("///", "//") + + +static func match_test_directory(directory: String, test_directory: String) -> bool: + return directory == test_directory or test_directory.is_empty() or test_directory == "/" or test_directory == "res://" diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid index 981c3f9b..0cca0d7f 100644 --- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid +++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid @@ -1 +1 @@ -uid://dkh40yvtnqoxw +uid://6teewr6pyk8 diff --git a/addons/gdUnit4/src/core/event/GdUnitEvent.gd b/addons/gdUnit4/src/core/event/GdUnitEvent.gd index d6c191a7..fb5a3a68 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEvent.gd +++ b/addons/gdUnit4/src/core/event/GdUnitEvent.gd @@ -22,6 +22,8 @@ enum { TESTCASE_AFTER, DISCOVER_START, DISCOVER_END, + SESSION_START, + SESSION_CLOSE } var _event_type: int @@ -61,9 +63,10 @@ func test_before(p_guid: GdUnitGUID) -> GdUnitEvent: return self -func test_after(p_guid: GdUnitGUID, p_statistics: Dictionary = {}, p_reports :Array[GdUnitReport] = []) -> GdUnitEvent: +func test_after(p_guid: GdUnitGUID, name: String, p_statistics: Dictionary = {}, p_reports :Array[GdUnitReport] = []) -> GdUnitEvent: _event_type = TESTCASE_AFTER _guid = p_guid + _test_name = name _statistics = p_statistics _reports = p_reports return self @@ -126,11 +129,11 @@ func resource_path() -> String: func is_success() -> bool: - return not is_warning() and not is_failed() and not is_error() and not is_skipped() + return not is_failed() and not is_error() func is_warning() -> bool: - return _statistics.get(WARNINGS, false) + return _statistics.get(WARNINGS, false) or orphan_nodes() > 0 func is_failed() -> bool: diff --git a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid index 549a202f..c32bd751 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid @@ -1 +1 @@ -uid://0bl5sfv34mjl +uid://vnexekqa287m diff --git a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid index 919c791c..e64c08af 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid @@ -1 +1 @@ -uid://bc6lj1xsoonhx +uid://drqsjg7ktta7m diff --git a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid index db9b4a23..aba89594 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid @@ -1 +1 @@ -uid://bsj6l1y8mebur +uid://dde6luj83tacs diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid index 141a50b2..da0298e9 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid @@ -1 +1 @@ -uid://bpepiow1roltr +uid://mpo0xiy8ucer diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid index 0f87c131..b3a12dd0 100644 --- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid +++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid @@ -1 +1 @@ -uid://cvawxva8c1imy +uid://ww5jm4a54i7j diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd new file mode 100644 index 00000000..52dab3ff --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd @@ -0,0 +1,6 @@ +class_name GdUnitSessionClose +extends GdUnitEvent + + +func _init() -> void: + _event_type = SESSION_CLOSE diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid new file mode 100644 index 00000000..a53e6d5b --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionClose.gd.uid @@ -0,0 +1 @@ +uid://s3ha3n44xqf7 diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd new file mode 100644 index 00000000..420ad538 --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd @@ -0,0 +1,6 @@ +class_name GdUnitSessionStart +extends GdUnitEvent + + +func _init() -> void: + _event_type = SESSION_START diff --git a/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid new file mode 100644 index 00000000..9c95b065 --- /dev/null +++ b/addons/gdUnit4/src/core/event/GdUnitSessionStart.gd.uid @@ -0,0 +1 @@ +uid://bikkn40kvpbd1 diff --git a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd index 4f0e6025..844561b1 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd @@ -2,6 +2,14 @@ ## It contains all the necessary information about the executed stage, such as memory observers, reports, orphan monitor class_name GdUnitExecutionContext +enum GC_ORPHANS_CHECK { + NONE, + SUITE_HOOK_AFTER, + TEST_HOOK_AFTER, + TEST_CASE +} + + var _parent_context: GdUnitExecutionContext var _sub_context: Array[GdUnitExecutionContext] = [] var _orphan_monitor: GdUnitOrphanNodesMonitor @@ -16,20 +24,6 @@ var _flaky_test_check := GdUnitSettings.is_test_flaky_check_enabled() var _flaky_test_retries := GdUnitSettings.get_flaky_max_retries() -# execution states -var _is_calculated := false -var _is_success: bool -var _is_flaky: bool -var _is_skipped: bool -var _has_warnings: bool -var _has_failures: bool -var _has_errors: bool -var _failure_count := 0 -var _orphan_count := 0 -var _error_count := 0 -var _skipped_count := 0 - - var error_monitor: GodotGdErrorMonitor = null: get: if _parent_context != null: @@ -58,7 +52,11 @@ func _init(name: StringName, parent_context: GdUnitExecutionContext = null) -> v _parent_context = parent_context _timer = LocalTime.now() _orphan_monitor = GdUnitOrphanNodesMonitor.new(name) - _orphan_monitor.start() + + if parent_context != null: + parent_context._orphan_monitor.add_child_monitor(_orphan_monitor) + orphan_monitor_start() + _memory_observer = GdUnitMemoryObserver.new() _report_collector = GdUnitTestReportCollector.new() if parent_context != null: @@ -66,6 +64,8 @@ func _init(name: StringName, parent_context: GdUnitExecutionContext = null) -> v func dispose() -> void: + if test_suite != null: + test_suite.free() _timer = null _orphan_monitor = null _report_collector = null @@ -82,6 +82,11 @@ func dispose_sub_contexts() -> void: _sub_context.clear() +func terminate() -> void: + if test_case: + test_case.do_terminate() + + static func of(pe: GdUnitExecutionContext) -> GdUnitExecutionContext: var context := GdUnitExecutionContext.new(pe._test_case_name, pe) context._test_case_name = pe._test_case_name @@ -89,16 +94,9 @@ static func of(pe: GdUnitExecutionContext) -> GdUnitExecutionContext: return context -static func of_test_suite(p_test_suite: GdUnitTestSuite) -> GdUnitExecutionContext: - assert(p_test_suite, "test_suite is null") - var context := GdUnitExecutionContext.new(p_test_suite.get_name()) - context.test_suite = p_test_suite - return context - - static func of_test_case(pe: GdUnitExecutionContext, p_test_case: _TestCase) -> GdUnitExecutionContext: assert(p_test_case, "test_case is null") - var context := GdUnitExecutionContext.new(p_test_case.get_name(), pe) + var context := GdUnitExecutionContext.new(p_test_case.test_name(), pe) context.test_case = p_test_case return context @@ -129,7 +127,7 @@ func error_monitor_start() -> void: func error_monitor_stop() -> void: - await error_monitor.scan() + error_monitor.stop() for error_report in error_monitor.to_reports(): if error_report.is_error(): _report_collector.push_back(error_report) @@ -139,12 +137,17 @@ func orphan_monitor_start() -> void: _orphan_monitor.start() +func orphan_monitor_collect() -> void: + _orphan_monitor.collect() + + func orphan_monitor_stop() -> void: _orphan_monitor.stop() -func add_report(report: GdUnitReport) -> void: +func add_report(report: GdUnitReport) -> GdUnitReport: _report_collector.push_back(report) + return report func reports() -> Array[GdUnitReport]: @@ -154,144 +157,61 @@ func reports() -> Array[GdUnitReport]: func collect_reports(recursive: bool) -> Array[GdUnitReport]: if not recursive: return reports() - var current_reports := reports() + # we combine the reports of test_before(), test_after() and test() to be reported by `fire_test_ended` + # we strictly need to copy the reports before adding sub context reports to avoid manipulation of the current context + var current_reports := reports().duplicate() for sub_context in _sub_context: - current_reports.append_array(sub_context.reports()) - # needs finally to clean the test reports to avoid counting twice - sub_context.reports().clear() - return current_reports + current_reports.append_array(sub_context.collect_reports(true)) + return current_reports -func collect_orphans(p_reports: Array[GdUnitReport]) -> int: - var orphans := 0 - if not _sub_context.is_empty(): - orphans += collect_testcase_orphan_reports(_sub_context[0], p_reports) - orphans += collect_teststage_orphan_reports(p_reports) - return orphans - - -func collect_testcase_orphan_reports(context: GdUnitExecutionContext, p_reports: Array[GdUnitReport]) -> int: - var orphans := context.count_orphans() - if orphans > 0: - p_reports.push_front(GdUnitReport.new()\ - .create(GdUnitReport.WARN, context.test_case.line_number(), GdAssertMessages.orphan_detected_on_test(orphans))) - return orphans - - -func collect_teststage_orphan_reports(p_reports: Array[GdUnitReport]) -> int: - var orphans := count_orphans() - if orphans > 0: - p_reports.push_front(GdUnitReport.new()\ - .create(GdUnitReport.WARN, test_case.line_number(), GdAssertMessages.orphan_detected_on_test_setup(orphans))) - return orphans - - -func build_reports(recursive:= true) -> Array[GdUnitReport]: - var collected_reports: Array[GdUnitReport] = collect_reports(recursive) - if recursive: - _orphan_count = collect_orphans(collected_reports) - else: - _orphan_count = count_orphans() - if _orphan_count > 0: - collected_reports.push_front(GdUnitReport.new() \ - .create(GdUnitReport.WARN, 1, GdAssertMessages.orphan_detected_on_suite_setup(_orphan_count))) - _is_skipped = is_skipped() - _skipped_count = count_skipped(recursive) - _is_success = is_success() - _is_flaky = is_flaky() - _has_warnings = has_warnings() - _has_errors = has_errors() - _error_count = count_errors(recursive) - if !_is_success: - _has_failures = has_failures() - _failure_count = count_failures(recursive) - _is_calculated = true - return collected_reports - - -# Evaluates the actual test case status by validate latest execution state (cold be more based on flaky max retry count) -func evaluate_test_retry_status() -> bool: - # get latest test execution status - var last_test_status :GdUnitExecutionContext = _sub_context.back() - _is_skipped = last_test_status.is_skipped() - _skipped_count = last_test_status.count_skipped(false) - _is_success = last_test_status.is_success() - # if success but it have more than one sub contexts the test was rerurn becouse of failures and will be marked as flaky - _is_flaky = _is_success and _sub_context.size() > 1 - _has_warnings = last_test_status.has_warnings() - _has_errors = last_test_status.has_errors() - _error_count = last_test_status.count_errors(false) - _has_failures = last_test_status.has_failures() - _failure_count = last_test_status.count_failures(false) - _orphan_count = last_test_status.collect_orphans(collect_reports(false)) - _is_calculated = true - # finally cleanup the retry execution contexts - dispose_sub_contexts() - return _is_success +func calculate_statistics(reports_: Array[GdUnitReport]) -> Dictionary: + var failed_count := GdUnitTestReportCollector.count_failures(reports_) + var error_count := GdUnitTestReportCollector.count_errors(reports_) + var warn_count := GdUnitTestReportCollector.count_warnings(reports_) + var skip_count := GdUnitTestReportCollector.count_skipped(reports_) + var orphan_count := GdUnitTestReportCollector.count_orphans(reports_) + var is_failed := !is_success() + var elapsed_time := _timer.elapsed_since_ms() + var retries := 1 if _parent_context == null else _sub_context.size() + # Mark as flaky if it is successful, but errors were counted + var is_flaky := retries > 1 and not is_failed + # In the case of a flakiness test, we do not report an error counter, as an unreliable test is considered successful + # after a certain number of repetitions. + if is_flaky: + failed_count = 0 -func get_execution_statistics() -> Dictionary: return { - GdUnitEvent.RETRY_COUNT: _test_execution_iteration, - GdUnitEvent.ORPHAN_NODES: _orphan_count, - GdUnitEvent.ELAPSED_TIME: _timer.elapsed_since_ms(), - GdUnitEvent.FAILED: !_is_success, - GdUnitEvent.ERRORS: _has_errors, - GdUnitEvent.WARNINGS: _has_warnings, - GdUnitEvent.FLAKY: _is_flaky, - GdUnitEvent.SKIPPED: _is_skipped, - GdUnitEvent.FAILED_COUNT: _failure_count, - GdUnitEvent.ERROR_COUNT: _error_count, - GdUnitEvent.SKIPPED_COUNT: _skipped_count + GdUnitEvent.RETRY_COUNT: retries, + GdUnitEvent.ELAPSED_TIME: elapsed_time, + GdUnitEvent.FAILED: is_failed, + GdUnitEvent.ERRORS: error_count > 0, + GdUnitEvent.WARNINGS: warn_count > 0, + GdUnitEvent.FLAKY: is_flaky, + GdUnitEvent.SKIPPED: skip_count > 0, + GdUnitEvent.FAILED_COUNT: failed_count, + GdUnitEvent.ERROR_COUNT: error_count, + GdUnitEvent.SKIPPED_COUNT: skip_count, + GdUnitEvent.ORPHAN_NODES: orphan_count, } -func has_failures() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_failures if c._is_calculated else c.has_failures()) - or _report_collector.has_failures() - ) - - -func has_errors() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_errors if c._is_calculated else c.has_errors()) - or _report_collector.has_errors() - ) - - -func has_warnings() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._has_warnings if c._is_calculated else c.has_warnings()) - or _report_collector.has_warnings() - ) - - -func is_flaky() -> bool: - return ( - _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._is_flaky if c._is_calculated else c.is_flaky()) - or _test_execution_iteration > 1 - ) - - func is_success() -> bool: if _sub_context.is_empty(): - return not has_failures() + return not _report_collector.has_failures() + # we on test suite level? + if _parent_context == null: + return not _report_collector.has_failures() - var failed_context := _sub_context.filter(func(c :GdUnitExecutionContext) -> bool: - return !(c._is_success if c._is_calculated else c.is_success())) - return failed_context.is_empty() and not has_failures() + return _sub_context[-1].is_success() and not _report_collector.has_failures() func is_skipped() -> bool: return ( _sub_context.any(func(c :GdUnitExecutionContext) -> bool: - return c._is_skipped if c._is_calculated else c.is_skipped()) + return c.is_skipped()) or test_case.is_skipped() if test_case != null else false ) @@ -300,43 +220,12 @@ func is_interupted() -> bool: return false if test_case == null else test_case.is_interupted() -func count_failures(recursive: bool) -> int: - if not recursive: - return _report_collector.count_failures() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_failures(recursive)).reduce(sum, _report_collector.count_failures()) - - -func count_errors(recursive: bool) -> int: - if not recursive: - return _report_collector.count_errors() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_errors(recursive)).reduce(sum, _report_collector.count_errors()) - - -func count_skipped(recursive: bool) -> int: - if not recursive: - return _report_collector.count_skipped() - return _sub_context\ - .map(func(c :GdUnitExecutionContext) -> int: - return c.count_skipped(recursive)).reduce(sum, _report_collector.count_skipped()) - - -func count_orphans() -> int: - var orphans := 0 - for c in _sub_context: - orphans += c._orphan_monitor.orphan_nodes() - return _orphan_monitor.orphan_nodes() - orphans - - func sum(accum: int, number: int) -> int: return accum + number func retry_execution() -> bool: - var retry := _test_execution_iteration < 1 if not _flaky_test_check else _test_execution_iteration < _flaky_test_retries + var retry := _test_execution_iteration < 1 if not _flaky_test_check else _test_execution_iteration < _flaky_test_retries if retry: _test_execution_iteration += 1 return retry @@ -346,9 +235,40 @@ func register_auto_free(obj: Variant) -> Variant: return _memory_observer.register_auto_free(obj) -## Runs the gdunit garbage collector to free registered object -func gc() -> void: +## Runs the gdunit garbage collector to free registered object and handle orphan node reporting +func gc(gc_orphan_check: GC_ORPHANS_CHECK = GC_ORPHANS_CHECK.NONE) -> void: # unreference last used assert form the test to prevent memory leaks GdUnitThreadManager.get_current_context().clear_assert() await _memory_observer.gc() orphan_monitor_stop() + + match(gc_orphan_check): + GC_ORPHANS_CHECK.SUITE_HOOK_AFTER: + _orphan_monitor.collect() + var orphan_infos := _orphan_monitor.detected_orphans() + if orphan_infos.is_empty(): + return + reports().push_front(GdUnitReport.new() \ + .create(GdUnitReport.ORPHAN, 1, GdAssertMessages.orphan_detected_on_suite_setup(orphan_infos)) + .with_current_value(orphan_infos.size())) + + GC_ORPHANS_CHECK.TEST_HOOK_AFTER: + _orphan_monitor.collect() + var orphans := _orphan_monitor.detected_orphans() + if not orphans.is_empty(): + reports().push_front(GdUnitReport.new()\ + .create(GdUnitReport.ORPHAN, 1, GdAssertMessages.orphan_detected_on_test_setup(orphans)) + .with_current_value(orphans.size())) + + GC_ORPHANS_CHECK.TEST_CASE: + var orphans := _orphan_monitor.detected_orphans() + if orphans.is_empty(): + var orphans_count := _orphan_monitor.orphans_count() + if orphans_count > 0: + reports().push_front(GdUnitReport.new() \ + .create(GdUnitReport.ORPHAN, test_case.line_number(), GdAssertMessages.orphan_warning(orphans_count)) + .with_current_value(orphans_count)) + else: + reports().push_front(GdUnitReport.new()\ + .create(GdUnitReport.ORPHAN, test_case.line_number(), GdAssertMessages.orphan_detected_on_test(orphans)) + .with_current_value(orphans.size())) diff --git a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid index 20171dfc..a44d3d71 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid @@ -1 +1 @@ -uid://d3efv548pfjn3 +uid://xm40igocd208 diff --git a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid index 272d4ebb..1aa63e0e 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid @@ -1 +1 @@ -uid://befegid5bg08s +uid://clbjbosh72d76 diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd index 62c4b02f..91247bd0 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd @@ -22,20 +22,35 @@ static func __filter_is_skipped(report :GdUnitReport) -> bool: return report.is_skipped() -func count_failures() -> int: - return _reports.filter(__filter_is_failure).size() +static func __filter_is_orphan(report: GdUnitReport) -> bool: + return report.is_orphan() -func count_errors() -> int: - return _reports.filter(__filter_is_error).size() +static func count_failures(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_failure).size() -func count_warnings() -> int: - return _reports.filter(__filter_is_warning).size() +static func count_errors(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_error).size() -func count_skipped() -> int: - return _reports.filter(__filter_is_skipped).size() +static func count_warnings(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_warning).size() + + +static func count_skipped(reports_: Array[GdUnitReport]) -> int: + return reports_.filter(__filter_is_skipped).size() + + +static func count_orphans(reports_: Array[GdUnitReport]) -> int: + var orphan_reports := reports_.filter(__filter_is_orphan) + if orphan_reports.is_empty(): + return 0 + ## Collect orphan count from the reports + var orphans := 0 + for report: GdUnitReport in orphan_reports: + orphans += report._current_value + return orphans func has_failures() -> bool: diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid index 009974b7..594f795e 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid @@ -1 +1 @@ -uid://r6m4c3af0jvf +uid://e8ycfgdtoxdr diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd index 1e1c08f9..dfb4441e 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd +++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd @@ -6,37 +6,50 @@ class_name GdUnitTestSuiteExecutor @warning_ignore("unused_private_class_variable") var _assertions := GdUnitAssertions.new() var _executeStage := GdUnitTestSuiteExecutionStage.new() +var _debug_mode : bool +var _terminated := false - -func _init(debug_mode :bool = false) -> void: +func _init(debug_mode: bool = false) -> void: _executeStage.set_debug_mode(debug_mode) + _debug_mode = debug_mode + GdUnitSignals.instance().gdunit_test_session_terminate.connect(_on_testsession_terminated) + + +func _on_testsession_terminated() -> void: + _terminated = true + GdUnitThreadManager.interrupt() + +func run_and_wait(tests: Array[GdUnitTestCase]) -> void: + if !_debug_mode: + GdUnitSignals.instance().gdunit_event.emit(GdUnitInit.new()) -func execute(test_suite :GdUnitTestSuite) -> void: var orphan_detection_enabled := GdUnitSettings.is_verbose_orphans() if not orphan_detection_enabled: prints("!!! Reporting orphan nodes is disabled. Please check GdUnit settings.") - (Engine.get_main_loop() as SceneTree).root.call_deferred("add_child", test_suite) - await (Engine.get_main_loop() as SceneTree).process_frame - await _executeStage.execute(GdUnitExecutionContext.of_test_suite(test_suite)) - - -func run_and_wait(tests: Array[GdUnitTestCase]) -> void: - # first we group all tests by his parent suite + # first we group all tests by resource path var grouped_by_suites := GdArrayTools.group_by(tests, func(test: GdUnitTestCase) -> String: - return test.source_file + return test.suite_resource_path ) var scanner := GdUnitTestSuiteScanner.new() for suite_path: String in grouped_by_suites.keys(): + if _terminated: + break @warning_ignore("unsafe_call_argument") var suite_tests: Array[GdUnitTestCase] = Array(grouped_by_suites[suite_path], TYPE_OBJECT, "RefCounted", GdUnitTestCase) var script := GdUnitTestSuiteScanner.load_with_disabled_warnings(suite_path) if script.get_class() == "GDScript": + var context := GdUnitExecutionContext.new(suite_path) var test_suite := scanner.load_suite(script as GDScript, suite_tests) - await execute(test_suite) + context.test_suite = test_suite + (Engine.get_main_loop() as SceneTree).root.add_child(test_suite) + await _executeStage.execute(context) + context.dispose() else: await GdUnit4CSharpApiLoader.execute(suite_tests) + if !_debug_mode: + GdUnitSignals.instance().gdunit_event.emit(GdUnitStop.new()) func fail_fast(enabled :bool) -> void: diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid index a893e9b8..9f3f1af1 100644 --- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid +++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid @@ -1 +1 @@ -uid://byu4stkw4742q +uid://cnmdoiqcxm6x2 diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd index 3515d721..f106f217 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd @@ -18,30 +18,5 @@ func _execute(context: GdUnitExecutionContext) -> void: @warning_ignore("redundant_await") await test_suite.after_test() - await context.gc() - await context.error_monitor_stop() - - var reports := context.build_reports() - - if context.is_skipped(): - fire_test_skipped(context) - else: - fire_event(GdUnitEvent.new().test_after(context.test_case.id(), context.get_execution_statistics(), reports)) - - -func fire_test_skipped(context: GdUnitExecutionContext) -> void: - var test_case := context.test_case - var statistics := { - GdUnitEvent.ORPHAN_NODES: 0, - GdUnitEvent.ELAPSED_TIME: 0, - GdUnitEvent.WARNINGS: false, - GdUnitEvent.ERRORS: false, - GdUnitEvent.ERROR_COUNT: 0, - GdUnitEvent.FAILED: false, - GdUnitEvent.FAILED_COUNT: 0, - GdUnitEvent.SKIPPED: true, - GdUnitEvent.SKIPPED_COUNT: 1, - } - var report := GdUnitReport.new() \ - .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) - fire_event(GdUnitEvent.new().test_after(test_case.id(), statistics, [report])) + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_HOOK_AFTER) + context.error_monitor_stop() diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid index 11b9385b..cfd7bf63 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid @@ -1 +1 @@ -uid://s5oftq7357lm +uid://djgp3qbfrd77l diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd index 8d22c100..d7790a9c 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd @@ -10,11 +10,11 @@ func _init(call_stage := true) -> void: _call_stage = call_stage -func _execute(context :GdUnitExecutionContext) -> void: +func _execute(context: GdUnitExecutionContext) -> void: var test_suite := context.test_suite - fire_event(GdUnitEvent.new().test_before(context.test_case.id())) if _call_stage: @warning_ignore("redundant_await") await test_suite.before_test() + context.error_monitor_start() diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid index 9e433536..5395966d 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid @@ -1 +1 @@ -uid://xpwskm1g6xa7 +uid://b2xg3wcwup0fg diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd index 12cc6fde..473f1e2e 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd @@ -24,7 +24,7 @@ func _execute(context :GdUnitExecutionContext) -> void: await _stage_single_test.execute(context) await context.gc() - await context.error_monitor_stop() + context.error_monitor_stop() # finally free the test instance if is_instance_valid(context.test_case): diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid index d6cbf4bb..a5440282 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid @@ -1 +1 @@ -uid://d3ih04g8adile +uid://bu8xjd1fjw1nr diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd index a0f8ef19..03bbd0f7 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd @@ -12,16 +12,18 @@ func _execute(context :GdUnitExecutionContext) -> void: @warning_ignore("redundant_await") await test_suite.after() - await context.gc() - var reports := context.build_reports(false) + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.SUITE_HOOK_AFTER) + + var reports := context.collect_reports(false) + var statistics := context.calculate_statistics(reports) fire_event(GdUnitEvent.new()\ .suite_after(context.get_test_suite_path(),\ test_suite.get_name(), - context.get_execution_statistics(), + statistics, reports)) - GdUnitFileAccess.clear_tmp() # Guard that checks if all doubled (spy/mock) objects are released - GdUnitClassDoubler.check_leaked_instances() + await GdUnitClassDoubler.check_leaked_instances() # we hide the scene/main window after runner is finished - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid index 5d7ec4bd..500a015f 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid @@ -1 +1 @@ -uid://bk7tpgycds4bi +uid://dtmmh0emgqcuw diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd index e9fa7186..a6ed8c5f 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd @@ -4,7 +4,7 @@ class_name GdUnitTestSuiteBeforeStage extends IGdUnitExecutionStage -func _execute(context :GdUnitExecutionContext) -> void: +func _execute(context: GdUnitExecutionContext) -> void: var test_suite := context.test_suite fire_event(GdUnitEvent.new()\ diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid index a1153692..cc44954e 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid @@ -1 +1 @@ -uid://dqowxiqnu8fdt +uid://3i2k3bb7d1vt diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd index 5b86b854..07e3779d 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd @@ -28,9 +28,10 @@ func _execute(context :GdUnitExecutionContext) -> void: if not is_instance_valid(test_case): continue context.test_suite.set_active_test_case(test_case.test_name()) - await _stage_test.execute(GdUnitExecutionContext.of_test_case(context, test_case)) + var test_case_context := GdUnitExecutionContext.of_test_case(context, test_case) + await _stage_test.execute(test_case_context) # stop on first error or if fail fast is enabled - if _fail_fast and not context.is_success(): + if test_case.is_terminated() or (_fail_fast and not test_case_context.is_success()): break if test_case.is_interupted(): # it needs to go this hard way to kill the outstanding awaits of a test case when the test timed out @@ -39,9 +40,8 @@ func _execute(context :GdUnitExecutionContext) -> void: context.test_suite = await clone_test_suite(context.test_suite) await _stage_after.execute(context) GdUnitMemoryObserver.unguard_instance(context.test_suite.__awaiter) + await (Engine.get_main_loop() as SceneTree).process_frame - context.test_suite.free() - context.dispose() # clones a test suite and moves the test cases to new instance @@ -98,7 +98,8 @@ func fire_test_suite_skipped(context :GdUnitExecutionContext) -> void: continue var test_case_context := GdUnitExecutionContext.of_test_case(context, test_case) fire_event(GdUnitEvent.new().test_before(test_case.id())) - fire_test_skipped(test_case_context) + # use skip count 0 because we counted it over the complete test suite + fire_test_skipped(test_case_context, 0) var statistics := { @@ -117,7 +118,7 @@ func fire_test_suite_skipped(context :GdUnitExecutionContext) -> void: await (Engine.get_main_loop() as SceneTree).process_frame -func fire_test_skipped(context: GdUnitExecutionContext) -> void: +func fire_test_skipped(context: GdUnitExecutionContext, skip_count := 1) -> void: var test_case := context.test_case var statistics := { GdUnitEvent.ORPHAN_NODES: 0, @@ -128,11 +129,11 @@ func fire_test_skipped(context: GdUnitExecutionContext) -> void: GdUnitEvent.FAILED: false, GdUnitEvent.FAILED_COUNT: 0, GdUnitEvent.SKIPPED: true, - GdUnitEvent.SKIPPED_COUNT: 1, + GdUnitEvent.SKIPPED_COUNT: skip_count, } var report := GdUnitReport.new() \ .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped("Skipped from the entire test suite")) - fire_event(GdUnitEvent.new().test_after(test_case.id(), statistics, [report])) + fire_event(GdUnitEvent.new().test_after(test_case.id(), test_case.test_name(), statistics, [report])) func set_debug_mode(debug_mode :bool = false) -> void: diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid index a65d54cb..54ee90b0 100644 --- a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid @@ -1 +1 @@ -uid://c4bbcbwcgesbb +uid://bgcyb2wuvnslx diff --git a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd index 39de3809..a04f7e78 100644 --- a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd @@ -1,7 +1,7 @@ ## The interface of execution stage.[br] ## An execution stage is defined as an encapsulated task that can execute 1-n substages covered by its own execution context.[br] ## Execution stage are always called synchronously. -class_name IGdUnitExecutionStage +@abstract class_name IGdUnitExecutionStage extends RefCounted var _debug_mode := false @@ -13,14 +13,14 @@ var _debug_mode := false ## # waits for 100ms ## await MyExecutionStage.new().execute() ## [/codeblock][br] -func execute(context :GdUnitExecutionContext) -> void: +func execute(context: GdUnitExecutionContext) -> void: GdUnitThreadManager.get_current_context().set_execution_context(context) @warning_ignore("redundant_await") await _execute(context) ## Sends the event to registered listeners -func fire_event(event :GdUnitEvent) -> void: +func fire_event(event: GdUnitEvent) -> void: if _debug_mode: GdUnitSignals.instance().gdunit_event_debug.emit(event) else: @@ -29,11 +29,9 @@ func fire_event(event :GdUnitEvent) -> void: ## Internal testing stuff.[br] ## Sets the executor into debug mode to emit `GdUnitEvent` via signal `gdunit_event_debug` -func set_debug_mode(debug_mode :bool) -> void: +func set_debug_mode(debug_mode: bool) -> void: _debug_mode = debug_mode ## The execution phase to be carried out. -func _execute(_context :GdUnitExecutionContext) -> void: - @warning_ignore("assert_always_false") - assert(false, "The execution stage is not implemented") +@abstract func _execute(context: GdUnitExecutionContext) -> void diff --git a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid index 5b7733a3..3b0f1be1 100644 --- a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid @@ -1 +1 @@ -uid://c8homl26jnl3 +uid://coqq8h8ks3n7r diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd index 243b889b..c1e09241 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd @@ -8,6 +8,8 @@ var _stage_test :IGdUnitExecutionStage = GdUnitTestCaseFuzzedTestStage.new() func _execute(context :GdUnitExecutionContext) -> void: + fire_event(GdUnitEvent.new().test_before(context.test_case.id())) + while context.retry_execution(): var test_context := GdUnitExecutionContext.of(context) await _stage_before.execute(test_context) @@ -16,12 +18,35 @@ func _execute(context :GdUnitExecutionContext) -> void: await _stage_after.execute(test_context) if test_context.is_success() or test_context.is_skipped() or test_context.is_interupted(): break - @warning_ignore("return_value_discarded") - context.evaluate_test_retry_status() + context.gc() + if context.is_skipped(): + fire_test_skipped(context) + else: + var reports: = context.collect_reports(true) + var statistics := context.calculate_statistics(reports) + fire_event(GdUnitEvent.new().test_after(context.test_case.id(), context.test_case.test_name(), statistics, reports)) func set_debug_mode(debug_mode :bool = false) -> void: super.set_debug_mode(debug_mode) _stage_before.set_debug_mode(debug_mode) _stage_after.set_debug_mode(debug_mode) _stage_test.set_debug_mode(debug_mode) + + +func fire_test_skipped(context: GdUnitExecutionContext) -> void: + var test_case := context.test_case + var statistics := { + GdUnitEvent.ORPHAN_NODES: 0, + GdUnitEvent.ELAPSED_TIME: 0, + GdUnitEvent.WARNINGS: false, + GdUnitEvent.ERRORS: false, + GdUnitEvent.ERROR_COUNT: 0, + GdUnitEvent.FAILED: false, + GdUnitEvent.FAILED_COUNT: 0, + GdUnitEvent.SKIPPED: true, + GdUnitEvent.SKIPPED_COUNT: 1, + } + var report := GdUnitReport.new() \ + .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) + fire_event(GdUnitEvent.new().test_after(test_case.id(), test_case.test_name(), statistics, [report])) diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid index 35baec26..0f5728cf 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd.uid @@ -1 +1 @@ -uid://dwnhibi8y605u +uid://drib5h88hjrb2 diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd index d32c28ed..62dda37e 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd @@ -33,7 +33,7 @@ func _execute(context :GdUnitExecutionContext) -> void: reports.append(GdUnitReport.new() \ .create(GdUnitReport.FAILURE, report.line_number(), GdAssertMessages.fuzzer_interuped(iteration, report.message()))) break - await context.gc() + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_CASE) # unguard on fuzzers if not test_case.is_interupted(): diff --git a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid index 1f5ec810..500e9ab0 100644 --- a/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd.uid @@ -1 +1 @@ -uid://bwl53ncmxcqnf +uid://d10eorwfmh7n diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd index 775b8dbc..03f8fa53 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd @@ -9,6 +9,7 @@ var _stage_test :IGdUnitExecutionStage = GdUnitTestCaseSingleTestStage.new() func _execute(context :GdUnitExecutionContext) -> void: + fire_event(GdUnitEvent.new().test_before(context.test_case.id())) while context.retry_execution(): var test_context := GdUnitExecutionContext.of(context) await _stage_before.execute(test_context) @@ -17,8 +18,14 @@ func _execute(context :GdUnitExecutionContext) -> void: await _stage_after.execute(test_context) if test_context.is_success() or test_context.is_skipped() or test_context.is_interupted(): break - @warning_ignore("return_value_discarded") - context.evaluate_test_retry_status() + + context.gc() + if context.is_skipped(): + fire_test_skipped(context) + else: + var reports: = context.collect_reports(true) + var statistics := context.calculate_statistics(reports) + fire_event(GdUnitEvent.new().test_after(context.test_case.id(), context.test_case.test_name(), statistics, reports)) func set_debug_mode(debug_mode :bool = false) -> void: @@ -26,3 +33,21 @@ func set_debug_mode(debug_mode :bool = false) -> void: _stage_before.set_debug_mode(debug_mode) _stage_after.set_debug_mode(debug_mode) _stage_test.set_debug_mode(debug_mode) + + +func fire_test_skipped(context: GdUnitExecutionContext) -> void: + var test_case := context.test_case + var statistics := { + GdUnitEvent.ORPHAN_NODES: 0, + GdUnitEvent.ELAPSED_TIME: 0, + GdUnitEvent.WARNINGS: false, + GdUnitEvent.ERRORS: false, + GdUnitEvent.ERROR_COUNT: 0, + GdUnitEvent.FAILED: false, + GdUnitEvent.FAILED_COUNT: 0, + GdUnitEvent.SKIPPED: true, + GdUnitEvent.SKIPPED_COUNT: 1, + } + var report := GdUnitReport.new() \ + .create(GdUnitReport.SKIPPED, test_case.line_number(), GdAssertMessages.test_skipped(test_case.skip_info())) + fire_event(GdUnitEvent.new().test_after(test_case.id(), test_case.test_name(), statistics, [report])) diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid index 8aab3be1..8e7707ec 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd.uid @@ -1 +1 @@ -uid://dudj2wcynwlla +uid://bsdqk4cuynsvi diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd index 6882fe29..b8ace502 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd @@ -6,6 +6,6 @@ extends IGdUnitExecutionStage ## Executes a single test case 'test_()'.[br] ## It executes synchronized following stages[br] ## -> test_case() [br] -func _execute(context :GdUnitExecutionContext) -> void: +func _execute(context: GdUnitExecutionContext) -> void: await context.test_case.execute() - await context.gc() + await context.gc(GdUnitExecutionContext.GC_ORPHANS_CHECK.TEST_CASE) diff --git a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid index c826c879..e7c1d9af 100644 --- a/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid +++ b/addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd.uid @@ -1 +1 @@ -uid://5mf7vxiq00dn +uid://by0qv07pw8hou diff --git a/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd new file mode 100644 index 00000000..0f87ad1b --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd @@ -0,0 +1,78 @@ +class_name GdUnitBaseReporterTestSessionHook +extends GdUnitTestSessionHook + + +var test_session: GdUnitTestSession: + get: + return test_session + set(value): + # disconnect first possible connected listener + if test_session != null: + test_session.test_event.disconnect(_on_test_event) + # add listening to current session + test_session = value + if test_session != null: + test_session.test_event.connect(_on_test_event) + + +var _report_summary: GdUnitReportSummary +var _reporter: GdUnitTestReporter +var _report_writer: GdUnitReportWriter +var _report_converter: Callable + +func _init(report_writer: GdUnitReportWriter, hook_name: String, hook_description: String, report_converter: Callable) -> void: + super(hook_name, hook_description) + _reporter = GdUnitTestReporter.new() + _report_writer = report_writer + _report_converter = report_converter + + +func startup(session: GdUnitTestSession) -> GdUnitResult: + test_session = session + _report_summary = GdUnitReportSummary.new(_report_converter) + _reporter.init_summary() + + return GdUnitResult.success() + + +func shutdown(session: GdUnitTestSession) -> GdUnitResult: + var report_path := _report_writer.write(session.report_path, _report_summary) + session.send_message("Open {0} Report at: file://{1}".format([_report_writer.output_format(), report_path])) + + return GdUnitResult.success() + + +func _on_test_event(event: GdUnitEvent) -> void: + match event.type(): + GdUnitEvent.TESTSUITE_BEFORE: + _reporter.init_statistics() + _report_summary.add_testsuite_report(event.resource_path(), event.suite_name(), event.total_count()) + GdUnitEvent.TESTSUITE_AFTER: + var statistics := _reporter.build_test_suite_statisitcs(event) + _report_summary.update_testsuite_counters( + event.resource_path(), + _reporter.error_count(statistics), + _reporter.failed_count(statistics), + _reporter.orphan_nodes(statistics), + _reporter.skipped_count(statistics), + _reporter.flaky_count(statistics), + event.elapsed_time()) + _report_summary.add_testsuite_reports( + event.resource_path(), + event.reports() + ) + GdUnitEvent.TESTCASE_BEFORE: + var test := test_session.find_test_by_id(event.guid()) + _report_summary.add_testcase(test.source_file, test.suite_name, test.display_name) + GdUnitEvent.TESTCASE_AFTER: + _reporter.add_test_statistics(event) + var test := test_session.find_test_by_id(event.guid()) + _report_summary.set_counters(test.source_file, + test.display_name, + event.error_count(), + event.failed_count(), + event.orphan_nodes(), + event.is_skipped(), + event.is_flaky(), + event.elapsed_time()) + _report_summary.add_reports(test.source_file, test.display_name, event.reports()) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..7ca6de50 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitBaseReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://chx163t22olq6 diff --git a/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd new file mode 100644 index 00000000..4b8f390f --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd @@ -0,0 +1,9 @@ +class_name GdUnitHtmlReporterTestSessionHook +extends GdUnitBaseReporterTestSessionHook + +const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") + + +func _init() -> void: + super(GdUnitHtmlReportWriter.new(), "GdUnitHtmlTestReporter", "The Html test reporting hook.", GdUnitTools.richtext_normalize) + set_meta("SYSTEM_HOOK", true) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..5b4b2b76 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://d01k8pcbsnrx6 diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd new file mode 100644 index 00000000..23850e4a --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd @@ -0,0 +1,111 @@ +## @since GdUnit4 5.1.0 +## +## Base class for creating custom test session hooks in GdUnit4.[br] +## [br] +## [i]Test session hooks allow users to extend the GdUnit4 test framework by providing +## custom functionality that runs at specific points during the test execution lifecycle. +## This base class defines the interface that all test session hooks must implement.[/i] +## [br] +## [br] +## [b][u]Usage[/u][/b][br] +## 1. Create a new class that extends GdUnitTestSessionHook[br] +## 2. Override the required methods (startup, shutdown)[br] +## 3. Register your hook with the test engine (using the GdUnit4 settings dialog)[br] +## [br] +## [b][u]Example[/u][/b] +## [codeblock] +## class_name MyCustomTestHook +## extends GdUnitTestSessionHook +## +## func _init(): +## super("MyHook", "This is a description") +## +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Custom hook initialized") +## # Initialize resources, setup test environment, etc. +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Custom hook cleanup completed") +## # Cleanup resources, generate reports, etc. +## return GdUnitResult.success() +## [/codeblock] +## +## [b][u]Hook Lifecycle[/u][/b][br] +## 1. [i][b]Registration[/b][/i]: Hooks are registered with the test engine via settings dialog[br] +## 2. [i][b]Priority Sorting[/b][/i]: Hooks are sorted by priority[br] +## 3. [i][b]Startup[/b][/i]: startup() is called before test execution begins, if it returns an error is shown in the console[br] +## 4. [i][b]Test Execution[/b][/i]: Tests run normally (only if all hooks started successfully)[br] +## 5. [i][b]Shutdown[/b][/i]: shutdown() is called after all tests complete, regardless of startup success[br] +## [br] +## [b][u]Priority System[/u][/b][br] +## The priority system allows controlling the execution order of multiple hooks.[br] +## - The order can be changed in the GdUnit4 settings dialog.[br] +## - The priority of system hooks cannot be changed and they cannot be deleted.[br] +## [br] +## [b][u]Session Access[/u][/b][br] +## +## Both [i]startup()[/i] and [i]shutdown()[/i] methods receive a [GdUnitTestSession] parameter that provides:[br] +## - Access to test cases being executed[br] +## - Event emission capabilities for test progress tracking[br] +## - Message sending functionality for logging and communication[br] +class_name GdUnitTestSessionHook +extends RefCounted + + +## The display name of this hook. +var name: String: + get: + return name + + +## A detailed description of what this hook does. +var description: String: + get: + return description + + +## Initializes a new test session hook. +## +## [param _name] The display name for this hook +## [param _description] A detailed description of the hook's functionality +func _init(_name: String, _description: String) -> void: + self.name = _name + self.description = _description + + +## Called when the test session starts up, before any tests are executed.[br] +## [br] +## [color=yellow][i]This method should be overridden to implement custom initialization logic[/i][/color][br] +## [br] +## such as:[br] +## - Setting up test databases or external services[br] +## - Initializing mock objects or test fixtures[br] +## - Configuring logging or reporting systems[br] +## - Preparing the test environment[br] +## - Subscribing to test events via the session[br] +## [br] +## [param session] The test session instance providing access to test data and communication[br] +## [b]return:[/b] [code]GdUnitResult.success()[/code] if initialization succeeds, or [code]GdUnitResult.error("error")[/code] with +## an error message if initialization fails. +func startup(_session: GdUnitTestSession) -> GdUnitResult: + return GdUnitResult.error("%s:startup is not implemented" % get_script().resource_path) + + +## Called when the test session shuts down, after all tests have completed.[br] +## [br] +## [color=yellow][i]This method should be overridden to implement custom cleanup logic[/i][/color][br] +## [br] +## such as:[br] +## - Cleaning up test databases or external services[br] +## - Generating test reports or artifacts[br] +## - Releasing resources allocated during startup[br] +## - Performing final validation or assertions[br] +## - Processing collected test events and data[br] +## [br] +## [param session] The test session instance providing access to test results and communication[br] +## [b]return:[/b] [code]GdUnitResult.success()[/code] if cleanup succeeds, or [code]GdUnitResult.error("error")[/code] with +## an error message if cleanup fails. Cleanup errors are typically logged +## but don't prevent the test engine from shutting down. +func shutdown(_session: GdUnitTestSession) -> GdUnitResult: + return GdUnitResult.error("%s:shutdown is not implemented" % get_script().resource_path) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid new file mode 100644 index 00000000..f202ccf7 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://bxn3xb8hixolw diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd new file mode 100644 index 00000000..5a9bdcb8 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd @@ -0,0 +1,191 @@ +class_name GdUnitTestSessionHookService +extends Object + + +var enigne_hooks: Array[GdUnitTestSessionHook] = []: + get: + return enigne_hooks + set(value): + enigne_hooks.append(value) + + +var _save_settings: bool = false + + +static func instance() -> GdUnitTestSessionHookService: + return GdUnitSingleton.instance("GdUnitTestSessionHookService", func()->GdUnitTestSessionHookService: + GdUnitSignals.instance().gdunit_message.emit("Installing GdUnit4 session system hooks.") + var service := GdUnitTestSessionHookService.new() + # Register default system hooks here + service._save_settings = false + service.register(GdUnitHtmlReporterTestSessionHook.new()) + service.register(GdUnitXMLReporterTestSessionHook.new()) + service.load_hook_settings() + service._save_settings = true + return service + ) + + +static func contains_hook(current: GdUnitTestSessionHook, other: GdUnitTestSessionHook) -> bool: + return current.get_script().resource_path == other.get_script().resource_path + + +func find_custom(hook: GdUnitTestSessionHook) -> int: + for index in enigne_hooks.size(): + if contains_hook.call(enigne_hooks[index], hook): + return index + return -1 + + +func load_hook(hook_resourc_path: String) -> GdUnitResult: + if !FileAccess.file_exists(hook_resourc_path): + return GdUnitResult.error("The hook '%s' not exists." % hook_resourc_path) + var script: GDScript = load(hook_resourc_path) + if script.get_base_script() != GdUnitTestSessionHook: + return GdUnitResult.error("The hook '%s' must inhertit from 'GdUnitTestSessionHook'." % hook_resourc_path) + + return GdUnitResult.success(script.new()) + + +func enable_hook(hook: GdUnitTestSessionHook, enabled: bool) -> void: + _enable_hook(hook, enabled) + GdUnitSignals.instance().gdunit_message.emit("Session hook '{name}' {enabled}.".format({ + "name": hook.name, + "enabled": "enabled" if enabled else "disabled"}) + ) + save_hock_setttings() + + +func register(hook: GdUnitTestSessionHook, enabled: bool = true) -> GdUnitResult: + if find_custom(hook) != -1: + return GdUnitResult.error("A hook instance of '%s' is already registered." % hook.get_script().resource_path) + + _enable_hook(hook, enabled) + enigne_hooks.append(hook) + save_hock_setttings() + GdUnitSignals.instance().gdunit_message.emit("Session hook '%s' installed." % hook.name) + + return GdUnitResult.success() + + +func unregister(hook: GdUnitTestSessionHook) -> GdUnitResult: + var hook_index := find_custom(hook) + if hook_index == -1: + return GdUnitResult.error("The hook instance of '%s' is NOT registered." % hook.get_script().resource_path) + + enigne_hooks.remove_at(hook_index) + save_hock_setttings() + return GdUnitResult.success() + + +func move_before(hook: GdUnitTestSessionHook, before: GdUnitTestSessionHook) -> void: + var before_index := find_custom(before) + var hook_index := find_custom(hook) + + # Verify the hook to move is behind the hook to be moved + if before_index >= hook_index: + return + + enigne_hooks.remove_at(hook_index) + enigne_hooks.insert(before_index, hook) + save_hock_setttings() + + +func move_after(hook: GdUnitTestSessionHook, after: GdUnitTestSessionHook) -> void: + var after_index := find_custom(after) + var hook_index := find_custom(hook) + + # Verify the hook to move is before the hook to be moved + if after_index <= hook_index: + return + + enigne_hooks.remove_at(hook_index) + enigne_hooks.insert(after_index, hook) + save_hock_setttings() + + +func execute_startup(session: GdUnitTestSession) -> GdUnitResult: + return await execute("startup", session) + + +func execute_shutdown(session: GdUnitTestSession) -> GdUnitResult: + return await execute("shutdown", session, true) + + +func execute(hook_func: String, session: GdUnitTestSession, reverse := false) -> GdUnitResult: + var failed_hook_calls: Array[GdUnitResult] = [] + + for hook_index in enigne_hooks.size(): + var index := enigne_hooks.size()-hook_index-1 if reverse else hook_index + var hook: = enigne_hooks[index] + if not is_enabled(hook): + continue + if OS.is_stdout_verbose(): + GdUnitSignals.instance().gdunit_message.emit("Session hook '%s' > %s()" % [hook.name, hook_func]) + var result: GdUnitResult = await hook.call(hook_func, session) + if result == null: + failed_hook_calls.push_back(GdUnitResult.error("Result is null! Check '%s'" % hook.get_script().resource_path)) + elif result.is_error(): + failed_hook_calls.push_back(result) + + if failed_hook_calls.is_empty(): + return GdUnitResult.success() + + var errors := failed_hook_calls.map(func(result: GdUnitResult) -> String: + return "Hook call '%s' failed with error: '%s'" % [hook_func, result.error_message()] + ) + return GdUnitResult.error( "\n".join(errors)) + + +func save_hock_setttings() -> void: + if not _save_settings: + return + + var hooks_to_save: Dictionary[String, bool] = {} + for hook in enigne_hooks: + var enabled: bool = hook.get_meta("enabled") + hooks_to_save[hook.get_script().resource_path] = enabled + + GdUnitSettings.set_session_hooks(hooks_to_save) + + +func load_hook_settings() -> void: + var hooks_resource_paths := GdUnitSettings.get_session_hooks() + if hooks_resource_paths.is_empty(): + return + + for hock_path: String in hooks_resource_paths.keys(): + var enabled := hooks_resource_paths[hock_path] + + # Do not reinstall already installed hooks + var existing_hook: GdUnitTestSessionHook = enigne_hooks.filter(func(element: GdUnitTestSessionHook) -> bool: + return element.get_script().resource_path == hock_path + ).front() + # Applay enabled settings + if existing_hook != null: + _enable_hook(existing_hook, enabled) + continue + + # Load additional hooks + var result := load_hook(hock_path) + if result.is_error(): + push_error(result.error_message()) + continue + + GdUnitSignals.instance().gdunit_message.emit("Installing GdUnit4 session hooks.") + var hook: GdUnitTestSessionHook = result.value() + + result = register(hook, enabled) + if result.is_error(): + push_error(result.error_message()) + continue + + +static func is_enabled(hook: GdUnitTestSessionHook) -> bool: + if hook.has_meta("enabled"): + return hook.get_meta("enabled") + return true + + +func _enable_hook(hook: GdUnitTestSessionHook, enabled: bool) -> void: + hook.set_meta("enabled", enabled) diff --git a/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid new file mode 100644 index 00000000..1c40e9ca --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitTestSessionHookService.gd.uid @@ -0,0 +1 @@ +uid://ynimlj5urfoq diff --git a/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd new file mode 100644 index 00000000..94caef59 --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd @@ -0,0 +1,11 @@ +class_name GdUnitXMLReporterTestSessionHook +extends GdUnitBaseReporterTestSessionHook + + +func _init() -> void: + super(JUnitXmlReportWriter.new(), "GdUnitXMLTestReporter", "The JUnit XML test reporting hook.", convert_report_message) + set_meta("SYSTEM_HOOK", true) + + +func convert_report_message(value: String) -> String: + return value diff --git a/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid new file mode 100644 index 00000000..838e4a5f --- /dev/null +++ b/addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd.uid @@ -0,0 +1 @@ +uid://dujdufds2s0og diff --git a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid index 54e24d96..a8cdbc31 100644 --- a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid @@ -1 +1 @@ -uid://cd7lic1hrb5vf +uid://ce4ioj4vo7nbe diff --git a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd index 202926ea..f1b22440 100644 --- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd +++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd @@ -21,7 +21,7 @@ var _decoders := { TYPE_PACKED_COLOR_ARRAY: _on_type_Array.bind(TYPE_PACKED_COLOR_ARRAY), TYPE_PACKED_VECTOR2_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR2_ARRAY), TYPE_PACKED_VECTOR3_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR3_ARRAY), - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: _on_type_Array.bind(GdObjects.TYPE_PACKED_VECTOR4_ARRAY), + TYPE_PACKED_VECTOR4_ARRAY: _on_type_Array.bind(TYPE_PACKED_VECTOR4_ARRAY), TYPE_DICTIONARY: _on_type_Dictionary, TYPE_RID: _on_type_RID, TYPE_NODE_PATH: _on_type_NodePath, @@ -126,7 +126,7 @@ func _on_type_Array(value: Variant, type: int) -> String: return "PackedVector3Array()" return "PackedVector3Array([%s])" % ", ".join(vectors) - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: + TYPE_PACKED_VECTOR4_ARRAY: var vectors := PackedStringArray() for vector: Vector4 in value: @warning_ignore("return_value_discarded") @@ -255,6 +255,9 @@ static func decode(value: Variant) -> String: @warning_ignore("unsafe_cast") if GdArrayTools.is_type_array(type) and (value as Array).is_empty(): return "" + # For Variant types we need to determine the original type + if type == GdObjects.TYPE_VARIANT: + type = typeof(value) var decoder := _get_value_decoder(type) if decoder == null: push_error("No value decoder registered for type '%d'! Please open a Bug issue at 'https://github.com/MikeSchulze/gdUnit4/issues/new/choose'." % type) @@ -267,6 +270,9 @@ static func decode(value: Variant) -> String: static func decode_typed(type: int, value: Variant) -> String: if value == null: return "null" + # For Variant types we need to determine the original type + if type == GdObjects.TYPE_VARIANT: + type = typeof(value) var decoder := _get_value_decoder(type) if decoder == null: push_error("No value decoder registered for type '%d'! Please open a Bug issue at 'https://github.com/MikeSchulze/gdUnit4/issues/new/choose'." % type) diff --git a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid index 7a16a92d..61c35ceb 100644 --- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid @@ -1 +1 @@ -uid://bvcgb8ww4rpy1 +uid://bt2bkfkyvi0y7 diff --git a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd index 087cf408..bd38eb2a 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd +++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd @@ -4,7 +4,7 @@ extends RefCounted const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") const UNDEFINED: String = "<-NO_ARG->" -const ARG_PARAMETERIZED_TEST := "test_parameters" +const ARG_PARAMETERIZED_TEST := ["test_parameters", "_test_parameters"] static var _fuzzer_regex: RegEx static var _cleanup_leading_spaces: RegEx @@ -22,7 +22,7 @@ func _init(p_name: String, p_type: int, value: Variant = UNDEFINED, p_type_hint: _name = p_name _type = p_type _type_hint = p_type_hint - if value != null and p_name == ARG_PARAMETERIZED_TEST: + if value != null and p_name in ARG_PARAMETERIZED_TEST: _parameter_sets = _parse_parameter_set(str(value)) _default_value = value # is argument a fuzzer? @@ -42,7 +42,7 @@ func name() -> String: func default() -> Variant: - return GodotVersionFixures.convert(_default_value, _type) + return type_convert(_default_value, _type) func set_value(value: String) -> void: @@ -50,14 +50,14 @@ func set_value(value: String) -> void: if _type == GdObjects.TYPE_FUZZER: _default_value = value return - if _name == ARG_PARAMETERIZED_TEST: + if _name in ARG_PARAMETERIZED_TEST: _parameter_sets = _parse_parameter_set(value) _default_value = value return if _type == TYPE_NIL or _type == GdObjects.TYPE_VARIANT: _type = _extract_value_type(value) - if _type == GdObjects.TYPE_VARIANT: + if _type == GdObjects.TYPE_VARIANT and _default_value == null: _default_value = value if _default_value == null: match _type: @@ -111,7 +111,7 @@ func is_typed_array() -> bool: func is_parameter_set() -> bool: - return _name == ARG_PARAMETERIZED_TEST + return _name in ARG_PARAMETERIZED_TEST func parameter_sets() -> PackedStringArray: @@ -131,7 +131,7 @@ func _to_string() -> String: s += ": " + GdObjects.type_as_string(_type) if _type_hint != TYPE_NIL: s += "[%s]" % GdObjects.type_as_string(_type_hint) - if typeof(_default_value) != TYPE_STRING: + if has_default(): s += "=" + value_as_string() return s diff --git a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid index e8efb74a..ee42944d 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid @@ -1 +1 @@ -uid://c5iskcxtyerbi +uid://bvvuuvy7sm55w diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd index 515c92a5..7eae4b2f 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd +++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd @@ -87,7 +87,7 @@ func is_coroutine() -> bool: func is_parameterized() -> bool: for current in _args: var arg :GdFunctionArgument = current - if arg.name() == GdFunctionArgument.ARG_PARAMETERIZED_TEST: + if arg.name() in GdFunctionArgument.ARG_PARAMETERIZED_TEST: return true return false @@ -101,6 +101,8 @@ func return_type() -> int: func return_type_as_string() -> String: + if return_type() == TYPE_NIL: + return "void" if (return_type() == TYPE_OBJECT or return_type() == GdObjects.TYPE_ENUM) and not _return_class.is_empty(): return _return_class return GdObjects.type_as_string(return_type()) @@ -110,7 +112,17 @@ func set_argument_value(arg_name: String, value: String) -> void: var argument: GdFunctionArgument = _args.filter(func(arg: GdFunctionArgument) -> bool: return arg.name() == arg_name ).front() - argument.set_value(value) + if argument != null: + argument.set_value(value) + + +func enrich_arguments(arguments: Array[Dictionary]) -> void: + for arg_index: int in arguments.size(): + var arg: Dictionary = arguments[arg_index] + if arg["type"] != GdObjects.TYPE_VARARG: + var arg_name: String = arg["name"] + var arg_value: String = arg["value"] + set_argument_value(arg_name, arg_value) func enrich_file_info(p_source_path: String, p_line_number: int) -> void: @@ -225,10 +237,7 @@ static func _build_varargs(p_is_vararg :bool) -> Array[GdFunctionArgument]: var varargs_ :Array[GdFunctionArgument] = [] if not p_is_vararg: return varargs_ - # if function has vararg we need to handle this manually by adding 10 default arguments - var type := GdObjects.TYPE_VARARG - for index in 10: - varargs_.push_back(GdFunctionArgument.new("vararg%d_" % index, type, '"%s"' % GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE)) + varargs_.push_back(GdFunctionArgument.new("varargs", GdObjects.TYPE_VARARG, '')) return varargs_ diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid index 7edf93d8..32280d48 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid @@ -1 +1 @@ -uid://bbbvxjnaas2n8 +uid://c7tkbfjo3euru diff --git a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd index bb3a11ac..9c45e625 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd +++ b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd @@ -26,7 +26,7 @@ func _init(fd: GdFunctionDescriptor) -> void: func resolve_test_cases(script: GDScript) -> Array[GdUnitTestCase]: if not is_parameterized(): - return [GdUnitTestCase.from(_fd.source_path(), _fd.line_number(), _fd.name())] + return [GdUnitTestCase.from(script.resource_path, _fd.source_path(), _fd.line_number(), _fd.name())] return extract_test_cases_by_reflection(script) @@ -94,7 +94,7 @@ func extract_test_cases_by_reflection(script: GDScript) -> Array[GdUnitTestCase] # if no parameter set detected we need to resolve it by using reflection if parameter_sets.size() == 0: _is_static = false - return _extract_test_cases_by_reflection(source) + return _extract_test_cases_by_reflection(source, script) else: var test_cases: Array[GdUnitTestCase] = [] var property_names := _extract_property_names(source) @@ -102,7 +102,7 @@ func extract_test_cases_by_reflection(script: GDScript) -> Array[GdUnitTestCase] var parameter_set := parameter_sets[parameter_set_index] _static_sets_by_index[parameter_set_index] = _is_static_parameter_set(parameter_set, property_names) @warning_ignore("return_value_discarded") - test_cases.append(GdUnitTestCase.from(_fd.source_path(), _fd.line_number(), _fd.name(), parameter_set_index, parameter_set)) + test_cases.append(GdUnitTestCase.from(script.resource_path, _fd.source_path(), _fd.line_number(), _fd.name(), parameter_set_index, parameter_set)) parameter_set_index += 1 return test_cases @@ -122,13 +122,13 @@ func _is_static_parameter_set(parameters :String, property_names :PackedStringAr return true -func _extract_test_cases_by_reflection(source: Node) -> Array[GdUnitTestCase]: +func _extract_test_cases_by_reflection(source: Node, script: GDScript) -> Array[GdUnitTestCase]: var parameter_sets := load_parameter_sets(source) var test_cases: Array[GdUnitTestCase] = [] for index in parameter_sets.size(): var parameter_set := str(parameter_sets[index]) @warning_ignore("return_value_discarded") - test_cases.append(GdUnitTestCase.from(_fd.source_path(), _fd.line_number(), _fd.name(), index, parameter_set)) + test_cases.append(GdUnitTestCase.from(script.resource_path, _fd.source_path(), _fd.line_number(), _fd.name(), index, parameter_set)) return test_cases diff --git a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid index 183d61bd..aed276c9 100644 --- a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid @@ -1 +1 @@ -uid://bk704y2xw4nyd +uid://dhsdvuypp2woi diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd b/addons/gdUnit4/src/core/parse/GdScriptParser.gd index 83789361..a1025082 100644 --- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd +++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd @@ -3,19 +3,25 @@ extends RefCounted const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") -const ALLOWED_CHARACTERS := "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"" +const TYPE_VOID = GdObjects.TYPE_VOID +const TYPE_VARIANT = GdObjects.TYPE_VARIANT +const TYPE_VARARG = GdObjects.TYPE_VARARG +const TYPE_FUNC = GdObjects.TYPE_FUNC +const TYPE_FUZZER = GdObjects.TYPE_FUZZER +const TYPE_ENUM = GdObjects.TYPE_ENUM + var TOKEN_NOT_MATCH := Token.new("") var TOKEN_SPACE := SkippableToken.new(" ") var TOKEN_TABULATOR := SkippableToken.new("\t") var TOKEN_NEW_LINE := SkippableToken.new("\n") var TOKEN_COMMENT := SkippableToken.new("#") -var TOKEN_CLASS_NAME := Token.new("class_name") -var TOKEN_INNER_CLASS := Token.new("class") -var TOKEN_EXTENDS := Token.new("extends") -var TOKEN_ENUM := Token.new("enum") -var TOKEN_FUNCTION_STATIC_DECLARATION := Token.new("static func") -var TOKEN_FUNCTION_DECLARATION := Token.new("func") +var TOKEN_CLASS_NAME := RegExToken.new("class_name", GdUnitTools.to_regex("(class_name)\\s+([\\w\\p{L}\\p{N}_]+) (extends[a-zA-Z]+:)|(class_name)\\s+([\\w\\p{L}\\p{N}_]+)"), 5) +var TOKEN_INNER_CLASS := TokenInnerClass.new("class", GdUnitTools.to_regex("(class)\\s+(\\w\\p{L}\\p{N}_]+) (extends[a-zA-Z]+:)|(class)\\s+([\\w\\p{L}\\p{N}_]+)"), 5) +var TOKEN_EXTENDS := RegExToken.new("extends", GdUnitTools.to_regex("extends\\s+")) +var TOKEN_ENUM := RegExToken.new("enum", GdUnitTools.to_regex("enum\\s+")) +var TOKEN_FUNCTION_STATIC_DECLARATION := RegExToken.new("static func", GdUnitTools.to_regex("^static\\s+func\\s+([\\w\\p{L}\\p{N}_]+)"), 1) +var TOKEN_FUNCTION_DECLARATION := RegExToken.new("func", GdUnitTools.to_regex("^func\\s+([\\w\\p{L}\\p{N}_]+)"), 1) var TOKEN_FUNCTION := Token.new(".") var TOKEN_FUNCTION_RETURN_TYPE := Token.new("->") var TOKEN_FUNCTION_END := Token.new("):") @@ -23,6 +29,7 @@ var TOKEN_ARGUMENT_ASIGNMENT := Token.new("=") var TOKEN_ARGUMENT_TYPE_ASIGNMENT := Token.new(":=") var TOKEN_ARGUMENT_FUZZER := FuzzerToken.new(GdUnitTools.to_regex("((?!(fuzzer_(seed|iterations)))fuzzer?\\w+)( ?+= ?+| ?+:= ?+| ?+:Fuzzer ?+= ?+|)")) var TOKEN_ARGUMENT_TYPE := Token.new(":") +var TOKEN_ARGUMENT_VARIADIC := Token.new("...") var TOKEN_ARGUMENT_SEPARATOR := Token.new(",") var TOKEN_BRACKET_ROUND_OPEN := Token.new("(") var TOKEN_BRACKET_ROUND_CLOSE := Token.new(")") @@ -32,7 +39,6 @@ var TOKEN_BRACKET_CURLY_OPEN := Token.new("{") var TOKEN_BRACKET_CURLY_CLOSE := Token.new("}") - var OPERATOR_ADD := Operator.new("+") var OPERATOR_SUB := Operator.new("-") var OPERATOR_MUL := Operator.new("*") @@ -60,6 +66,7 @@ var TOKENS :Array[Token] = [ TOKEN_ARGUMENT_TYPE_ASIGNMENT, TOKEN_ARGUMENT_ASIGNMENT, TOKEN_ARGUMENT_TYPE, + TOKEN_ARGUMENT_VARIADIC, TOKEN_FUNCTION, TOKEN_ARGUMENT_SEPARATOR, TOKEN_FUNCTION_RETURN_TYPE, @@ -70,10 +77,10 @@ var TOKENS :Array[Token] = [ OPERATOR_REMAINDER, ] -var _regex_clazz_name := GdUnitTools.to_regex("(class) ([a-zA-Z0-9_]+) (extends[a-zA-Z]+:)|(class) ([a-zA-Z0-9_]+)") var _regex_strip_comments := GdUnitTools.to_regex("^([^#\"']|'[^']*'|\"[^\"]*\")*\\K#.*") var _scanned_inner_classes := PackedStringArray() var _script_constants := {} +var _is_awaiting := GdUnitTools.to_regex("\\bawait\\s+(?![^\"]*\"[^\"]*$)(?!.*#.*await)") static func to_unix_format(input :String) -> String: @@ -84,24 +91,18 @@ class Token extends RefCounted: var _token: String var _consumed: int var _is_operator: bool - var _regex :RegEx - - func _init(p_token: String, p_is_operator := false, p_regex :RegEx = null) -> void: + func _init(p_token: String, p_is_operator := false) -> void: _token = p_token _is_operator = p_is_operator _consumed = p_token.length() - _regex = p_regex func match(input: String, pos: int) -> bool: - if _regex: - var result := _regex.search(input, pos) - if result == null: - return false - _consumed = result.get_end() - result.get_start() - return pos == result.get_start() return input.findn(_token, pos) == pos + func value() -> Variant: + return _token + func is_operator() -> bool: return _is_operator @@ -122,8 +123,8 @@ class Token extends RefCounted: class Operator extends Token: - func _init(value: String) -> void: - super(value, true) + func _init(p_value: String) -> void: + super(p_value, true) func _to_string() -> String: return "OperatorToken{%s}" % [_token] @@ -139,38 +140,6 @@ class SkippableToken extends Token: return true -# Token to parse Fuzzers -class FuzzerToken extends Token: - var _name: String - - - func _init(regex: RegEx) -> void: - super("", false, regex) - - - func match(input: String, pos: int) -> bool: - if _regex: - var result := _regex.search(input, pos) - if result == null: - return false - _name = result.strings[1] - _consumed = result.get_end() - result.get_start() - return pos == result.get_start() - return input.findn(_token, pos) == pos - - - func name() -> String: - return _name - - - func type() -> int: - return GdObjects.TYPE_FUZZER - - - func _to_string() -> String: - return "FuzzerToken{%s: '%s'}" % [_name, _token] - - # Token to parse function arguments class Variable extends Token: var _plain_value :String @@ -231,12 +200,57 @@ class Variable extends Token: return "Variable{%s: %s : '%s'}" % [_plain_value, GdObjects.type_as_string(_type), _token] -class TokenInnerClass extends Token: - var _clazz_name :String +class RegExToken extends Token: + var _regex: RegEx + var _extract_group_index: int + var _value := "" + + + func _init(token: String, regex: RegEx, extract_group_index: int = -1) -> void: + super(token, false) + _regex = regex + _extract_group_index = extract_group_index + + + func match(input: String, pos: int) -> bool: + var matching := _regex.search(input, pos) + if matching == null or pos != matching.get_start(): + return false + if _extract_group_index != -1: + _value = matching.get_string(_extract_group_index) + _consumed = matching.get_end() - matching.get_start() + return true + + + func value() -> String: + return _value + + +# Token to parse Fuzzers +class FuzzerToken extends RegExToken: + + + func _init(regex: RegEx) -> void: + super("fuzzer", regex, 1) + + + func name() -> String: + return value() + + + func type() -> int: + return GdObjects.TYPE_FUZZER + + + func _to_string() -> String: + return "FuzzerToken{%s: '%s'}" % [value(), _token] + + +class TokenInnerClass extends RegExToken: var _content := PackedStringArray() - static func _strip_leading_spaces(input :String) -> String: + static func _strip_leading_spaces(input: String) -> String: var characters := input.to_utf8_buffer() while not characters.is_empty(): if characters[0] != 0x20: @@ -245,26 +259,26 @@ class TokenInnerClass extends Token: return characters.get_string_from_utf8() - static func _consumed_bytes(row :String) -> int: + static func _consumed_bytes(row: String) -> int: return row.replace(" ", "").replace(" ", "").length() - func _init(clazz_name :String) -> void: - super("class") - _clazz_name = clazz_name + func _init(token: String, p_regex: RegEx, extract_group_index: int = -1) -> void: + super(token, p_regex, extract_group_index) - func is_class_name(clazz_name :String) -> bool: - return _clazz_name == clazz_name + func is_class_name(clazz_name: String) -> bool: + return value() == clazz_name func content() -> PackedStringArray: return _content - func parse(source_rows :PackedStringArray, offset :int) -> void: + @warning_ignore_start("return_value_discarded") + func parse(source_rows: PackedStringArray, offset: int) -> void: # add class signature - @warning_ignore("return_value_discarded") + _content.clear() _content.append(source_rows[offset]) # parse class content for row_index in range(offset+1, source_rows.size()): @@ -277,22 +291,21 @@ class TokenInnerClass extends Token: source_row = source_row.trim_prefix("\t") # refomat invalid empty lines if source_row.dedent().is_empty(): - @warning_ignore("return_value_discarded") _content.append("") else: - @warning_ignore("return_value_discarded") _content.append(source_row) continue break _consumed += TokenInnerClass._consumed_bytes("".join(_content)) + @warning_ignore_restore("return_value_discarded") func _to_string() -> String: - return "TokenInnerClass{%s}" % [_clazz_name] + return "TokenInnerClass{%s}" % [value()] -func get_token(input :String, current_index :int) -> Token: +func get_token(input: String, current_index: int) -> Token: for t in TOKENS: if t.match(input, current_index): return t @@ -302,13 +315,12 @@ func get_token(input :String, current_index :int) -> Token: func next_token(input: String, current_index: int, ignore_tokens :Array[Token] = []) -> Token: var token := TOKEN_NOT_MATCH for t :Token in TOKENS.filter(func(t :Token) -> bool: return not ignore_tokens.has(t)): + if t.match(input, current_index): token = t break if token == OPERATOR_SUB: token = tokenize_value(input, current_index, token) - if token == TOKEN_INNER_CLASS: - token = tokenize_inner_class(input, current_index, token) if token == TOKEN_NOT_MATCH: return tokenize_value(input, current_index, token, ignore_tokens.has(TOKEN_FUNCTION)) return token @@ -325,7 +337,7 @@ func tokenize_value(input: String, current: int, token: Token, ignore_dots := fa # or allowend charset # or is a float value if (test_for_sign and next==0) \ - or character in ALLOWED_CHARACTERS \ + or is_allowed_character(character) \ or (character == "." and (ignore_dots or current_token.is_valid_int())): current_token += character next += 1 @@ -336,21 +348,29 @@ func tokenize_value(input: String, current: int, token: Token, ignore_dots := fa return TOKEN_NOT_MATCH -func extract_clazz_name(value :String) -> String: - var result := _regex_clazz_name.search(value) - if result == null: - push_error("Can't extract class name from '%s'" % value) - return "" - if result.get_string(2).is_empty(): - return result.get_string(5) - else: - return result.get_string(2) - - -@warning_ignore("unused_parameter") -func tokenize_inner_class(source_code: String, current: int, token: Token) -> Token: - var clazz_name := extract_clazz_name(source_code.substr(current, 64)) - return TokenInnerClass.new(clazz_name) +# const ALLOWED_CHARACTERS := "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"" +func is_allowed_character(input: String) -> bool: + var code_point := input.unicode_at(0) + # Unicode + if code_point > 127: + # This is a Unicode character (Chinese, Japanese, etc.) + return true + # ASCII digit 0-9 + if code_point >= 48 and code_point <= 57: + return true + # ASCII lowercase a-z + if code_point >= 97 and code_point <= 122: + return true + # ASCII uppercase A-Z + if code_point >= 65 and code_point <= 90: + return true + # underscore _ + if code_point == 95: + return true + # quotes '" + if code_point == 34 or code_point == 39: + return true + return false func parse_return_token(input: String) -> Variable: @@ -389,12 +409,14 @@ func is_getter_or_setter(func_name: String) -> bool: return func_name.begins_with("@") and (func_name.ends_with("getter") or func_name.ends_with("setter")) -func _parse_function_arguments(input: String) -> Dictionary: - var arguments := {} +func _parse_function_arguments(input: String) -> Array[Dictionary]: + var arguments: Array[Dictionary] = [] var current_index := 0 - var token :Token = null + var token: Token = null var bracket := 0 var in_function := false + + while current_index < len(input): token = next_token(input, current_index) # fallback to not end in a endless loop @@ -416,7 +438,6 @@ func _parse_function_arguments(input: String) -> Dictionary: if token == TOKEN_BRACKET_ROUND_OPEN : in_function = true bracket += 1 - continue if token == TOKEN_BRACKET_ROUND_CLOSE: bracket -= 1 # if function end? @@ -424,20 +445,17 @@ func _parse_function_arguments(input: String) -> Dictionary: return arguments # is function if token == TOKEN_FUNCTION_DECLARATION: - token = next_token(input, current_index) - current_index += token._consumed - continue - # is fuzzer argument - if token is FuzzerToken: - var arg_value := _parse_end_function(input.substr(current_index), true) - current_index += arg_value.length() - var arg_name :String = (token as FuzzerToken).name() - arguments[arg_name] = arg_value.lstrip(" ") continue + # is value argument - if in_function and token.is_variable(): - var arg_name: String = (token as Variable).plain_value() - var arg_value: String = GdFunctionArgument.UNDEFINED + if in_function: + var arg_value := "" + var current_argument := { + "name" : "", + "value" : GdFunctionArgument.UNDEFINED, + "type" : TYPE_VARIANT + } + # parse type and default value while current_index < len(input): token = next_token(input, current_index) @@ -445,19 +463,44 @@ func _parse_function_arguments(input: String) -> Dictionary: if token.is_skippable(): continue + if token.is_variable() && current_argument["name"] == "": + arguments.append(current_argument) + current_argument["name"] = (token as Variable).plain_value() + continue + match token: + # is fuzzer argument + TOKEN_ARGUMENT_FUZZER: + arg_value = _parse_end_function(input.substr(current_index), true) + current_index += arg_value.length() + current_argument["name"] = (token as FuzzerToken).name() + current_argument["value"] = arg_value.lstrip(" ") + current_argument["type"] = TYPE_FUZZER + arguments.append(current_argument) + continue + + TOKEN_ARGUMENT_VARIADIC: + current_argument["type"] = TYPE_VARARG + TOKEN_ARGUMENT_TYPE: token = next_token(input, current_index) if token == TOKEN_SPACE: current_index += token._consumed token = next_token(input, current_index) + current_index += token._consumed + if current_argument["type"] != TYPE_VARARG: + current_argument["type"] = GdObjects.string_to_type((token as Variable).plain_value()) + TOKEN_ARGUMENT_TYPE_ASIGNMENT: arg_value = _parse_end_function(input.substr(current_index), true) current_index += arg_value.length() + current_argument["value"] = arg_value.lstrip(" ") TOKEN_ARGUMENT_ASIGNMENT: token = next_token(input, current_index) arg_value = _parse_end_function(input.substr(current_index), true) current_index += arg_value.length() + current_argument["value"] = arg_value.lstrip(" ") + TOKEN_BRACKET_SQUARE_OPEN: bracket += 1 TOKEN_BRACKET_CURLY_OPEN: @@ -483,8 +526,13 @@ func _parse_function_arguments(input: String) -> Dictionary: break TOKEN_ARGUMENT_SEPARATOR: if bracket <= 1: - break - arguments[arg_name] = arg_value.lstrip(" ") + # next argument + current_argument = { + "name" : "", + "value" : GdFunctionArgument.UNDEFINED, + "type" : GdObjects.TYPE_VARIANT + } + continue return arguments @@ -556,7 +604,7 @@ func extract_inner_class(source_rows: PackedStringArray, clazz_name :String) -> return PackedStringArray() -func extract_func_signature(rows :PackedStringArray, index :int) -> String: +func extract_func_signature(rows: PackedStringArray, index: int) -> String: var signature := "" for rowIndex in range(index, rows.size()): @@ -579,31 +627,24 @@ func get_class_name(script :GDScript) -> String: var input := source_rows[index] var token := next_token(input, 0) if token == TOKEN_CLASS_NAME: - var current_index := token._consumed - token = next_token(input, current_index) - current_index += token._consumed - token = tokenize_value(input, current_index, token) - return (token as Variable).value() + return token.value() # if no class_name found extract from file name return GdObjects.to_pascal_case(script.resource_path.get_basename().get_file()) -func parse_func_name(input :String) -> String: - var current_index := 0 - var token := next_token(input, current_index) - current_index += token._consumed - if token != TOKEN_FUNCTION_STATIC_DECLARATION and token != TOKEN_FUNCTION_DECLARATION: - return "" - while not token is Variable: - token = next_token(input, current_index) - current_index += token._consumed - return token._token +func parse_func_name(input: String) -> String: + if TOKEN_FUNCTION_DECLARATION.match(input, 0): + return TOKEN_FUNCTION_DECLARATION.value() + if TOKEN_FUNCTION_STATIC_DECLARATION.match(input, 0): + return TOKEN_FUNCTION_STATIC_DECLARATION.value() + push_error("Can't extract function name from '%s'" % input) + return "" ## Enriches the function descriptor by line number and argument default values ## - enrich all function descriptors form current script up to all inherited scrips func _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescriptor]) -> void: - var enriched_functions := PackedStringArray() + var enriched_functions := {} # Use Dictionary for O(1) lookup instead of PackedStringArray var script_to_scan := script while script_to_scan != null: # do not scan the test suite base class itself @@ -620,29 +661,35 @@ func _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescript if input.begins_with("#") or input.length() == 0: continue var token := next_token(input, 0) - if token == TOKEN_FUNCTION_STATIC_DECLARATION or token == TOKEN_FUNCTION_DECLARATION: - var function_name := parse_func_name(input) - var fd: GdFunctionDescriptor = fds.filter(func(element: GdFunctionDescriptor) -> bool: - # is same function name and not already enriched - return function_name == element.name() and not enriched_functions.has(element.name()) - ).pop_front() - if fd != null: - # add as enriched function to exclude from next iteration (could be inherited) - @warning_ignore("return_value_discarded") - enriched_functions.append(fd.name()) - var func_signature := extract_func_signature(rows, rowIndex) - var func_arguments := _parse_function_arguments(func_signature) - # enrich missing default values - for arg_name: String in func_arguments.keys(): - var func_argument: String = func_arguments[arg_name] - fd.set_argument_value(arg_name, func_argument) - fd.enrich_file_info(script_to_scan.resource_path, rowIndex + 1) - fd._is_coroutine = is_func_coroutine(rows, rowIndex) - # enrich return class name if not set - if fd.return_type() == TYPE_OBJECT and fd._return_class in ["", "Resource", "RefCounted"]: - var var_token := parse_return_token(func_signature) - if var_token != TOKEN_NOT_MATCH and var_token.type() == TYPE_OBJECT: - fd._return_class = _patch_inner_class_names(var_token.plain_value(), "") + if token != TOKEN_FUNCTION_STATIC_DECLARATION and token != TOKEN_FUNCTION_DECLARATION: + continue + + var function_name: String = token.value() + # Skip if already enriched (from parent class scan) + if enriched_functions.has(function_name): + continue + + # Find matching function descriptor + var fd: GdFunctionDescriptor = null + for candidate in fds: + if candidate.name() == function_name: + fd = candidate + break + if fd == null: + continue + # Mark as enriched + enriched_functions[function_name] = true + var func_signature := extract_func_signature(rows, rowIndex) + var func_arguments := _parse_function_arguments(func_signature) + # enrich missing default values + fd.enrich_arguments(func_arguments) + fd.enrich_file_info(script_to_scan.resource_path, rowIndex + 1) + fd._is_coroutine = is_func_coroutine(rows, rowIndex) + # enrich return class name if not set + if fd.return_type() == TYPE_OBJECT and fd._return_class in ["", "Resource", "RefCounted"]: + var var_token := parse_return_token(func_signature) + if var_token != TOKEN_NOT_MATCH and var_token.type() == TYPE_OBJECT: + fd._return_class = _patch_inner_class_names(var_token.plain_value(), "") # if the script ihnerits we need to scan this also script_to_scan = script_to_scan.get_base_script() @@ -650,14 +697,16 @@ func _enrich_function_descriptor(script: GDScript, fds: Array[GdFunctionDescript func is_func_coroutine(rows :PackedStringArray, index :int) -> bool: var is_coroutine := false for rowIndex in range(index+1, rows.size()): - var input := rows[rowIndex] - is_coroutine = input.contains("await") - if is_coroutine: - return true + var input := rows[rowIndex].strip_edges() + if input.begins_with("#") or input.is_empty(): + continue var token := next_token(input, 0) # scan until next function if token == TOKEN_FUNCTION_STATIC_DECLARATION or token == TOKEN_FUNCTION_DECLARATION: break + + if _is_awaiting.search(input): + return true return is_coroutine diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid b/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid index 4961e914..ccc48c93 100644 --- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid @@ -1 +1 @@ -uid://b78j43ljfuk77 +uid://bf1gwn3im0osu diff --git a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid index a45cbb43..27dcc3bf 100644 --- a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid @@ -1 +1 @@ -uid://b1yoqtvbi13bp +uid://4fj4evlfll6u diff --git a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid index aad421fd..da0c74ed 100644 --- a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid +++ b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid @@ -1 +1 @@ -uid://cxrcrn0fwe7u6 +uid://diulbkfdbxocl diff --git a/addons/gdUnit4/src/core/report/GdUnitReport.gd b/addons/gdUnit4/src/core/report/GdUnitReport.gd index eb7ed2e5..aaf309ff 100644 --- a/addons/gdUnit4/src/core/report/GdUnitReport.gd +++ b/addons/gdUnit4/src/core/report/GdUnitReport.gd @@ -16,6 +16,7 @@ enum { var _type :int var _line_number :int var _message :String +var _current_value: Variant func create(p_type :int, p_line_number :int, p_message :String) -> GdUnitReport: @@ -25,6 +26,11 @@ func create(p_type :int, p_line_number :int, p_message :String) -> GdUnitReport: return self +func with_current_value(value: Variant) -> GdUnitReport: + _current_value = value + return self + + func type() -> int: return _type @@ -53,6 +59,10 @@ func is_error() -> bool: return _type == TERMINATED or _type == INTERUPTED or _type == ABORT +func is_orphan() -> bool: + return _type == ORPHAN + + func _to_string() -> String: if _line_number == -1: return "[color=green]line [/color][color=aqua]:[/color] %s" % [_message] diff --git a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid index 97b6e99d..6fc0ee06 100644 --- a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid +++ b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid @@ -1 +1 @@ -uid://bvq0loao62flo +uid://du4tx164lah4t diff --git a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd deleted file mode 100644 index 093cc50f..00000000 --- a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd +++ /dev/null @@ -1,131 +0,0 @@ -extends Node -## The base test runner implementation.[br] -## [br] -## This class provides the core functionality to execute test suites with following features:[br] -## - Loading and initialization of test suites[br] -## - Executing test suites and managing test states[br] -## - Event dispatching and test reporting[br] -## - Support for headless mode[br] -## - Plugin version verification[br] -## [br] -## Supported by specialized runners:[br] -## - [b]GdUnitTestRunner[/b]: Used in the editor, connects via tcp to report test results[br] -## - [b]GdUnitCLRunner[/b]: A command line interface runner, writes test reports to file[br] -## The test runner runs checked default in fail-fast mode, it stops checked first test failure. - -## Overall test run status codes used by the runners -const RETURN_SUCCESS = 0 -const RETURN_ERROR = 100 -const RETURN_ERROR_HEADLESS_NOT_SUPPORTED = 103 -const RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED = 104 -const RETURN_WARNING = 101 - -## Specifies the Node name under which the runner is registered -const GDUNIT_RUNNER = "GdUnitRunner" -## The maximum number of report history files to store -const DEFAULT_REPORT_COUNT = 20 - -## The current runner configuration -@warning_ignore("unused_private_class_variable") -var _runner_config := GdUnitRunnerConfig.new() - -## The test suite executor instance -var _executor: GdUnitTestSuiteExecutor - -## Current runner state -var _state := READY - -## Current tests to be processed -var _test_cases: Array[GdUnitTestCase] = [] - -## Runner state machine -enum { - READY, - INIT, - RUN, - STOP, - EXIT -} - - -func _init() -> void: - # minimize scene window checked debug mode - if OS.get_cmdline_args().size() == 1: - DisplayServer.window_set_title("GdUnit4 Runner (Debug Mode)") - else: - DisplayServer.window_set_title("GdUnit4 Runner (Release Mode)") - DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) - # store current runner instance to engine meta data to can be access in as a singleton - Engine.set_meta(GDUNIT_RUNNER, self) - - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - if Engine.get_version_info().hex < 0x40300: - printerr("The GdUnit4 plugin requires Godot version 4.3 or higher to run.") - quit(RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED) - return - _executor = GdUnitTestSuiteExecutor.new() - - GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event) - _state = INIT - - -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - Engine.remove_meta(GDUNIT_RUNNER) - - -## Main test runner loop. Is called every frame to manage the test execution. -func _process(_delta: float) -> void: - match _state: - INIT: - init_runner() - RUN: - # process next test suite - set_process(false) - await _executor.run_and_wait(_test_cases) - _state = STOP - set_process(true) - STOP: - _state = EXIT - # give the engine small amount time to finish the rpc - _on_gdunit_event(GdUnitStop.new()) - await get_tree().create_timer(0.1).timeout - await quit(get_exit_code()) - - -## Used by the inheriting runners to initialize test execution -func init_runner() -> void: - pass - - -## Returns the exit code when the test run is finished.[br] -## Abstract method to be implemented by the inheriting runners. -func get_exit_code() -> int: - return RETURN_SUCCESS - - -## Quits the test runner with given exit code. -func quit(code: int) -> void: - await get_tree().process_frame - await get_tree().physics_frame - get_tree().quit(code) - - -func prints_warning(message: String) -> void: - prints(message) - - -## Default event handler to process test events.[br] -## Should be overridden by concrete runner implementation. -@warning_ignore("unused_parameter") -func _on_gdunit_event(event: GdUnitEvent) -> void: - pass - - -## Event bridge from C# GdUnit4.ITestEventListener.cs[br] -## Used to handle test events from C# tests. -# gdlint: disable=function-name -func PublishEvent(data: Dictionary) -> void: - _on_gdunit_event(GdUnitEvent.new().deserialize(data)) diff --git a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid deleted file mode 100644 index 43b27cef..00000000 --- a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d067xlq7k011y diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd index 608f02b9..34dcfa38 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd +++ b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd @@ -1,6 +1,6 @@ #warning-ignore-all:return_value_discarded class_name GdUnitTestCIRunner -extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" +extends "res://addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd" ## Command line test runner implementation.[br] ## [br] ## This runner is designed for CI/CD pipelines and command line test execution.[br] @@ -23,10 +23,7 @@ extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") var _console := GdUnitCSIMessageWriter.new() -var _console_reporter: GdUnitTestReporter -var _html_reporter: GdUnitHtmlTestReporter -var _report_dir: String -var _report_max: int = DEFAULT_REPORT_COUNT +var _console_reporter: GdUnitConsoleTestReporter var _headless_mode_ignore := false var _runner_config_file := "" var _debug_cmd_args := PackedStringArray() @@ -80,7 +77,7 @@ var _cmd_options := CmdOptions.new([ CmdOption.new( "-rc, --report-count", "-rc ", - "Specifies how many reports are saved before they are deleted. The default is %s." % str(DEFAULT_REPORT_COUNT), + "Specifies how many reports are saved before they are deleted. The default is %s." % str(GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT), TYPE_INT, true ), @@ -108,10 +105,10 @@ func _init() -> void: func _ready() -> void: super() - _report_dir = GdUnitFileAccess.current_dir() + "reports" - _console_reporter = GdUnitConsoleTestReporter.new(_console, true) # stop checked first test failure to fail fast _executor.fail_fast(true) + _console_reporter = GdUnitConsoleTestReporter.new(_console, true) + GdUnitSignals.instance().gdunit_message.connect(_on_send_message) func _notification(what: int) -> void: @@ -134,8 +131,7 @@ func get_exit_code() -> int: ## [br] ## [param code] The exit code to return. func quit(code: int) -> void: - if code != RETURN_SUCCESS: - _state = EXIT + _state = EXIT GdUnitTools.dispose_all() await GdUnitMemoryObserver.gc_on_guarded_instances() await super(code) @@ -167,9 +163,9 @@ func console_warning(message: String) -> void: ## [br] ## [param path] The path where reports should be written. func set_report_dir(path: String) -> void: - _report_dir = ProjectSettings.globalize_path(GdUnitFileAccess.make_qualified_path(path)) + report_base_path = ProjectSettings.globalize_path(GdUnitFileAccess.make_qualified_path(path)) console_info( - "Set write reports to %s" % _report_dir, + "Set write reports to %s" % report_base_path, Color.DEEP_SKY_BLUE ) @@ -182,15 +178,15 @@ func set_report_count(count: String) -> void: if report_count < 1: console_error( "Invalid report history count '%s' set back to default %d" - % [count, DEFAULT_REPORT_COUNT] + % [count, GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT] ) - _report_max = DEFAULT_REPORT_COUNT + max_report_history = GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT else: console_info( "Set report history count to %s" % count, Color.DEEP_SKY_BLUE ) - _report_max = report_count + max_report_history = report_count ## Disables fail-fast mode to run all tests.[br] @@ -387,13 +383,12 @@ func init_gd_unit() -> void: quit(RETURN_ERROR_HEADLESS_NOT_SUPPORTED) return - _html_reporter = GdUnitHtmlTestReporter.new(_report_dir, _report_max) - discover_tests() + _test_cases = discover_tests() if _test_cases.is_empty(): console_info("No test cases found, abort test run!", Color.YELLOW) console_info("Exit code: %d" % RETURN_SUCCESS, Color.DARK_SALMON) quit(RETURN_SUCCESS) - _on_gdunit_event(GdUnitInit.new()) + return _state = RUN @@ -437,13 +432,14 @@ func is_skipped(test: GdUnitTestCase) -> bool: # is suite skipped by full path or suite name if skipped_info == test.suite_name or test.source_file.contains(skipped_info): return true + var skip_file := skipped_info.replace("res://", "") # check for skipped single test - if not skipped_info.contains(":"): + if not skip_file.contains(":"): continue - var parts: PackedStringArray = skipped_info.rsplit(":") - var skipped_suite := parts[0] + ":" + parts[1] if parts[0] == "res" else parts[0] - var skipped_test := parts[2] if parts[0] == "res" else parts[1] + var parts: PackedStringArray = skip_file.rsplit(":") + var skipped_suite := parts[0] + var skipped_test := parts[1] # is suite skipped by full path or suite name if (skipped_suite == test.suite_name or test.source_file.contains(skipped_suite)) and skipped_test == test.test_name: return true @@ -451,18 +447,16 @@ func is_skipped(test: GdUnitTestCase) -> bool: return false -func _on_gdunit_event(event: GdUnitEvent) -> void: - _console_reporter.on_gdunit_event(event) - _html_reporter.on_gdunit_event(event) +func _on_send_message(message: String) -> void: + _console.color(Color.CORNFLOWER_BLUE).println_message(message) + +func _on_gdunit_event(event: GdUnitEvent) -> void: match event.type(): - GdUnitEvent.STOP: - # TODO move to `GdUnitJUnitXMLTestReporter` - JUnitXmlReport.new(_html_reporter._report._report_path, _html_reporter._report.iteration()).write(_html_reporter._report) - console_info( - "Open HTML Report at: file://%s" % _html_reporter.report_file(), - Color.CORNFLOWER_BLUE - ) + GdUnitEvent.SESSION_START: + _console_reporter.test_session = _test_session + GdUnitEvent.SESSION_CLOSE: + _console_reporter.test_session = null func report_exit_code() -> int: diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid index 6bd3a81e..d6f7b871 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid +++ b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid @@ -1 +1 @@ -uid://bj5xoyqhuebbg +uid://cqfnh4g4h4qcw diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd index 2d139721..0fd8fc7b 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd @@ -1,4 +1,5 @@ -extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" +@tool +extends "res://addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd" ## Runner implementation used by the editor UI.[br] ## [br] ## This runner connects to a GdUnit server via TCP to report test results.[br] @@ -8,17 +9,20 @@ extends "res://addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd" ## - Messages to report progress[br] ## - Events to report test results[br] - ## The TCP client used to connect to the GdUnit server @onready var _client: GdUnitTcpClient = $GdUnitTcpClient +@onready var _version_label: Control = %Version func _init() -> void: super() + # We set the default max report history to 1 + max_report_history = 1 func _ready() -> void: super() + GdUnit4Version.init_version_label(_version_label) var config_result := _runner_config.load_config() if config_result.is_error(): @@ -26,13 +30,25 @@ func _ready() -> void: _state = EXIT return @warning_ignore("return_value_discarded") - _client.connect("connection_failed", _on_connection_failed) + _client.connection_failed.connect(_on_connection_failed) + GdUnitSignals.instance().gdunit_message.connect(_on_send_message) + _executor.fail_fast(_runner_config.is_fail_fast()) var result := _client.start("127.0.0.1", _runner_config.server_port()) if result.is_error(): push_error(result.error_message()) return +## Cleanup and quit the runner.[br] +## [br] +## [param code] The exit code to return. +func quit(code: int) -> void: + if code != RETURN_SUCCESS: + _state = EXIT + await GdUnitMemoryObserver.gc_on_guarded_instances() + await super.quit(code) + + ## Called when the TCP connection to the GdUnit server fails.[br] ## Stops the test execution.[br] ## [br] @@ -48,7 +64,7 @@ func _on_connection_failed(message: String) -> void: func init_runner() -> void: # wait until client is connected to the GdUnitServer if _client.is_client_connected(): - gdUnitInit() + await gdUnitInit() _state = RUN @@ -63,7 +79,7 @@ func gdUnitInit() -> void: ## Sends a message via TCP to the GdUnit server.[br] ## [br] ## [param message] The message to send. -func send_message(message: String) -> void: +func _on_send_message(message: String) -> void: _client.send(RPCMessage.of(message)) diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid index aff91a66..6113217a 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid @@ -1 +1 @@ -uid://ck1aurdyu3fdc +uid://biclgpd614aya diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn index 1da430e4..eaa6f1ab 100644 --- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn +++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn @@ -1,10 +1,34 @@ [gd_scene load_steps=3 format=3 uid="uid://belidlfknh74r"] -[ext_resource type="Script" uid="uid://cewyamo5wr2xw" path="res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd" id="1"] -[ext_resource type="Script" uid="uid://cp5knenan84na" path="res://addons/gdUnit4/src/network/GdUnitTcpClient.gd" id="2"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitTcpClient.gd" id="2"] [node name="Control" type="Node"] script = ExtResource("1") [node name="GdUnitTcpClient" type="Node" parent="."] script = ExtResource("2") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +custom_minimum_size = Vector2(0, 24) +layout_direction = 2 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 0 +size_flags_horizontal = 3 +size_flags_vertical = 10 +alignment = 2 + +[node name="Version" type="RichTextLabel" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(128, 0) +layout_mode = 2 +size_flags_horizontal = 10 +bbcode_enabled = true +scroll_active = false +shortcut_keys_enabled = false +horizontal_alignment = 1 +justification_flags = 0 diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd new file mode 100644 index 00000000..c789847b --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd @@ -0,0 +1,169 @@ +## +## @since GdUnit4 5.1.0 +## +## Represents a test execution session in GdUnit4.[br] +## [br] +## [i]A test session encapsulates a complete test execution cycle, managing the collection +## of test cases to be executed and providing communication channels for test events +## and messages. This class serves as the central coordination point for test execution +## and allows hooks and other components to interact with the running test session.[/i][br] +## [br] +## [b][u]Key Features[/u][/b][br] +## - [i][b]Test Case Management[/b][/i]: Maintains a collection of test cases to be executed[br] +## - [i][b]Event Broadcasting[/b][/i]: Forwards GdUnit events to session-specific listeners[br] +## - [i][b]Message Communication[/b][/i]: Provides a channel for sending messages during test execution[br] +## - [i][b]Hook Integration[/b][/i]: Passed to test session hooks for startup and shutdown operations[br] +## [br] +## [b][u]Usage in Test Hooks[/u][/b] +## [codeblock] +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## # Access test cases +## print("Running %d test cases" % session.test_cases.size()) +## +## # Send status messages +## session.send_message("Custom hook initialized") +## +## # Listen for test events +## session.test_event.connect(_on_test_event) +## +## return GdUnitResult.success() +## +## func _on_test_event(event: GdUnitEvent) -> void: +## print("Test event received: %s" % event.type) +## [/codeblock] +## [br] +## [b][u]Event Flow[/u][/b][br] +## 1. Session is created with a collection of test cases[br] +## 2. Session connects to the global GdUnit event system[br] +## 3. During test execution, events are automatically forwarded to session listeners[br] +## 4. Hooks and other components can subscribe to session events[br] +## 5. Messages can be sent through the session for logging and communication[br] +class_name GdUnitTestSession +extends RefCounted + + +## Emitted when a test execution event occurs.[br] +## [br] +## [i]This signal forwards events from the global GdUnit event system to session-specific +## listeners. It allows hooks and other session components to react to test events +## without directly connecting to the global event system.[/i][br] +## [br] +## [u]Common event types include:[/u][br] +## - Test suite start/end events[br] +## - Test case start/end events[br] +## - Test assertion events[br] +## - Test failure/error events[br] +## +## [param event] The test event containing details about test execution, timing, and results +@warning_ignore("unused_signal") +signal test_event(event: GdUnitEvent) + + +## [b][color=red]@readonly: Should not be modified directly during test execution![/color][/b][br] +## Collection of test cases to be executed in this session.[br] +## [br] +## This array contains all the test cases that will be run during the session. +## Test hooks can access this collection to: +## - Get the total number of tests to be executed +## - Access individual test case metadata +## - Perform setup/teardown based on test case requirements +## - Generate reports or statistics about the test suite +## +## The collection is typically populated before session startup and remains +## constant during test execution. +var _test_cases : Array[GdUnitTestCase] = [] + + +## [b][color=red]@readonly: The report path should not be modified after session creation![/color][/b][br] +## The file system path where test reports for this session will be generated.[br] +## [br] +## [i]This property provides centralized access to the report output location, +## allowing test hooks, reporters, and other components to reference the same +## report path without coupling to specific reporter implementations.[/i][br] +## [br] +## [b][u]Common use cases include:[/u][/b][br] +## - Test hooks generating additional report files in the same directory[br] +## - Custom reporters creating supplementary output files[br] +## - Post-processing scripts that need to locate generated reports[br] +## - Cleanup operations that need to manage report artifacts[br] +## [br] +## [b][u]Example Usage:[/u][/b] +## [codeblock] +## # In a test hook +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## var report_dir = session.report_path.get_base_dir() +## var custom_report = report_dir.path_join("custom_metrics.json") +## # Generate additional reports in the same location +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Reports available at: " + session.report_path) +## return GdUnitResult.success() +## [/codeblock] +## [br] +## The path is set during session initialization and remains constant throughout +## the test execution lifecycle. +var report_path: String: + get: + return report_path + + +## Initializes the test session and sets up event forwarding.[br] +## [br] +## [i]This constructor automatically connects to the global GdUnit event system +## and forwards all events to the session's test_event signal. This allows +## session-specific components to listen for test events without managing +## global signal connections.[/i] +func _init(test_cases: Array[GdUnitTestCase], session_report_path: String) -> void: + # We build a copy to prevent a user is modifing the tests + _test_cases = test_cases.duplicate(true) + report_path = session_report_path + GdUnitSignals.instance().gdunit_event.connect(func(event: GdUnitEvent) -> void: + test_event.emit(event) + ) + + +## Finds a test case by its unique identifier.[br] +## [br] +## [i]Searches through all test cases to find a test with the matching GUID.[/i][br] +## [br] +## [param id] The GUID of the test to find[br] +## Returns the matching test case or null if not found. +func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: + for test in _test_cases: + if test.guid.equals(id): + return test + + return null + + +## Sends a message through the GdUnit messaging system.[br] +## [br] +## [i]This method provides a convenient way for test hooks and other session +## components to send messages that will be handled by the GdUnit framework.[/i] +## [br][br] +## [b][u]Messages are typically used for:[/u][/b][br] +## - Status updates during test execution[br] +## - Progress reporting from test hooks[br] +## - Debug information and logging[br] +## - User notifications and alerts[br] +## [br] +## The message will be processed by the global GdUnit message system and +## may be displayed in the test runner UI, logged to files, or handled +## by other registered message handlers. +## [br] +## [b][u]Example Usage:[/u][/b] +## [codeblock] +## # In a test hook +## func startup(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Database connection established") +## return GdUnitResult.success() +## +## func shutdown(session: GdUnitTestSession) -> GdUnitResult: +## session.send_message("Generated test report: report.html") +## return GdUnitResult.success() +## ``` +## [/codeblock] +## [param message] The message text to send through the GdUnit messaging system +func send_message(message: String) -> void: + GdUnitSignals.instance().gdunit_message.emit(message) diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid new file mode 100644 index 00000000..05669fb7 --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSession.gd.uid @@ -0,0 +1 @@ +uid://dnmlsirdcx7h4 diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd new file mode 100644 index 00000000..6551e085 --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd @@ -0,0 +1,180 @@ +extends Node +## The base test runner implementation.[br] +## [br] +## This class provides the core functionality to execute test suites with following features:[br] +## - Loading and initialization of test suites[br] +## - Executing test suites and managing test states[br] +## - Event dispatching and test reporting[br] +## - Support for headless mode[br] +## - Plugin version verification[br] +## [br] +## Supported by specialized runners:[br] +## - [b]GdUnitTestRunner[/b]: Used in the editor, connects via tcp to report test results[br] +## - [b]GdUnitCLRunner[/b]: A command line interface runner, writes test reports to file[br] +## The test runner runs checked default in fail-fast mode, it stops checked first test failure. + +## Overall test run status codes used by the runners +const RETURN_SUCCESS = 0 +const RETURN_ERROR = 100 +const RETURN_ERROR_HEADLESS_NOT_SUPPORTED = 103 +const RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED = 104 +const RETURN_WARNING = 101 + +## Specifies the Node name under which the runner is registered +const GDUNIT_RUNNER = "GdUnitRunner" + +## The current runner configuration +@warning_ignore("unused_private_class_variable") +var _runner_config := GdUnitRunnerConfig.new() + +## The test suite executor instance +var _executor: GdUnitTestSuiteExecutor +var _hooks : GdUnitTestSessionHookService + +## Current runner state +var _state := READY + +## Current tests to be processed +var _test_cases: Array[GdUnitTestCase] = [] + + +## Configured report base path (can be set on CI test runner) +var report_base_path: String = GdUnitFileAccess.current_dir() + "reports": + get: + return report_base_path + + +## Current session report path +var report_path: String: + get: + return "%s/%s%d" % [report_base_path, GdUnitConstants.REPORT_DIR_PREFIX, current_report_history_index] + + +## Current report history index, if max_report_history > 1 we scan for the next index over the existing reports +var current_report_history_index: int: + get: + if max_report_history > 1: + return GdUnitFileAccess.find_last_path_index(report_base_path, GdUnitConstants.REPORT_DIR_PREFIX) + 1 + else: + return 1 + + +## Controls how many report historys will be hold +var max_report_history: int = GdUnitConstants.DEFAULT_REPORT_HISTORY_COUNT: + get: + return max_report_history + set(value): + max_report_history = value + + +# holds the current test session context +var _test_session: GdUnitTestSession + +## Runner state machine +enum { + READY, + INIT, + RUN, + STOP, + EXIT +} + +func _init() -> void: + if OS.get_cmdline_args().size() == 1: + DisplayServer.window_set_title("GdUnit4 Runner (Debug Mode)") + else: + DisplayServer.window_set_title("GdUnit4 Runner (Release Mode)") + if not Engine.is_embedded_in_editor(): + # minimize scene window checked debug mode + DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MINIMIZED) + # store current runner instance to engine meta data to can be access in as a singleton + Engine.set_meta(GDUNIT_RUNNER, self) + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + if Engine.get_version_info().hex < 0x40300: + printerr("The GdUnit4 plugin requires Godot version 4.3 or higher to run.") + quit(RETURN_ERROR_GODOT_VERSION_NOT_SUPPORTED) + return + _executor = GdUnitTestSuiteExecutor.new() + + GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event) + _state = INIT + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + Engine.remove_meta(GDUNIT_RUNNER) + + +## Main test runner loop. Is called every frame to manage the test execution. +func _process(_delta: float) -> void: + match _state: + INIT: + await init_runner() + RUN: + _hooks = GdUnitTestSessionHookService.instance() + _test_session = GdUnitTestSession.new(_test_cases, report_path) + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionStart.new()) + # process next test suite + set_process(false) + var result := await _hooks.execute_startup(_test_session) + if result.is_error(): + push_error(result.error_message()) + await _executor.run_and_wait(_test_cases) + result = await _hooks.execute_shutdown(_test_session) + if result.is_error(): + push_error(result.error_message()) + _state = STOP + set_process(true) + GdUnitSignals.instance().gdunit_event.emit(GdUnitSessionClose.new()) + cleanup_report_history() + STOP: + _state = EXIT + # give the engine small amount time to finish the rpc + await get_tree().create_timer(0.1).timeout + await quit(get_exit_code()) + + +## Used by the inheriting runners to initialize test execution +func init_runner() -> void: + await get_tree().process_frame + + +func cleanup_report_history() -> int: + return GdUnitFileAccess.delete_path_index_lower_equals_than( + report_path.get_base_dir(), + GdUnitConstants.REPORT_DIR_PREFIX, + current_report_history_index-1-max_report_history) + + +## Returns the exit code when the test run is finished.[br] +## Abstract method to be implemented by the inheriting runners. +func get_exit_code() -> int: + return RETURN_SUCCESS + + +## Quits the test runner with given exit code. +func quit(code: int) -> void: + await get_tree().process_frame + await get_tree().physics_frame + get_tree().quit(code) + + +func prints_warning(message: String) -> void: + prints(message) + + +## Default event handler to process test events.[br] +## Should be overridden by concrete runner implementation. +@warning_ignore("unused_parameter") +func _on_gdunit_event(event: GdUnitEvent) -> void: + pass + + +## Event bridge from C# GdUnit4.ITestEventListener.cs[br] +## Used to handle test events from C# tests. +# gdlint: disable=function-name +func PublishEvent(data: Dictionary) -> void: + _on_gdunit_event(GdUnitEvent.new().deserialize(data)) diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid new file mode 100644 index 00000000..df56a7de --- /dev/null +++ b/addons/gdUnit4/src/core/runners/GdUnitTestSessionRunner.gd.uid @@ -0,0 +1 @@ +uid://c6c2sw04ucedx diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd index 931fe846..20325ac1 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd @@ -10,7 +10,7 @@ const DEFAULT_TEMP_TS_GD =""" @warning_ignore('return_value_discarded') # TestSuite generated from - const __source = '${source_resource_path}' + const __source: String = '${source_resource_path}' """ diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid index 8a49357c..59fd5511 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd.uid @@ -1 +1 @@ -uid://byso8v2terhtt +uid://c86s8qkynjl33 diff --git a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid index 30c8f638..b37c88d5 100644 --- a/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid +++ b/addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd.uid @@ -1 +1 @@ -uid://c14u8ppcco1at +uid://b7wf6mvyor0y4 diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd index f2b2672c..402199b6 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd @@ -1,15 +1,15 @@ class_name GdUnitThreadContext extends RefCounted -var _thread :Thread -var _thread_name :String -var _thread_id :int -var _signal_collector :GdUnitSignalCollector -var _execution_context :GdUnitExecutionContext +var _thread: Thread +var _thread_name: String +var _thread_id: int +var _signal_collector: GdUnitSignalCollector +var _execution_context: GdUnitExecutionContext var _asserts := [] -func _init(thread :Thread = null) -> void: +func _init(thread: Thread = null) -> void: if thread != null: _thread = thread _thread_name = thread.get_meta("name") @@ -29,11 +29,15 @@ func dispose() -> void: _thread = null +func terminate() -> void: + _execution_context.terminate() + + func clear_assert() -> void: _asserts.clear() -func set_assert(value :GdUnitAssert) -> void: +func set_assert(value: GdUnitAssert) -> void: if value != null: _asserts.append(value) @@ -42,7 +46,7 @@ func get_assert() -> GdUnitAssert: return null if _asserts.is_empty() else _asserts[-1] -func set_execution_context(context :GdUnitExecutionContext) -> void: +func set_execution_context(context: GdUnitExecutionContext) -> void: _execution_context = context diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid index 28f25fcc..db692a0b 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid @@ -1 +1 @@ -uid://12h8llwtn2r5 +uid://crgyexgcj7m3i diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd index 31b10782..2e694e25 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd @@ -3,7 +3,7 @@ class_name GdUnitThreadManager extends Object ## { = } -var _thread_context_by_id := {} +var _thread_context_by_id: Dictionary[int, GdUnitThreadContext] = {} ## holds the current thread id var _current_thread_id :int = -1 @@ -25,6 +25,11 @@ static func run(name :String, cb :Callable) -> Variant: return await instance()._run(name, cb) +static func interrupt() -> void: + for thread_context: GdUnitThreadContext in instance()._thread_context_by_id.values(): + thread_context.terminate() + + ## Returns the current valid thread context static func get_current_context() -> GdUnitThreadContext: return instance()._get_current_context() diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid index 58f9b6cf..a31a326a 100644 --- a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid +++ b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid @@ -1 +1 @@ -uid://cwxjlrkeyjqso +uid://b0e2s3t0vs0qo diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd index 01b7dfea..cc93ee2b 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd +++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd @@ -1,6 +1,6 @@ @tool class_name GdUnitCSIMessageWriter -extends GdUnitMessageWritter +extends GdUnitMessageWriter ## A message writer implementation using ANSI/CSI escape codes for console output.[br] ## [br] ## This writer provides formatted message output using CSI (Control Sequence Introducer) codes.[br] @@ -20,6 +20,7 @@ enum { const CSI_BOLD = "" const CSI_ITALIC = "" const CSI_UNDERLINE = "" +const CSI_RESET = "" # Control Sequence Introducer var _debug_show_color_codes := false @@ -28,6 +29,9 @@ var _color_mode := COLOR_TABLE ## Current cursor position in the line var _current_pos := 0 +# Pre-compiled regex patterns for tag matching +var _tag_regex: RegEx + ## Constructs CSI style codes based on flags.[br] ## [br] @@ -44,12 +48,110 @@ func _apply_style_flags(flags: int) -> String: return _style +## Converts a color string (named or hex) to a Color object +func _parse_color(color_str: String) -> Color: + return Color.from_string(color_str.strip_edges().to_lower(), Color.WHITE) + + +## Generates CSI color code for foreground color +func _color_to_csi_fg(c: Color) -> String: + return "[38;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a] + + +## Generates CSI color code for background color +func _color_to_csi_bg(c: Color) -> String: + return "[48;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a] + + +func _init_regex_patterns() -> void: + if not _tag_regex: + _tag_regex = RegEx.new() + # Match all richtext tags: [tag], [tag=value], [/tag] + _tag_regex.compile(r"\[/?(?:color|bgcolor|b|i|u)(?:=[^\]]+)?\]") + + +func _extract_color_from_tag(tag: String, tag_assign: String) -> Color: + var tag_assign_length := tag_assign.length() + var color_value := tag.substr(tag_assign_length, tag.length() - tag_assign_length - 1) + return _parse_color(color_value) + + +## Optimized richtext to CSI conversion using regex and lookup processing +func _bbcode_tags_to_csi_codes(message: String) -> String: + _init_regex_patterns() + + var result := "" + var last_pos := 0 + var color_stack: Array[Color] = [] + var bgcolor_stack: Array[Color] = [] + + # Find all richtext tags + var matches := _tag_regex.search_all(message) + + for match in matches: + var start_pos := match.get_start() + var end_pos := match.get_end() + var tag := match.get_string(0) + + # Add text before this tag + result += message.substr(last_pos, start_pos - last_pos) + + # Process the tag + if tag.begins_with("[color="): + var fg_color := _extract_color_from_tag(tag, "[color=") + color_stack.push_back(fg_color) + result += _color_to_csi_fg(fg_color) + elif tag.begins_with("[bgcolor="): + var bg_color := _extract_color_from_tag(tag, "[bgcolor=") + bgcolor_stack.push_back(bg_color) + result += _color_to_csi_bg(bg_color) + elif tag == "[b]": + result += CSI_BOLD + elif tag == "[i]": + result += CSI_ITALIC + elif tag == "[u]": + result += CSI_UNDERLINE + elif tag == "[/color]": + result += CSI_RESET + if color_stack.size() > 0: + color_stack.pop_back() + # Restore remaining styles and colors + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + elif tag == "[/bgcolor]": + result += CSI_RESET + if bgcolor_stack.size() > 0: + bgcolor_stack.pop_back() + # Restore remaining styles and colors + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + elif tag in ["[/b]", "[/i]", "[/u]"]: + result += CSI_RESET + # Restore remaining colors after style reset + if color_stack.size() > 0: + result += _color_to_csi_fg(color_stack[-1]) + if bgcolor_stack.size() > 0: + result += _color_to_csi_bg(bgcolor_stack[-1]) + + last_pos = end_pos + + # Add remaining text after last tag + result += message.substr(last_pos) + + return result + + ## Implementation of basic message output with formatting. func _print_message(_message: String, _color: Color, _indent: int, _flags: int) -> void: + var text := _bbcode_tags_to_csi_codes(_message) var indent_text := "".lpad(_indent * 2) var _style := _apply_style_flags(_flags) - printraw("%s[38;2;%d;%d;%dm%s%s" % [indent_text, _color.r8, _color.g8, _color.b8, _style, _message] ) - _current_pos += _indent * 2 + _message.length() + printraw("%s[38;2;%d;%d;%dm%s%s" % [indent_text, _color.r8, _color.g8, _color.b8, _style, text]) + _current_pos += _indent * 2 + text.length() ## Implementation of line-ending message output with formatting. @@ -60,17 +162,17 @@ func _println_message(_message: String, _color: Color, _indent: int, _flags: int ## Implementation of positioned message output with formatting. -func _print_at(_message: String, cursor_pos: int, _color: Color, _effect: Effect, _align: Align, _flags: int) -> void: +func _print_at(_message: String, cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, _flags: int) -> void: if _align == Align.RIGHT: cursor_pos = cursor_pos - _message.length() if cursor_pos > _current_pos: - printraw("[%dG" % cursor_pos) # Move cursor to absolute position + printraw("[%dG" % cursor_pos) # Move cursor to absolute position else: _message = " " + _message var _style := _apply_style_flags(_flags) - printraw("[38;2;%d;%d;%dm%s%s" % [_color.r8, _color.g8, _color.b8, _style, _message] ) + printraw("[38;2;%d;%d;%dm%s%s" % [_color.r8, _color.g8, _color.b8, _style, _message]) _current_pos = cursor_pos + _message.length() @@ -96,7 +198,7 @@ func restore_cursor() -> GdUnitCSIMessageWriter: ## Clears screen content and resets cursor position. func clear() -> void: - printraw("") # Clear screen and move cursor to home + printraw("") # Clear screen and move cursor to home _current_pos = 0 @@ -109,7 +211,7 @@ func _print_color_table() -> void: for green in range(0, 6): for red in range(0, 6): for blue in range(0, 6): - color(Color8(red*42, green*42, blue*42)).println_message("████████ ") + color(Color8(red * 42, green * 42, blue * 42)).println_message("████████ ") new_line() new_line() @@ -118,7 +220,7 @@ func _print_color_table() -> void: for green in range(0, 6): for red in range(0, 6): for blue in range(0, 6): - color(Color8(red*42, green*42, blue*42)).println_message("████████ ") + color(Color8(red * 42, green * 42, blue * 42)).println_message("████████ ") new_line() new_line() _color_mode = COLOR_TABLE diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid index d3fbc18d..902b8141 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid @@ -1 +1 @@ -uid://dd0l0xtkj75j +uid://bmuo55721ny3b diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd index 2ae94a46..5244efa4 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd +++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd @@ -1,5 +1,5 @@ @tool -class_name GdUnitMessageWritter +class_name GdUnitMessageWriter extends RefCounted ## Base interface class for writing formatted messages to different outputs.[br] ## [br] @@ -64,14 +64,14 @@ var _current_flags := 0 var _current_align := Align.LEFT ## The current text effect to be used for the next output operation -var _current_effect := Effect.NONE +var _current_effect := GdUnitMessageWriter.Effect.NONE ## Sets the text color for the next output operation.[br] ## [br] ## [param value] The color to be used for the text. ## Returns self for method chaining. -func color(value: Color) -> GdUnitMessageWritter: +func color(value: Color) -> GdUnitMessageWriter: _current_color = value return self @@ -80,7 +80,7 @@ func color(value: Color) -> GdUnitMessageWritter: ## [br] ## [param value] The number of indentation levels, where each level equals two spaces. ## Returns self for method chaining. -func indent(value: int) -> GdUnitMessageWritter: +func indent(value: int) -> GdUnitMessageWriter: _current_indent = value return self @@ -89,7 +89,7 @@ func indent(value: int) -> GdUnitMessageWritter: ## [br] ## [param value] A combination of style flags (BOLD, ITALIC, UNDERLINE). ## Returns self for method chaining. -func style(value: int) -> GdUnitMessageWritter: +func style(value: int) -> GdUnitMessageWriter: _current_flags = value return self @@ -98,7 +98,7 @@ func style(value: int) -> GdUnitMessageWritter: ## [br] ## [param value] The effect to apply to the text (NONE, WAVE). ## Returns self for method chaining. -func effect(value: Effect) -> GdUnitMessageWritter: +func effect(value: GdUnitMessageWriter.Effect) -> GdUnitMessageWriter: _current_effect = value return self @@ -107,7 +107,7 @@ func effect(value: Effect) -> GdUnitMessageWritter: ## [br] ## [param value] The alignment to use (LEFT, RIGHT). ## Returns self for method chaining. -func align(value: Align) -> GdUnitMessageWritter: +func align(value: Align) -> GdUnitMessageWriter: _current_align = value return self @@ -121,7 +121,7 @@ func align(value: Align) -> GdUnitMessageWritter: ## - align: LEFT[br] ## - effect: NONE[br] ## Returns self for method chaining. -func reset() -> GdUnitMessageWritter: +func reset() -> GdUnitMessageWriter: _current_color = Color.WHITE _current_indent = 0 _current_flags = 0 @@ -203,7 +203,7 @@ func _println_message(_message: String, _color: Color, _indent: int, _flags: int ## [param effect] The effect to apply.[br] ## [param align] The text alignment.[br] ## [param flags] The style flags to apply. -func _print_at(_message: String, _cursor_pos: int, _color: Color, _effect: Effect, _align: Align, _flags: int) -> void: +func _print_at(_message: String, _cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, _flags: int) -> void: pass diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid index e5af79ed..cfffc997 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid @@ -1 +1 @@ -uid://b68mtydadk8xp +uid://csxykwcrvjh4d diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd index 64793bbe..b9f79e49 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd +++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd @@ -1,6 +1,6 @@ @tool class_name GdUnitRichTextMessageWriter -extends GdUnitMessageWritter +extends GdUnitMessageWriter ## A message writer implementation using [RichTextLabel] for the test report UI.[br] ## [br] ## This writer implementation writes formatted messages to a [RichTextLabel] using BBCode.[br] @@ -85,7 +85,7 @@ func _println_message(message: String, _color: Color, _indent: int, flags: int) ## [param _effect] The text effect to apply (e.g. wave).[br] ## [param _align] The text alignment (left or right).[br] ## [param flags] The text style flags to apply. -func _print_at(message: String, cursor_pos: int, _color: Color, _effect: Effect, _align: Align, flags: int) -> void: +func _print_at(message: String, cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, flags: int) -> void: if _align == Align.RIGHT: cursor_pos = cursor_pos - message.length() diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid index 02a36e64..dc13c453 100644 --- a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid +++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid @@ -1 +1 @@ -uid://53rfaenrwp4p +uid://cwx66mlvc1bmk diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs index af8320f4..096e83c7 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs @@ -1,100 +1,59 @@ +// Copyright (c) 2025 Mike Schulze +// MIT License - See LICENSE file in the repository root for full license text +#pragma warning disable IDE1006 namespace gdUnit4.addons.gdUnit4.src.dotnet; +#pragma warning restore IDE1006 +#if GDUNIT4NET_API_V5 using System; using System.Collections.Generic; -using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; +using GdUnit4; using GdUnit4.Api; -using GdUnit4.Core.Discovery; using Godot; using Godot.Collections; -// GdUnit4 GDScript - C# API wrapper -// ReSharper disable once CheckNamespace +/// +/// The GdUnit4 GDScript - C# API wrapper. +/// public partial class GdUnit4CSharpApi : RefCounted { + /// + /// The signal to be emitted when the execution is completed. + /// [Signal] +#pragma warning disable CA1711 public delegate void ExecutionCompletedEventHandler(); +#pragma warning restore CA1711 - private static readonly object LockObject = new(); - - private static Type? apiType; - private static Assembly? gdUnit4Api; +#pragma warning disable CA2213, SA1201 private CancellationTokenSource? executionCts; - - public override void _Notification(int what) - { - if (what != NotificationPredelete) - return; - executionCts?.Dispose(); - executionCts = null; - } - - private static Assembly GetApi() - { - if (gdUnit4Api != null) - return gdUnit4Api; - lock (LockObject) - return gdUnit4Api ??= Assembly.Load("gdUnit4Api"); - } - - private static Type? GetApiType() - { - if (apiType != null) - return apiType; - apiType = GetApi().GetType("GdUnit4.GdUnit4NetApiGodotBridge"); - return apiType; - } - - private static Version? GdUnit4NetVersion() - { - try - { - return GetApi().GetName().Version; - } - catch (Exception) - { - return null; - } - } - - private static T? InvokeApiMethod(string methodName, params object[] args) - { - var method = GetApiType()?.GetMethod(methodName) ?? - throw new MethodAccessException($"Can't invoke method {methodName}"); - return (T?)method.Invoke(null, args); - } - - public static bool FindGdUnit4NetAssembly() - { - try - { - return GetApi().GetType("GdUnit4.GdUnit4NetApiGodotBridge") != null; - } - catch (Exception) - { - return false; - } - } - - public static string Version() - => GdUnit4NetVersion()?.ToString() - ?? "Unknown"; - - public static bool IsTestSuite(CSharpScript script) - => InvokeApiMethod("IsTestSuite", script); - +#pragma warning restore CA2213, SA1201 + + /// + /// Indicates if the API loaded. + /// + /// Returns true if the API already loaded. + public static bool IsApiLoaded() + => true; + + /// + /// Runs test discovery on the given script. + /// + /// The script to be scanned. + /// The list of tests discovered as dictionary. public static Array DiscoverTests(CSharpScript sourceScript) { try { // Get the list of test case descriptors from the API - var testCaseDescriptors = InvokeApiMethod>("DiscoverTestsFromScript", sourceScript)!; + var testCaseDescriptors = GdUnit4NetApiGodotBridge.DiscoverTestsFromScript(sourceScript); + // Convert each TestCaseDescriptor to a Dictionary return testCaseDescriptors .Select(descriptor => new Dictionary @@ -106,24 +65,61 @@ public static Array DiscoverTests(CSharpScript sourceScript) ["line_number"] = descriptor.LineNumber, ["attribute_index"] = descriptor.AttributeIndex, ["require_godot_runtime"] = descriptor.RequireRunningGodotEngine, - ["code_file_path"] = descriptor.CodeFilePath ?? "", + ["code_file_path"] = descriptor.CodeFilePath ?? string.Empty, ["simple_name"] = descriptor.SimpleName, ["fully_qualified_name"] = descriptor.FullyQualifiedName, ["assembly_location"] = descriptor.AssemblyPath }) - .Aggregate(new Array(), (array, dict) => - { - array.Add(dict); - return array; - }); + .Aggregate( + new Array(), + (array, dict) => + { + array.Add(dict); + return array; + }); } +#pragma warning disable CA1031 catch (Exception e) +#pragma warning restore CA1031 { GD.PrintErr($"Error discovering tests: {e.Message}\n{e.StackTrace}"); +#pragma warning disable IDE0028 // Do not catch general exception types return new Array(); +#pragma warning restore IDE0028 // Do not catch general exception types } } + /// + /// Creates a test suite based on the specified source path and line number. + /// + /// The path to the source file from which to create the test suite. + /// The line number in the source file where the method to test is defined. + /// The path where the test suite should be created. + /// A dictionary containing information about the created test suite. + public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) + => GdUnit4NetApiGodotBridge.CreateTestSuite(sourcePath, lineNumber, testSuitePath); + + /// + /// Gets the version of the GdUnit4 assembly. + /// + /// The version string of the GdUnit4 assembly. + public static string Version() + => GdUnit4NetApiGodotBridge.Version(); + + /// + public override void _Notification(int what) + { + if (what != NotificationPredelete) + return; + executionCts?.Dispose(); + executionCts = null; + } + + /// + /// Executes the tests and using the listener for reporting the results. + /// + /// A list of tests to be executed. + /// The listener to report the results. public void ExecuteAsync(Array tests, Callable listener) { try @@ -135,34 +131,38 @@ public void ExecuteAsync(Array tests, Callable listener) // Create new cancellation token source executionCts = new CancellationTokenSource(); + Debug.Assert(tests != null, nameof(tests) + " != null"); var testSuiteNodes = new List { BuildTestSuiteNodeFrom(tests) }; - InvokeApiMethod("ExecuteAsync", testSuiteNodes, listener, executionCts.Token)? + GdUnit4NetApiGodotBridge.ExecuteAsync(testSuiteNodes, listener, executionCts.Token) .GetAwaiter() .OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); } +#pragma warning disable CA1031 catch (Exception e) +#pragma warning restore CA1031 { GD.PrintErr($"Error executing tests: {e.Message}\n{e.StackTrace}"); Task.Run(() => { }).GetAwaiter().OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted)); } } + /// + /// Will cancel the current test execution. + /// public void CancelExecution() { try { executionCts?.Cancel(); } +#pragma warning disable CA1031 catch (Exception e) +#pragma warning restore CA1031 { GD.PrintErr($"Error cancelling execution: {e.Message}"); } } - public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) - => InvokeApiMethod("CreateTestSuite", sourcePath, lineNumber, testSuitePath)!; - - // Convert a set of Tests stored as Dictionaries to TestSuiteNode // all tests are assigned to a single test suit internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) @@ -180,15 +180,14 @@ internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) // Create TestCaseNodes for each test in the suite var testCaseNodes = tests .Select(test => new TestCaseNode - { - Id = Guid.Parse(test["guid"].AsString()), - ParentId = suiteId, - ManagedMethod = test["test_name"].AsString(), - LineNumber = test["line_number"].AsInt32(), - AttributeIndex = test["attribute_index"].AsInt32(), - RequireRunningGodotEngine = test["require_godot_runtime"].AsBool() - } - ) + { + Id = Guid.Parse(test["guid"].AsString()), + ParentId = suiteId, + ManagedMethod = test["test_name"].AsString(), + LineNumber = test["line_number"].AsInt32(), + AttributeIndex = test["attribute_index"].AsInt32(), + RequireRunningGodotEngine = test["require_godot_runtime"].AsBool() + }) .ToList(); return new TestSuiteNode @@ -202,3 +201,35 @@ internal static TestSuiteNode BuildTestSuiteNodeFrom(Array tests) }; } } +#else +using Godot; +using Godot.Collections; + +public partial class GdUnit4CSharpApi : RefCounted +{ + [Signal] + public delegate void ExecutionCompletedEventHandler(); + + public static bool IsApiLoaded() + { + GD.PushWarning("No `gdunit4.api` dependency found, check your project dependencies."); + return false; + } + + + public static string Version() + => "Unknown"; + + public static Array DiscoverTests(CSharpScript sourceScript) => new(); + + public void ExecuteAsync(Array tests, Callable listener) + { + } + + public static bool IsTestSuite(CSharpScript script) + => false; + + public static Dictionary CreateTestSuite(string sourcePath, int lineNumber, string testSuitePath) + => new(); +} +#endif diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd index 398afbe9..3d8ba25f 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd @@ -32,7 +32,7 @@ static var _test_event_listener := TestEventListener.new() ## Returns an instance of the GdUnit4CSharpApi wrapper.[br] ## @return Script: The loaded C# wrapper or null if .NET is not supported static func instance() -> Script: - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return null return _gdUnit4NetWrapper @@ -41,7 +41,7 @@ static func instance() -> Script: ## Returns or creates a single instance of the API [br] ## This improves performance by reusing the same object static func api_instance() -> RefCounted: - if _api_instance == null and is_dotnet_supported(): + if _api_instance == null and is_api_loaded(): @warning_ignore("unsafe_method_access") _api_instance = instance().new() return _api_instance @@ -52,14 +52,8 @@ static func is_engine_version_supported(engine_version: int = Engine.get_version ## Checks if the .NET environment is properly configured and available.[br] -## This performs multiple checks:[br] -## 1. Verifies if the wrapper is already loaded[br] -## 2. Confirms Godot has C# support[br] -## 3. Validates the project's C# configuration[br] -## 4. Attempts to load the wrapper and find the GdUnit4 assembly[br] -## ## @return bool: True if .NET is fully supported and the assembly is found -static func is_dotnet_supported() -> bool: +static func is_api_loaded() -> bool: # If the wrapper is already loaded we don't need to check again if _gdUnit4NetWrapper != null: return true @@ -74,13 +68,14 @@ static func is_dotnet_supported() -> bool: # Finally load the wrapper and check if the GdUnit4 assembly can be found _gdUnit4NetWrapper = load("res://addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs") - return _gdUnit4NetWrapper != null and _gdUnit4NetWrapper.call("FindGdUnit4NetAssembly") + @warning_ignore("unsafe_method_access") + return _gdUnit4NetWrapper.call("IsApiLoaded") ## Returns the version of the GdUnit4 .NET assembly.[br] ## @return String: The version string or "unknown" if .NET is not supported static func version() -> String: - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return "unknown" @warning_ignore("unsafe_method_access") return instance().Version() @@ -105,7 +100,7 @@ static func execute(tests: Array[GdUnitTestCase]) -> void: static func create_test_suite(source_path: String, line_number: int, test_suite_path: String) -> GdUnitResult: - if not GdUnit4CSharpApiLoader.is_dotnet_supported(): + if not GdUnit4CSharpApiLoader.is_api_loaded(): return GdUnitResult.error("Can't create test suite. No .NET support found.") @warning_ignore("unsafe_method_access") var result: Dictionary = instance().CreateTestSuite(source_path, line_number, test_suite_path) @@ -114,11 +109,6 @@ static func create_test_suite(source_path: String, line_number: int, test_suite_ return GdUnitResult.success(result) -static func is_test_suite(script: Script) -> bool: - @warning_ignore("unsafe_method_access") - return instance().IsTestSuite(script) - - static func is_csharp_file(resource_path: String) -> bool: var ext := resource_path.get_extension() - return ext == "cs" and GdUnit4CSharpApiLoader.is_dotnet_supported() + return ext == "cs" and GdUnit4CSharpApiLoader.is_api_loaded() diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid index 0d1c56ab..72d32e0f 100644 --- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid +++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid @@ -1 +1 @@ -uid://em6wu2kmyv05 +uid://dhticpi2o00pb diff --git a/addons/gdUnit4/src/doubler/CallableDoubler.gd b/addons/gdUnit4/src/doubler/CallableDoubler.gd index 14a5947d..1bc78a1a 100644 --- a/addons/gdUnit4/src/doubler/CallableDoubler.gd +++ b/addons/gdUnit4/src/doubler/CallableDoubler.gd @@ -58,20 +58,8 @@ static func callable_functions() -> PackedStringArray: ## Callable functions stubing ## ----------------------------------------------------------------------------------------------------------------------------------------- -@warning_ignore("untyped_declaration") -func bind(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Callable: - # save - var bind_values: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - _cb = _cb.bindv(bind_values) +func bind(...varargs: Array) -> Callable: + _cb = _cb.bindv(varargs) return _cb @@ -80,34 +68,19 @@ func bindv(caller_args: Array) -> Callable: return _cb -@warning_ignore("untyped_declaration", "native_method_override", "unused_parameter") -func call(arg0=null, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> Variant: - - # This is a placeholder function signanture without any functionallity! - # It is used by the function doubler to double function signature of Callable:call() - # The doubled function calls direct _cb.callv() see GdUnitSpyFunctionDoubler:TEMPLATE_CALLABLE_CALL template - assert(false) - return null +@warning_ignore("native_method_override") +func call(...varargs: Array) -> Variant: + return _cb.callv(varargs) # Is not supported, see class description -#func call_deferred(a) -> void: -# pass +#func call_deferred(...varargs: Array) -> void: +# return _cb.call_deferred(varargs) # Is not supported, see class description -#func callv(a) -> void: -# pass - +#func callv(arguments: Array) -> Variant: +# return _cb.callv(arguments) func get_bound_arguments() -> Array: @@ -150,60 +123,33 @@ func is_valid() -> bool: return _cb.is_valid() -@warning_ignore("untyped_declaration") -func rpc(arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void: - - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - match args.size(): - 0: _cb.rpc(0) - 1: _cb.rpc(args[0]) - 2: _cb.rpc(args[0], args[1]) - 3: _cb.rpc(args[0], args[1], args[2]) - 4: _cb.rpc(args[0], args[1], args[2], args[3]) - 5: _cb.rpc(args[0], args[1], args[2], args[3], args[4]) - 6: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5]) - 7: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6]) - 8: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]) - 9: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) - 10: _cb.rpc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]) +func rpc(...varargs: Array) -> void: + match varargs.size(): + 0: _cb.rpc() + 1: _cb.rpc(varargs[0]) + 2: _cb.rpc(varargs[0], varargs[1]) + 3: _cb.rpc(varargs[0], varargs[1], varargs[2]) + 4: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4]) + 5: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5]) + 6: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6]) + 7: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7]) + 8: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8]) + 9: _cb.rpc(varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8], varargs[9]) @warning_ignore("untyped_declaration") -func rpc_id(peer_id: int, - arg0=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg1=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg2=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg3=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg4=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg5=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg6=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg7=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg8=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE, - arg9=GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) -> void: - - var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE) - match args.size(): - 0: _cb.rpc_id(peer_id) - 1: _cb.rpc_id(peer_id, args[0]) - 2: _cb.rpc_id(peer_id, args[0], args[1]) - 3: _cb.rpc_id(peer_id, args[0], args[1], args[2]) - 4: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3]) - 5: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4]) - 6: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5]) - 7: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6]) - 8: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]) - 9: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]) - 10: _cb.rpc_id(peer_id, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]) - +func rpc_id(peer_id: int, ...varargs: Array) -> void: + match varargs.size(): + 0: _cb.rpc_id(peer_id ) + 1: _cb.rpc_id(peer_id, varargs[0]) + 2: _cb.rpc_id(peer_id, varargs[0], varargs[1]) + 3: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2]) + 4: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4]) + 5: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5]) + 6: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6]) + 7: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7]) + 8: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8]) + 9: _cb.rpc_id(peer_id, varargs[0], varargs[1], varargs[2], varargs[3], varargs[4], varargs[5], varargs[6], varargs[7], varargs[8], varargs[9]) func unbind(argcount: int) -> Callable: _cb = _cb.unbind(argcount) diff --git a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid index 9c937dca..1345a094 100644 --- a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid @@ -1 +1 @@ -uid://c44li8m22q2g +uid://byguldi4c1h5 diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd index 892e65c8..d9459dc3 100644 --- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd +++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd @@ -1,222 +1,4 @@ -class_name GdFunctionDoubler +@abstract class_name GdFunctionDoubler extends RefCounted -const DEFAULT_TYPED_RETURN_VALUES := { - TYPE_NIL: "null", - TYPE_BOOL: "false", - TYPE_INT: "0", - TYPE_FLOAT: "0.0", - TYPE_STRING: "\"\"", - TYPE_STRING_NAME: "&\"\"", - TYPE_VECTOR2: "Vector2.ZERO", - TYPE_VECTOR2I: "Vector2i.ZERO", - TYPE_RECT2: "Rect2()", - TYPE_RECT2I: "Rect2i()", - TYPE_VECTOR3: "Vector3.ZERO", - TYPE_VECTOR3I: "Vector3i.ZERO", - TYPE_VECTOR4: "Vector4.ZERO", - TYPE_VECTOR4I: "Vector4i.ZERO", - TYPE_TRANSFORM2D: "Transform2D()", - TYPE_PLANE: "Plane()", - TYPE_QUATERNION: "Quaternion()", - TYPE_AABB: "AABB()", - TYPE_BASIS: "Basis()", - TYPE_TRANSFORM3D: "Transform3D()", - TYPE_PROJECTION: "Projection()", - TYPE_COLOR: "Color()", - TYPE_NODE_PATH: "NodePath()", - TYPE_RID: "RID()", - TYPE_OBJECT: "null", - TYPE_CALLABLE: "Callable()", - TYPE_SIGNAL: "Signal()", - TYPE_DICTIONARY: "Dictionary()", - TYPE_ARRAY: "Array()", - TYPE_PACKED_BYTE_ARRAY: "PackedByteArray()", - TYPE_PACKED_INT32_ARRAY: "PackedInt32Array()", - TYPE_PACKED_INT64_ARRAY: "PackedInt64Array()", - TYPE_PACKED_FLOAT32_ARRAY: "PackedFloat32Array()", - TYPE_PACKED_FLOAT64_ARRAY: "PackedFloat64Array()", - TYPE_PACKED_STRING_ARRAY: "PackedStringArray()", - TYPE_PACKED_VECTOR2_ARRAY: "PackedVector2Array()", - TYPE_PACKED_VECTOR3_ARRAY: "PackedVector3Array()", - # since Godot 4.3.beta1 TYPE_PACKED_VECTOR4_ARRAY = 38 - GdObjects.TYPE_PACKED_VECTOR4_ARRAY: "PackedVector4Array()", - TYPE_PACKED_COLOR_ARRAY: "PackedColorArray()", - GdObjects.TYPE_VARIANT: "null", - GdObjects.TYPE_ENUM: "0" -} - -# @GlobalScript enums -# needs to manually map because of https://github.com/godotengine/godot/issues/73835 -const DEFAULT_ENUM_RETURN_VALUES = { - "Side" : "SIDE_LEFT", - "Corner" : "CORNER_TOP_LEFT", - "Orientation" : "HORIZONTAL", - "ClockDirection" : "CLOCKWISE", - "HorizontalAlignment" : "HORIZONTAL_ALIGNMENT_LEFT", - "VerticalAlignment" : "VERTICAL_ALIGNMENT_TOP", - "InlineAlignment" : "INLINE_ALIGNMENT_TOP_TO", - "EulerOrder" : "EULER_ORDER_XYZ", - "Key" : "KEY_NONE", - "KeyModifierMask" : "KEY_CODE_MASK", - "MouseButton" : "MOUSE_BUTTON_NONE", - "MouseButtonMask" : "MOUSE_BUTTON_MASK_LEFT", - "JoyButton" : "JOY_BUTTON_INVALID", - "JoyAxis" : "JOY_AXIS_INVALID", - "MIDIMessage" : "MIDI_MESSAGE_NONE", - "Error" : "OK", - "PropertyHint" : "PROPERTY_HINT_NONE", - "Variant.Type" : "TYPE_NIL", -} - -var _push_errors :String - - -# Determine the enum default by reflection -static func get_enum_default(value :String) -> Variant: - var script := GDScript.new() - script.source_code = """ - extends Resource - - static func get_enum_default() -> Variant: - return %s.values()[0] - - """.dedent() % value - @warning_ignore("return_value_discarded") - script.reload() - @warning_ignore("unsafe_method_access") - return script.new().call("get_enum_default") - - -static func default_return_value(func_descriptor :GdFunctionDescriptor) -> String: - var return_type :Variant = func_descriptor.return_type() - if return_type == GdObjects.TYPE_ENUM: - var enum_class := func_descriptor._return_class - var enum_path := enum_class.split(".") - if enum_path.size() >= 2: - var keys := ClassDB.class_get_enum_constants(enum_path[0], enum_path[1]) - if not keys.is_empty(): - return "%s.%s" % [enum_path[0], keys[0]] - var enum_value :Variant = get_enum_default(enum_class) - if enum_value != null: - return str(enum_value) - # we need fallback for @GlobalScript enums, - return DEFAULT_ENUM_RETURN_VALUES.get(func_descriptor._return_class, "0") - return DEFAULT_TYPED_RETURN_VALUES.get(return_type, "invalid") - - -func _init(push_errors :bool = false) -> void: - _push_errors = "true" if push_errors else "false" - for type_key in TYPE_MAX: - if not DEFAULT_TYPED_RETURN_VALUES.has(type_key): - push_error("missing default definitions! Expexting %d bud is %d" % [DEFAULT_TYPED_RETURN_VALUES.size(), TYPE_MAX]) - prints("missing default definition for type", type_key) - assert(DEFAULT_TYPED_RETURN_VALUES.has(type_key), "Missing Type default definition!") - - -@warning_ignore("unused_parameter") -func get_template(return_type: GdFunctionDescriptor, is_callable: bool) -> String: - assert(false, "'get_template' must be implemented!") - return "" - - -func double(func_descriptor: GdFunctionDescriptor, is_callable: bool = false) -> PackedStringArray: - var is_coroutine := func_descriptor.is_coroutine() - var func_name := func_descriptor.name() - var args := func_descriptor.args() - var varargs := func_descriptor.varargs() - var return_value := GdFunctionDoubler.default_return_value(func_descriptor) - var arg_names := extract_arg_names(args, true) - var vararg_names := extract_arg_names(varargs) - - # save original constructor arguments - if func_name == "_init": - var constructor_args := ",".join(GdFunctionDoubler.extract_constructor_args(args)) - var constructor := """ - func _init(%s) -> void: - Engine.set_meta(__INSTANCE_ID, self) - @warning_ignore("unsafe_call_argument") - super(%s) - - """.dedent() % [constructor_args, ", ".join(arg_names)] - return constructor.split("\n") - - var double_src := "@warning_ignore('shadowed_variable', 'untyped_declaration')\n" - if func_descriptor.is_engine(): - double_src += '@warning_ignore("native_method_override")\n' - double_src += GdFunctionDoubler.extract_func_signature(func_descriptor) - # fix to unix format, this is need when the template is edited under windows than the template is stored with \r\n - var func_template := get_template(func_descriptor, is_callable).replace("\r\n", "\n") - double_src += func_template\ - .replace("$(arguments)", ", ".join(arg_names))\ - .replace("$(varargs)", ", ".join(vararg_names))\ - .replace("$(await)", GdFunctionDoubler.await_is_coroutine(is_coroutine)) \ - .replace("$(func_name)", func_name )\ - .replace("${default_return_value}", return_value)\ - .replace("$(push_errors)", _push_errors) - - if func_descriptor.return_type() == GdObjects.TYPE_ENUM: - double_src = double_src.replace("$(return_as)", " as " + func_descriptor.return_type_as_string()) - else: - double_src = double_src.replace("$(return_as)", "") - - return double_src.split("\n") - - -func extract_arg_names(argument_signatures: Array[GdFunctionArgument], add_suffix := false) -> PackedStringArray: - var arg_names := PackedStringArray() - for arg in argument_signatures: - @warning_ignore("return_value_discarded") - arg_names.append(arg._name + ("_" if add_suffix else "")) - return arg_names - - -static func extract_constructor_args(args :Array[GdFunctionArgument]) -> PackedStringArray: - var constructor_args := PackedStringArray() - for arg in args: - var arg_name := arg._name + "_" - var default_value := get_default(arg) - if default_value == "null": - @warning_ignore("return_value_discarded") - constructor_args.append(arg_name + ":Variant=" + default_value) - else: - @warning_ignore("return_value_discarded") - constructor_args.append(arg_name + ":=" + default_value) - return constructor_args - - -static func extract_func_signature(descriptor: GdFunctionDescriptor) -> String: - var func_signature := "" - if descriptor._return_type == TYPE_NIL: - func_signature = "func %s(%s) -> void:" % [descriptor.name(), typeless_args(descriptor)] - elif descriptor._return_type == GdObjects.TYPE_VARIANT: - func_signature = "func %s(%s):" % [descriptor.name(), typeless_args(descriptor)] - else: - func_signature = "func %s(%s) -> %s:" % [descriptor.name(), typeless_args(descriptor), descriptor.return_type_as_string()] - return "static " + func_signature if descriptor.is_static() else func_signature - - -static func typeless_args(descriptor: GdFunctionDescriptor) -> String: - var collect := PackedStringArray() - for arg in descriptor.args(): - if arg.has_default(): - @warning_ignore("return_value_discarded") - collect.push_back(arg.name() + "_" + "=" + arg.value_as_string()) - else: - @warning_ignore("return_value_discarded") - collect.push_back(arg.name() + "_") - for arg in descriptor.varargs(): - @warning_ignore("return_value_discarded") - collect.push_back(arg.name() + "=" + arg.value_as_string()) - return ", ".join(collect) - - -static func get_default(arg :GdFunctionArgument) -> String: - if arg.has_default(): - return arg.value_as_string() - else: - return DEFAULT_TYPED_RETURN_VALUES.get(arg.type(), "null") - - -static func await_is_coroutine(is_coroutine :bool) -> String: - return "await " if is_coroutine else "" +@abstract func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid index e0c3e24e..79ef5ff9 100644 --- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid @@ -1 +1 @@ -uid://c7snbahh8hwm3 +uid://cvhcxm0tgneol diff --git a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd index ee02fff8..31aa06bc 100644 --- a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd +++ b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd @@ -2,7 +2,6 @@ class_name GdUnitClassDoubler extends RefCounted - const DOUBLER_INSTANCE_ID_PREFIX := "gdunit_doubler_instance_id_" const EXCLUDE_VIRTUAL_FUNCTIONS = [ # we have to exclude notifications because NOTIFICATION_PREDELETE is try @@ -29,23 +28,23 @@ static func check_leaked_instances() -> void: ## we check that all registered spy/mock instances are removed from the engine meta data for key in Engine.get_meta_list(): if key.begins_with(DOUBLER_INSTANCE_ID_PREFIX): - var instance :Variant = Engine.get_meta(key) + var instance: Variant = Engine.get_meta(key) push_error("GdUnit internal error: an spy/mock instance '%s', class:'%s' is not removed from the engine and will lead in a leaked instance!" % [instance, instance.__SOURCE_CLASS]) + await (Engine.get_main_loop() as SceneTree).process_frame # loads the doubler template # class_info = { "class_name": <>, "class_path" : <>} -static func load_template(template: String, class_info: Dictionary, instance: Object) -> PackedStringArray: - # store instance id +static func load_template(template: String, class_info: Dictionary) -> PackedStringArray: var clazz_name: String = class_info.get("class_name") var source_code := template\ - .replace("${instance_id}", "%s%d" % [DOUBLER_INSTANCE_ID_PREFIX, abs(instance.get_instance_id())])\ .replace("${source_class}", clazz_name)\ # Replace template class_name DoubledClass with source class name .replace("SourceClassName", clazz_name.replace(".", "_")) var lines := GdScriptParser.to_unix_format(source_code).split("\n") @warning_ignore("return_value_discarded") lines.insert(1, extends_clazz(class_info)) + lines.insert(0, "@warning_ignore_start('unsafe_call_argument', 'shadowed_variable', 'untyped_declaration', 'native_method_override', 'int_as_enum_without_cast')") return lines @@ -80,7 +79,7 @@ static func double_functions(instance: Object, clazz_name: String, clazz_path: P continue if functions.has(func_descriptor.name()) or exclude_override_functions.has(func_descriptor.name()): continue - doubled_source += func_doubler.double(func_descriptor, instance is CallableDoubler) + doubled_source += func_doubler.double(func_descriptor) functions.append(func_descriptor.name()) # double regular class functions @@ -101,7 +100,7 @@ static func double_functions(instance: Object, clazz_name: String, clazz_path: P #prints("no virtual func implemented",clazz_name, func_descriptor.name() ) continue functions.append(func_descriptor.name()) - doubled_source.append_array(func_doubler.double(func_descriptor, instance is CallableDoubler)) + doubled_source.append_array(func_doubler.double(func_descriptor)) return doubled_source diff --git a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid index c85f07a2..1789ffe0 100644 --- a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid @@ -1 +1 @@ -uid://by7gx6owx6r0u +uid://fgprwti60rrx diff --git a/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd new file mode 100644 index 00000000..a1a75f11 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd @@ -0,0 +1,336 @@ +class_name GdUnitFunctionDoublerBuilder +extends RefCounted + +const TYPE_VOID = GdObjects.TYPE_VOID +const TYPE_VARIANT = GdObjects.TYPE_VARIANT +const TYPE_VARARG = GdObjects.TYPE_VARARG +const TYPE_FUNC = GdObjects.TYPE_FUNC +const TYPE_FUZZER = GdObjects.TYPE_FUZZER +const TYPE_ENUM = GdObjects.TYPE_ENUM + +const DEFAULT_TYPED_RETURN_VALUES := { + TYPE_NIL: "null", + TYPE_BOOL: "false", + TYPE_INT: "0", + TYPE_FLOAT: "0.0", + TYPE_STRING: "\"\"", + TYPE_STRING_NAME: "&\"\"", + TYPE_VECTOR2: "Vector2.ZERO", + TYPE_VECTOR2I: "Vector2i.ZERO", + TYPE_RECT2: "Rect2()", + TYPE_RECT2I: "Rect2i()", + TYPE_VECTOR3: "Vector3.ZERO", + TYPE_VECTOR3I: "Vector3i.ZERO", + TYPE_VECTOR4: "Vector4.ZERO", + TYPE_VECTOR4I: "Vector4i.ZERO", + TYPE_TRANSFORM2D: "Transform2D()", + TYPE_PLANE: "Plane()", + TYPE_QUATERNION: "Quaternion()", + TYPE_AABB: "AABB()", + TYPE_BASIS: "Basis()", + TYPE_TRANSFORM3D: "Transform3D()", + TYPE_PROJECTION: "Projection()", + TYPE_COLOR: "Color()", + TYPE_NODE_PATH: "NodePath()", + TYPE_RID: "RID()", + TYPE_OBJECT: "null", + TYPE_CALLABLE: "Callable()", + TYPE_SIGNAL: "Signal()", + TYPE_DICTIONARY: "Dictionary()", + TYPE_ARRAY: "Array()", + TYPE_PACKED_BYTE_ARRAY: "PackedByteArray()", + TYPE_PACKED_INT32_ARRAY: "PackedInt32Array()", + TYPE_PACKED_INT64_ARRAY: "PackedInt64Array()", + TYPE_PACKED_FLOAT32_ARRAY: "PackedFloat32Array()", + TYPE_PACKED_FLOAT64_ARRAY: "PackedFloat64Array()", + TYPE_PACKED_STRING_ARRAY: "PackedStringArray()", + TYPE_PACKED_VECTOR2_ARRAY: "PackedVector2Array()", + TYPE_PACKED_VECTOR3_ARRAY: "PackedVector3Array()", + TYPE_PACKED_VECTOR4_ARRAY: "PackedVector4Array()", + TYPE_PACKED_COLOR_ARRAY: "PackedColorArray()", + GdObjects.TYPE_VARIANT: "null", + GdObjects.TYPE_ENUM: "0" +} + + +# @GlobalScript enums +# needs to manually map because of https://github.com/godotengine/godot/issues/73835 +const DEFAULT_ENUM_RETURN_VALUES = { + "Side" : "SIDE_LEFT", + "Corner" : "CORNER_TOP_LEFT", + "Orientation" : "HORIZONTAL", + "ClockDirection" : "CLOCKWISE", + "HorizontalAlignment" : "HORIZONTAL_ALIGNMENT_LEFT", + "VerticalAlignment" : "VERTICAL_ALIGNMENT_TOP", + "InlineAlignment" : "INLINE_ALIGNMENT_TOP_TO", + "EulerOrder" : "EULER_ORDER_XYZ", + "Key" : "KEY_NONE", + "KeyModifierMask" : "KEY_CODE_MASK", + "MouseButton" : "MOUSE_BUTTON_NONE", + "MouseButtonMask" : "MOUSE_BUTTON_MASK_LEFT", + "JoyButton" : "JOY_BUTTON_INVALID", + "JoyAxis" : "JOY_AXIS_INVALID", + "MIDIMessage" : "MIDI_MESSAGE_NONE", + "Error" : "OK", + "PropertyHint" : "PROPERTY_HINT_NONE", + "Variant.Type" : "TYPE_NIL", + "Vector2.Axis" : "Vector2.AXIS_X", + "Vector2i.Axis" : "Vector2i.AXIS_X", + "Vector3.Axis" : "Vector3.AXIS_X", + "Vector3i.Axis" : "Vector3i.AXIS_X", + "Vector4.Axis" : "Vector4.AXIS_X", + "Vector4i.Axis" : "Vector4i.AXIS_X", +} + + +static var def_constructor := """ + func _init({constructor_args}) -> void: + __init_doubler() + super({args}) + """.dedent() + + +static var def_verify_block := """ + # verify block + var __verifier := __get_verifier() + if __verifier != null: + if __verifier.is_verify_interactions(): + __verifier.verify_interactions("{func_name}", __args) + {default_return} + else: + __verifier.save_function_interaction("{func_name}", __args) + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_prepare_block := """ + if __is_prepare_return_value(): + __save_function_return_value("{func_name}", __args) + {default_return} + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_void_prepare_block := """ + if __is_prepare_return_value(): + push_error("Mocking functions with return type void is not allowed!") + return + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_mock_return := """ + if __is_do_not_call_real_func("{func_name}", __args): + return __return_mock_value("{func_name}", __args, {default_return}) + """.dedent().indent("\t").trim_suffix("\n") + + +static var def_void_mock_return := """ + if __is_do_not_call_real_func("{func_name}", __args): + return + """.dedent().indent("\t").trim_suffix("\n") + + +var fd: GdFunctionDescriptor +var func_args: Array +var default_return: String +var verify_block: String = "" +var prepare_block: String = "" +var mock_return: String = "" + + +func _init(descriptor: GdFunctionDescriptor) -> void: + # verify all default types are covered + for type_key in TYPE_MAX: + if not DEFAULT_TYPED_RETURN_VALUES.has(type_key): + push_error("missing default definitions! Expexting %d bud is %d" % [DEFAULT_TYPED_RETURN_VALUES.size(), TYPE_MAX]) + prints("missing default definition for type", type_key) + assert(DEFAULT_TYPED_RETURN_VALUES.has(type_key), "Missing Type default definition!") + + fd = descriptor + func_args = argument_names() + default_return = default_return_value() + + +func build_func_signature() -> String: + var return_type := ":" if fd._return_type == TYPE_VARIANT else " -> %s:" % fd.return_type_as_string() + return "{static}func {func_name}({args}){return_type}".format({ + "static" : "static " if fd.is_static() else "", + "func_name": fd.name(), + "args": arguments_full_quilified(), + "return_type": return_type + }) + + +func arguments_full_quilified() -> String: + var collect := PackedStringArray() + for arg in fd.args(): + var name := argument_name(arg) + if arg.has_default(): + var signature := "{argument_name}{arg_typed}={arg_value}".format({ + "argument_name" : name, + "arg_typed" : ":"+GdObjects.type_as_string(arg.type()) if arg.type() == GdObjects.TYPE_VARIANT else "", + "arg_value" : arg.value_as_string() + }) + collect.push_back(signature) + else: + collect.push_back(name) + if fd.is_vararg(): + var arg_descriptor := fd.varargs()[0] + collect.push_back("...%s_: Array" % arg_descriptor.name()) + return ", ".join(collect) + + +func argument_name(arg: GdFunctionArgument) -> String: + return arg.name() + "_" + + +func argument_names() -> PackedStringArray: + return fd.args().map(argument_name) + + +func argument_default(arg :GdFunctionArgument) -> String: + return (arg.value_as_string() + if arg.has_default() + else DEFAULT_TYPED_RETURN_VALUES.get(arg.type(), "null")) + + +func build_constructor_arguments() -> String: + var arguments := PackedStringArray() + for arg in fd.args(): + var default_value := argument_default(arg) + var arg_signature := "{name}:{type}={default}".format({ + "name" : argument_name(arg), + "type" : "Variant" if default_value == "null" else "", + "default" : default_value + }) + arguments.append(arg_signature) + if fd.is_vararg(): + arguments.append("...varargs: Array") + return ", ".join(arguments) + + +func build_arguments() -> String: + return "\tvar __args := [{args}]{varargs}".format({ + "args" : ", ".join(func_args), + "varargs" : " + varargs_" if fd.is_vararg() else "" + }) + + +func build_super_calls() -> String: + if !fd.is_vararg(): + return 'super(%s)\n' % ", ".join(func_args) + + var match_block := "match varargs_.size():\n" + for index in range(0, 11): + match_block += '{index}: super({args})\n'.format({ + "index" : index, + "args" : ", ".join(func_args + build_vararg_list(index)) + }).indent("\t") + match_block += '_: push_error("To many varradic arguments.")\n'.indent("\t") + match_block += "return\n" if is_void_func() else "return %s\n" % default_return + return match_block + + +func build_vararg_list(count: int) -> Array: + var arg_list := [] + for index in count: + arg_list.append("varargs_[%d]" % index) + return arg_list + + +func default_return_value() -> String: + var return_type: Variant = fd.return_type() + if return_type == GdObjects.TYPE_ENUM: + var enum_class := fd._return_class + if DEFAULT_ENUM_RETURN_VALUES.has(enum_class): + return DEFAULT_ENUM_RETURN_VALUES.get(fd._return_class, "0") + + var enum_path := enum_class.split(".") + if enum_path.size() >= 2: + var keys := ClassDB.class_get_enum_constants(enum_path[0], enum_path[1]) + if not keys.is_empty(): + return "%s.%s" % [enum_path[0], keys[0]] + var enum_value: Variant = get_enum_default(enum_class) + if enum_value != null: + return str(enum_value) + # we need fallback for @GlobalScript enums, + return DEFAULT_ENUM_RETURN_VALUES.get(fd._return_class, "0") + return DEFAULT_TYPED_RETURN_VALUES.get(return_type, "invalid") + + +# Determine the enum default by reflection +func get_enum_default(value: String) -> Variant: + var script := GDScript.new() + script.source_code = """ + extends RefCounted + + static func get_enum_default() -> Variant: + return %s.values()[0] + + """.dedent() % value + var err := script.reload() + if err != OK: + push_error("Cant get enum values form '%s', %s" % [value, error_string(err)]) + return 0 + @warning_ignore("unsafe_method_access") + return script.new().call("get_enum_default") + + +func is_void_func() -> bool: + return fd.return_type() == TYPE_NIL or fd.return_type() == TYPE_VOID + + +func with_verify_block() -> GdUnitFunctionDoublerBuilder: + verify_block = def_verify_block.format({ + "func_name" : fd.name(), + "default_return" : "return" if is_void_func() else "return " + default_return + }) + return self + + +func with_prepare_block() -> GdUnitFunctionDoublerBuilder: + if fd.return_type() == TYPE_NIL or fd.return_type() == GdObjects.TYPE_VOID: + prepare_block = def_void_prepare_block + return self + + prepare_block = def_prepare_block.format({ + "func_name" : fd.name(), + "default_return" : "return" if is_void_func() else "return " + default_return + }) + return self + + +func with_mocked_return_value() -> GdUnitFunctionDoublerBuilder: + if is_void_func(): + mock_return = def_void_mock_return.format({ + "func_name" : fd.name(), + }) + else: + mock_return = def_mock_return.format({ + "func_name" : fd.name(), + "default_return" : '"no_arg"' if is_void_func() else default_return + }) + return self + + +func build() -> PackedStringArray: + if fd.name() == "_init": + return [def_constructor.format({ + "constructor_args" : build_constructor_arguments(), + "args" : ", ".join(func_args) + })] + + var func_body: PackedStringArray = [] + func_body.append(build_func_signature()) + func_body.append(build_arguments()) + if not prepare_block.is_empty(): + func_body.append(prepare_block) + func_body.append(verify_block) + if not mock_return.is_empty(): + func_body.append(mock_return) + func_body.append("") + var super_calls := build_super_calls() + if not is_void_func(): + super_calls = super_calls.replace("super(", "return super(" ) + if fd.is_coroutine(): + super_calls = super_calls.replace("super(", "await super(" ) + func_body.append(super_calls.indent("\t")) + return func_body diff --git a/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid new file mode 100644 index 00000000..a7e034bf --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitFunctionDoublerBuilder.gd.uid @@ -0,0 +1 @@ +uid://connajdktx8re diff --git a/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd new file mode 100644 index 00000000..d735bc56 --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd @@ -0,0 +1,10 @@ +class_name GdUnitMockFunctionDoubler +extends GdFunctionDoubler + + +func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray: + return GdUnitFunctionDoublerBuilder.new(func_descriptor)\ + .with_prepare_block()\ + .with_verify_block()\ + .with_mocked_return_value()\ + .build() diff --git a/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid new file mode 100644 index 00000000..eccddccf --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitMockFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://chcileiyxo43f diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid index 742a23df..d768074f 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid @@ -1 +1 @@ -uid://bx7ng06gj6xt2 +uid://c03tdup7rc30i diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd index d0b17ce1..36aa9783 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd @@ -5,7 +5,8 @@ var saved_interactions := Dictionary() var verified_interactions := Array() -func save_function_interaction(function_args :Array[Variant]) -> void: +func save_function_interaction(func_name: String, args :Array[Variant]) -> void: + var function_args := [func_name] + args var matcher := GdUnitArgumentMatchers.to_matcher(function_args, true) for index in saved_interactions.keys().size(): var key: Variant = saved_interactions.keys()[index] @@ -23,9 +24,10 @@ func do_verify_interactions(interactions_times: int = 1) -> void: expected_interactions = interactions_times -func verify_interactions(function_args: Array[Variant]) -> void: +func verify_interactions(func_name: String, args: Array[Variant]) -> void: var summary := Dictionary() var total_interactions := 0 + var function_args := [func_name] + args var matcher := GdUnitArgumentMatchers.to_matcher(function_args, true) for index in saved_interactions.keys().size(): var key: Variant = saved_interactions.keys()[index] @@ -80,13 +82,3 @@ func verify_no_more_interactions() -> Dictionary: func reset_interactions() -> void: saved_interactions.clear() - - -func filter_vargs(arg_values: Array[Variant]) -> Array[Variant]: - var filtered: Array[Variant] = [] - for index in arg_values.size(): - var arg: Variant = arg_values[index] - if typeof(arg) == TYPE_STRING and arg == GdObjects.TYPE_VARARG_PLACEHOLDER_VALUE: - continue - filtered.append(arg) - return filtered diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid index 3db11b0b..eaca6ae8 100644 --- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid +++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid @@ -1 +1 @@ -uid://7ni6p5fmj303 +uid://c10t36hym7jji diff --git a/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd new file mode 100644 index 00000000..091241da --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd @@ -0,0 +1,8 @@ +class_name GdUnitSpyFunctionDoubler +extends GdFunctionDoubler + + +func double(func_descriptor: GdFunctionDescriptor) -> PackedStringArray: + return GdUnitFunctionDoublerBuilder.new(func_descriptor)\ + .with_verify_block()\ + .build() diff --git a/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid new file mode 100644 index 00000000..c34ccacf --- /dev/null +++ b/addons/gdUnit4/src/doubler/GdUnitSpyFunctionDoubler.gd.uid @@ -0,0 +1 @@ +uid://wg0dbt0s66jq diff --git a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid index 2503e518..70267faa 100644 --- a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid +++ b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid @@ -1 +1 @@ -uid://bnykspgpnndrk +uid://bbhmp4orjmsaq diff --git a/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd b/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd new file mode 100644 index 00000000..906ee2c5 --- /dev/null +++ b/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd @@ -0,0 +1,24 @@ +## A fuzzer that generates random boolean values for testing.[br] +## +## This is useful for testing code paths that +## depend on boolean conditions, flags, or toggle states.[br] +## +## [b]Usage example:[/b] +## [codeblock] +## func test_toggle_feature(fuzzer := BoolFuzzer.new(), _fuzzer_iterations = 100): +## var enabled := fuzzer.next_value() +## my_feature.set_enabled(enabled) +## assert_bool(my_feature.is_enabled()),is_equal(enabled) +## [/codeblock] +class_name BoolFuzzer +extends Fuzzer + + +## Generates a random boolean value.[br] +## +## Returns either [code]true[/code] or [code]false[/code] with equal probability. +## This method is called automatically during fuzz testing iterations.[br] +## +## @returns A randomly generated boolean value ([code]true[/code] or [code]false[/code]). +func next_value() -> bool: + return randi() % 2 diff --git a/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd.uid new file mode 100644 index 00000000..15c402b0 --- /dev/null +++ b/addons/gdUnit4/src/fuzzers/BoolFuzzer.gd.uid @@ -0,0 +1 @@ +uid://bv60kwl0hlnd0 diff --git a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd index 347513f3..5da1a777 100644 --- a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd @@ -1,7 +1,24 @@ +## A fuzzer that generates random floating-point values within a specified range.[br] +## +## This is particularly useful for testing numerical calculations, +## physics simulations, shader parameters, or any code that processes floating-point +## values.[br] +## +## [b]Usage example:[/b] +## [codeblock] +## func test_calculate_damage(fuzzer := FloatFuzzer.new(0.0, 100.0), _fuzzer_iterations := 500): +## var damage := fuzzer.next_value() +## var result = calculate_damage_reduction(damage) +## assert_float(result).is_between(0.0, damage) +## [/codeblock] +## [br] +## [b]Note:[/b] The range is inclusive on both ends, and values are uniformly distributed. class_name FloatFuzzer extends Fuzzer +## Minimum value (inclusive) for generated floats. var _from: float = 0 +## Maximum value (inclusive) for generated floats. var _to: float = 0 func _init(from: float, to: float) -> void: @@ -9,5 +26,12 @@ func _init(from: float, to: float) -> void: _from = from _to = to + +## Generates a random float value within the configured range.[br] +## +## Returns a uniformly distributed random float between [member _from] and +## [member _to] (inclusive). Each call produces a new random value.[br] +## +## @returns A random float value within the specified range. func next_value() -> float: return randf_range(_from, _to) diff --git a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid index 9b5ac982..0436d805 100644 --- a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid @@ -1 +1 @@ -uid://d05keunlyb13g +uid://d0mp4ok1coko6 diff --git a/addons/gdUnit4/src/fuzzers/Fuzzer.gd b/addons/gdUnit4/src/fuzzers/Fuzzer.gd index 7cd6a588..e4e597d0 100644 --- a/addons/gdUnit4/src/fuzzers/Fuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/Fuzzer.gd @@ -1,39 +1,80 @@ -# Base interface for fuzz testing -# https://en.wikipedia.org/wiki/Fuzzing +## Base interface for fuzz testing.[br] +## +## Fuzzer is an abstract base class that provides the foundation for creating +## custom fuzzers used in automated testing. Fuzz testing (fuzzing) is a software +## testing technique that involves providing invalid, unexpected, or random data +## as inputs to a program to find bugs and potential security vulnerabilities. +## [br][br] +## To use a fuzzer in your test cases, add optional parameters to your test function: +## [codeblock] +## func test_foo(fuzzer := Fuzzers.randomInt(), _fuzzer_iterations := 10, _fuzzer_seed := 12345): +## var value := fuzzer.next_value() +## # Test logic using the fuzzed value +## [/codeblock] +## [br] +## @tutorial(Fuzzing on Wikipedia): https://en.wikipedia.org/wiki/Fuzzing +@abstract class_name Fuzzer extends RefCounted -# To run a test with a specific fuzzer you have to add defailt argument checked your test case -# all arguments are optional [] -# syntax: -# func test_foo([fuzzer = ], [fuzzer_iterations=], [fuzzer_seed=]) -# example: -# # runs the test 'test_foo' 10 times with a random int value generated by the IntFuzzer -# func test_foo(fuzzer = Fuzzers.randomInt(), fuzzer_iterations=10) -# -# # runs the test 'test_foo2' 1000 times as default with a random seed='101010101' -# func test_foo2(fuzzer = Fuzzers.randomInt(), fuzzer_seed=101010101) - -const ITERATION_DEFAULT_COUNT = 1000 + +## Default number of iterations for fuzz testing when not specified. +const ITERATION_DEFAULT_COUNT := 1000 +## Parameter name for passing the fuzzer instance to test functions. const ARGUMENT_FUZZER_INSTANCE := "fuzzer" +## Parameter name for specifying the number of iterations in test functions. const ARGUMENT_ITERATIONS := "fuzzer_iterations" +## Parameter name for specifying the random seed in test functions. const ARGUMENT_SEED := "fuzzer_seed" -var _iteration_index :int = 0 -var _iteration_limit :int = ITERATION_DEFAULT_COUNT +## Current iteration index during fuzzing execution. +var _iteration_index := 0 +## Maximum number of iterations to run for this fuzzer. +var _iteration_limit := ITERATION_DEFAULT_COUNT -# generates the next fuzz value -# needs to be implement -func next_value() -> Variant: - push_error("Invalid vall. Fuzzer not implemented 'next_value()'") - return null +## Generates the next fuzz value.[br] +## +## This abstract method must be implemented by derived classes to provide +## the specific fuzzing logic for generating test values.[br] +## +## [b]Example implementation:[/b] +## [codeblock] +## func next_value() -> int: +## return randi_range(0, 100) +## [/codeblock] +## +## @returns The next generated fuzz value. The type depends on the specific fuzzer implementation. +@abstract +func next_value() -> Variant -# returns the current iteration index +## Returns the current iteration index.[br] +## +## Useful for tracking progress during fuzzing or for debugging purposes +## when a specific iteration causes a failure.[br] +## +## [b]Example:[/b] +## [codeblock] +## if fuzzer.iteration_index() % 100 == 0: +## print("Processed %d iterations" % fuzzer.iteration_index()) +## [/codeblock] +## +## @returns The current iteration index, starting from 0. func iteration_index() -> int: return _iteration_index -# returns the amount of iterations where the fuzzer will be run +## Returns the maximum number of iterations for this fuzzer.[br] +## +## This value determines how many times the fuzzer will generate values +## during a test run. It can be overridden by the [code]fuzzer_iterations[/code] +## parameter in test functions.[br] +## +## [b]Example:[/b] +## [codeblock] +## print("Running %d fuzzing iterations" % fuzzer.iteration_limit()) +## [/codeblock] +## +## @returns The maximum number of iterations to be executed. func iteration_limit() -> int: return _iteration_limit diff --git a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid index 1d1bc1b1..124e5e98 100644 --- a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid @@ -1 +1 @@ -uid://dn4s1yxhf58rv +uid://snrey38gwrwy diff --git a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd index 064dc20a..4100d514 100644 --- a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd @@ -1,24 +1,69 @@ +## A fuzzer that generates random integer values with optional even/odd constraints.[br] +## +## It supports three modes: normal (any integer), even-only, +## and odd-only generation. This is useful for testing array indices, loop counters, +## enumeration values, or any code that processes integer values.[br] +## +## [b]Usage example:[/b] +## [codeblock] +## # Test with any integer in range +## func test_array_access(fuzzer = IntFuzzer.new(0, 99), fuzzer_iterations = 100): +## var index = fuzzer.next_value() +## var array = create_array(100) +## assert(array[index] != null) +## +## # Test with only even numbers +## func test_even_processing(fuzzer := IntFuzzer.new(0, 100, IntFuzzer.EVEN)): +## var even_num := fuzzer.next_value() +## assert_int(even_num % 2).is_equal(0) +## [/codeblock] class_name IntFuzzer extends Fuzzer + +## Generates any integer within the range. enum { - NORMAL, - EVEN, - ODD + NORMAL, ## Generate any integer within the specified range. + EVEN, ## Generate only even integers within the specified range. + ODD ## Generate only odd integers within the specified range. } -var _from :int = 0 -var _to : int = 0 -var _mode : int = NORMAL + +## Minimum value (inclusive) for generated integers. +var _from: int = 0 +## Maximum value (inclusive) for generated integers. +var _to: int = 0 +## Generation mode: NORMAL, EVEN, or ODD. +var _mode: int = NORMAL -func _init(from: int, to: int, mode :int = NORMAL) -> void: +func _init(from: int, to: int, mode: int = NORMAL) -> void: assert(from <= to, "Invalid range!") _from = from _to = to _mode = mode +## Generates a random integer value based on the configured mode.[br] +## +## Returns a random integer between [member _from] and [member _to] (inclusive).[br] +## The value will be constrained according to the [member _mode]:[br] +## - [constant NORMAL]: Any integer in the range[br] +## - [constant EVEN]: Only even integers[br] +## - [constant ODD]: Only odd integers[br] +## +## [b]Example:[/b] +## [codeblock] +## var normal_fuzzer = IntFuzzer.new(1, 10, IntFuzzer.NORMAL) +## var even_fuzzer = IntFuzzer.new(1, 10, IntFuzzer.EVEN) +## var odd_fuzzer = IntFuzzer.new(1, 10, IntFuzzer.ODD) +## +## print(normal_fuzzer.next_value()) # Could be any: 1, 2, 3, ..., 10 +## print(even_fuzzer.next_value()) # Only even: 2, 4, 6, 8, 10 +## print(odd_fuzzer.next_value()) # Only odd: 1, 3, 5, 7, 9 +## [/codeblock] +## +## @returns A random integer value within the specified range and mode func next_value() -> int: var value := randi_range(_from, _to) match _mode: diff --git a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid index 73bd8501..9cd5296c 100644 --- a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid @@ -1 +1 @@ -uid://bvyh3bnhntg8f +uid://c7ed17xomitvx diff --git a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd index ca165d33..277b4ac6 100644 --- a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd @@ -1,29 +1,86 @@ +## A fuzzer that generates random strings with configurable length and character sets.[br] +## +## It supports custom character sets defined by patterns or ranges, +## making it ideal for testing input validation, text processing, parsers, or any +## code that handles string data.[br] +## +## The fuzzer uses a pattern syntax to define allowed characters:[br] +## - Single characters: [code]abc[/code] allows 'a', 'b', 'c'[br] +## - Ranges: [code]a-z[/code] allows lowercase letters[br] +## - Special patterns: [code]\\w[/code] (word chars), [code]\\p{L}[/code] (letters), [code]\\p{N}[/code] (numbers)[br] +## +## [b]Usage example:[/b] +## [codeblock] +## # Test with alphanumeric strings +## func test_username(fuzzer := StringFuzzer.new(3, 20, "a-zA-Z0-9"), _fuzzer_iterations := 100): +## var username _= fuzzer.next_value() +## assert_bool(validate_username(username)).is_true() +## +## # Test with special characters +## func test_password(fuzzer := StringFuzzer.new(8, 32, "a-zA-Z0-9!@#$%"), _fuzzer_iterations := 100) -> void: +## var password := fuzzer.next_value() +## assert_str(password).has_length(8, Comparator.GREATER_EQUAL).has_length(32, Comparator.LESS_EQUAL) +## [/codeblock] class_name StringFuzzer extends Fuzzer +## Default character set pattern including word characters, letters, numbers, and common symbols.[br] +## Includes: word characters (\\w), Unicode letters (\\p{L}), Unicode numbers (\\p{N}), +## and the characters: +, -, _, ' +const DEFAULT_CHARSET = "\\w\\p{L}\\p{N}+-_'" -const DEFAULT_CHARSET = "a-zA-Z0-9+-_" +## Minimum length for generated strings (inclusive). +var _min_length: int +## Maximum length for generated strings (inclusive). +var _max_length: int +## Array of character codes that can be used in generated strings. +var _charset: PackedInt32Array -var _min_length :int -var _max_length :int -var _charset :PackedByteArray - -func _init(min_length :int, max_length :int, pattern :String = DEFAULT_CHARSET) -> void: - assert(min_length>0 and min_length < max_length) - assert(not null or not pattern.is_empty()) +func _init(min_length: int, max_length: int, pattern: String = DEFAULT_CHARSET) -> void: _min_length = min_length - _max_length = max_length - _charset = StringFuzzer.extract_charset(pattern) + _max_length = max_length + 1 # +1 for inclusive + assert(not null or not pattern.is_empty()) + assert(_min_length > 0 and _min_length < _max_length) + _charset = _extract_charset(pattern) -static func extract_charset(pattern :String) -> PackedByteArray: +## Generates a random string based on configured parameters.[br] +## +## Creates a string with random length between [member _min_length] and +## [member _max_length], using only characters from the configured charset. +## Each character is selected randomly and independently.[br] +## +## [b]Example:[/b] +## [codeblock] +## var fuzzer = StringFuzzer.new(5, 10, "ABC") +## for i in range(5): +## var str = fuzzer.next_value() +## print("Generated: ", str) +## # Possible outputs: "ABCAB", "BCAABCA", "CCCBAA", etc. +## assert(str.length() >= 5 and str.length() <= 10) +## for c in str: +## assert(c in ["A", "B", "C"]) +## [/codeblock] +## +## @returns A random string matching the configured constraints. +func next_value() -> String: + var value := PackedInt32Array() + var max_char := len(_charset) + var length: int = max(_min_length, randi() % _max_length) + for i in length: + @warning_ignore("return_value_discarded") + value.append(_charset[randi() % max_char]) + return value.to_byte_array().get_string_from_utf32() + + +static func _extract_charset(pattern: String) -> PackedInt32Array: var reg := RegEx.new() if reg.compile(pattern) != OK: - push_error("Invalid pattern to generate Strings! Use e.g 'a-zA-Z0-9+-_'") - return PackedByteArray() + push_error("Invalid pattern to generate Strings! Use e.g '\\w\\p{L}\\p{N}+-_'") + return PackedInt32Array() - var charset := Array() + var charset := PackedInt32Array() var char_before := -1 var index := 0 while index < pattern.length(): @@ -37,7 +94,7 @@ static func extract_charset(pattern :String) -> PackedByteArray: # range starts if char_current == 45 and char_before != -1: var char_next := pattern.unicode_at(index) - var characters := build_chars(char_before, char_next) + var characters := _build_chars(char_before, char_next) for character in characters: charset.append(character) char_before = -1 @@ -45,21 +102,11 @@ static func extract_charset(pattern :String) -> PackedByteArray: continue char_before = char_current charset.append(char_current) - return PackedByteArray(charset) + return charset -static func build_chars(from :int, to :int) -> Array[int]: - var characters :Array[int] = [] +static func _build_chars(from: int, to: int) -> PackedInt32Array: + var characters := PackedInt32Array() for character in range(from+1, to+1): characters.append(character) return characters - - -func next_value() -> String: - var value := PackedByteArray() - var max_char := len(_charset) - var length :int = max(_min_length, randi() % _max_length) - for i in length: - @warning_ignore("return_value_discarded") - value.append(_charset[randi() % max_char]) - return value.get_string_from_utf8() diff --git a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid index d45396a5..bb259fc6 100644 --- a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid @@ -1 +1 @@ -uid://cmghw5dtcdrxl +uid://d21tnl0qc6ee0 diff --git a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd index 855cf6a9..70ef31b7 100644 --- a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd @@ -1,9 +1,27 @@ +## A fuzzer that generates random Vector2 values within a specified rectangular range.[br] +## +## This is particularly useful for testing 2D physics, movement +## systems, UI positioning, sprite coordinates, or any code that processes 2D vectors.[br] +## +## The fuzzer generates vectors where each component (x, y) is independently randomized +## within its respective range, creating a uniform distribution over the rectangular area.[br] +## +## [b]Usage example:[/b] +## [codeblock] +## # Test 2D movement within screen bounds +## func test_movement(fuzzer := Vector2Fuzzer.new(Vector2.ZERO, Vector2(1920, 1080)), _fuzzer_iterations := 200) -> void: +## var position := fuzzer.next_value() +## player.set_position(position) +## +## [/codeblock] class_name Vector2Fuzzer extends Fuzzer -var _from :Vector2 -var _to : Vector2 +## Minimum bounds for the generated vectors (inclusive for both x and y). +var _from: Vector2 +## Maximum bounds for the generated vectors (inclusive for both x and y). +var _to: Vector2 func _init(from: Vector2, to: Vector2) -> void: @@ -12,6 +30,15 @@ func _init(from: Vector2, to: Vector2) -> void: _to = to +## Generates a random Vector2 within the configured rectangular range.[br] +## +## Returns a Vector2 where each component is independently randomized:[br] +## - x: random float between [code]_from.x[/code] and [code]_to.x[/code][br] +## - y: random float between [code]_from.y[/code] and [code]_to.y[/code][br] +## +## The distribution is uniform over the rectangular area defined by the bounds.[br] +## +## @returns A random Vector2 within the specified range. func next_value() -> Vector2: var x := randf_range(_from.x, _to.x) var y := randf_range(_from.y, _to.y) diff --git a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid index 36643e8c..925faf9e 100644 --- a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid @@ -1 +1 @@ -uid://c0nafcj411138 +uid://bgirn21lva4ll diff --git a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd index c773ab51..b54870d1 100644 --- a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd +++ b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd @@ -1,9 +1,28 @@ +## A fuzzer that generates random Vector3 values within a specified box range.[br] +## +## This is particularly useful for testing 3D physics, spatial +## positioning, camera systems, particle effects, or any code that processes 3D vectors.[br] +## +## The fuzzer generates vectors where each component (x, y, z) is independently +## randomized within its respective range, creating a uniform distribution over the +## 3D box volume.[br] +## +## [b]Usage example:[/b] +## [codeblock] +## # Test 3D object placement within world bounds +## func test_spawn_position(fuzzer := Vector3Fuzzer.new(Vector3(-100, 0, -100), Vector3(100, 50, 100)), _fuzzer_iterations := 300): +## var position := fuzzer.next_value() +## var object = spawn_object(position) +## +## [/codeblock] class_name Vector3Fuzzer extends Fuzzer -var _from :Vector3 -var _to : Vector3 +## Minimum bounds for the generated vectors (inclusive for x, y, and z). +var _from: Vector3 +## Maximum bounds for the generated vectors (inclusive for x, y, and z). +var _to: Vector3 func _init(from: Vector3, to: Vector3) -> void: @@ -12,6 +31,16 @@ func _init(from: Vector3, to: Vector3) -> void: _to = to +## Generates a random Vector3 within the configured box range.[br] +## +## Returns a Vector3 where each component is independently randomized:[br] +## - x: random float between [code]_from.x[/code] and [code]_to.x[/code][br] +## - y: random float between [code]_from.y[/code] and [code]_to.y[/code][br] +## - z: random float between [code]_from.z[/code] and [code]_to.z[/code][br] +## +## The distribution is uniform over the 3D box volume defined by the bounds.[br] +## +## @returns A random Vector3 within the specified range. func next_value() -> Vector3: var x := randf_range(_from.x, _to.x) var y := randf_range(_from.y, _to.y) diff --git a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid index cb55b26b..0e91e808 100644 --- a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid +++ b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid @@ -1 +1 @@ -uid://bdw37s7i5ddgy +uid://bd70m2gamfxis diff --git a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid index 418aba02..eefa0f22 100644 --- a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://8wtpiv1qvpg6 +uid://mw5gwhm5hwwh diff --git a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid index 54dcb2d0..1392327a 100644 --- a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://bpvbfh6rscg6b +uid://dylm6ityiu63b diff --git a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid index c75956c7..5629033a 100644 --- a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://berqup1jifqvo +uid://7q5ysgmc4x6p diff --git a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid index 12a810d8..0bd32141 100644 --- a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://bxfddo1mxd7yk +uid://cbmagmhek1cm4 diff --git a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid index 4066bdc8..7959474a 100644 --- a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://dptx538y0iv5 +uid://wsnvlu6timiu diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd index aa43b80f..d5adc4bb 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd @@ -4,5 +4,10 @@ extends RefCounted @warning_ignore("unused_parameter") -func is_match(value :Variant) -> bool: +func is_match(value: Variant) -> bool: return true + + +func _to_string() -> String: + assert(false, "`_to_string()` Is not implemented!") + return "" diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid index da99d3a3..89ea7a80 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid @@ -1 +1 @@ -uid://lcfje3tsypup +uid://o3kufisi8oal diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd index a40eacee..6a70cc15 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd @@ -4,9 +4,9 @@ extends RefCounted const TYPE_ANY = TYPE_MAX + 100 -static func to_matcher(arguments :Array[Variant], auto_deep_check_mode := false) -> ChainedArgumentMatcher: - var matchers :Array[Variant] = [] - for arg :Variant in arguments: +static func to_matcher(arguments: Array[Variant], auto_deep_check_mode := false) -> ChainedArgumentMatcher: + var matchers: Array[Variant] = [] + for arg: Variant in arguments: # argument is already a matcher if arg is GdUnitArgumentMatcher: matchers.append(arg) @@ -20,13 +20,23 @@ static func any() -> GdUnitArgumentMatcher: return AnyArgumentMatcher.new() -static func by_type(type :int) -> GdUnitArgumentMatcher: +static func by_type(type: int) -> GdUnitArgumentMatcher: return AnyBuildInTypeArgumentMatcher.new([type]) -static func by_types(types :PackedInt32Array) -> GdUnitArgumentMatcher: +static func by_types(types: PackedInt32Array) -> GdUnitArgumentMatcher: return AnyBuildInTypeArgumentMatcher.new(types) -static func any_class(clazz :Object) -> GdUnitArgumentMatcher: +static func any_class(clazz: Object) -> GdUnitArgumentMatcher: return AnyClazzArgumentMatcher.new(clazz) + + +static func is_variant_string_matching(value: Variant) -> GdUnitResult: + if value is String or value is StringName: + return GdUnitResult.success() + if value is GdUnitArgumentMatcher: + if str(value) == "any()" or str(value) == "any_string()": + return GdUnitResult.success() + return GdUnitResult.error("Only 'any()' and 'any_string()' argument matchers are allowed!") + return GdUnitResult.error("Only String or StringName types are allowed!") diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid index f0d7dc53..6b0cc37c 100644 --- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid +++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid @@ -1 +1 @@ -uid://c8voejjm5titg +uid://b7h4aplfpavlr diff --git a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid index 9c3e5478..c18bbbeb 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid @@ -1 +1 @@ -uid://dgc1qam4o7i0i +uid://bx8nmv2uxpor5 diff --git a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd index beafb52f..537e9239 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd +++ b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd @@ -22,6 +22,8 @@ static func build(clazz :Variant, mock_mode :String, debug_write := false) -> Va return mock_on_scene(packed_scene, debug_write) # mocking a script var instance := create_instance(clazz) + if instance == null: + push_error("Can't create instance of class %s" % clazz) var mock := mock_on_script(instance, clazz, [ "get_script"], debug_write) if not instance is RefCounted: instance.free() @@ -93,14 +95,20 @@ static func get_class_info(clazz :Variant) -> Dictionary: static func mock_on_script(instance :Object, clazz :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript: - var push_errors := is_push_errors() - var function_doubler := GdUnitMockFunctionDoubler.new(push_errors) + var function_doubler := GdUnitMockFunctionDoubler.new() var class_info := get_class_info(clazz) - var lines := load_template(MOCK_TEMPLATE.source_code, class_info, instance) - var clazz_name :String = class_info.get("class_name") var clazz_path :PackedStringArray = class_info.get("class_path", [clazz_name]) + var mock_template := MOCK_TEMPLATE.source_code.format({ + "instance_id" : abs(instance.get_instance_id()), + "gdunit_source_class": clazz_name if clazz_path.is_empty() else clazz_path[0] + }) + var lines := load_template(mock_template, class_info) lines += double_functions(instance, clazz_name, clazz_path, function_doubler, function_excludes) + # We disable warning/errors for inferred_declaration + if Engine.get_version_info().hex >= 0x40400: + lines.insert(0, '@warning_ignore_start("inferred_declaration")') + lines.append('@warning_ignore_restore("inferred_declaration")') var mock := GDScript.new() mock.source_code = "\n".join(lines) diff --git a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid index f40ca653..75a0471b 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid @@ -1 +1 @@ -uid://dq56630tdb00j +uid://vocgx4reahs6 diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd deleted file mode 100644 index 4bbecc30..00000000 --- a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd +++ /dev/null @@ -1,113 +0,0 @@ -class_name GdUnitMockFunctionDoubler -extends GdFunctionDoubler - - -const TEMPLATE_FUNC_WITH_RETURN_VALUE = """ - var args__: Array = ["$(func_name)", $(arguments)] - - if __is_prepare_return_value(): - __save_function_return_value(args__) - return ${default_return_value}$(return_as) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)", args__): - @warning_ignore("unsafe_call_argument") - return $(await)super($(arguments))$(return_as) - - return __get_mocked_return_value_or_default(args__, ${default_return_value}) - -""" - - -const TEMPLATE_FUNC_WITH_RETURN_VOID = """ - var args__: Array = ["$(func_name)", $(arguments)] - - if __is_prepare_return_value(): - if $(push_errors): - push_error(\"Mocking a void function '$(func_name)() -> void:' is not allowed.\") - return - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - $(await)super($(arguments)) - -""" - - -const TEMPLATE_FUNC_VARARG_RETURN_VALUE = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - if __is_prepare_return_value(): - if $(push_errors): - push_error(\"Mocking a void function '$(func_name)() -> void:' is not allowed.\") - __save_function_return_value(args__) - return ${default_return_value}$(return_as) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)", args__): - match varargs__.size(): - @warning_ignore("unsafe_call_argument") - 0: return $(await)super($(arguments)) - @warning_ignore("unsafe_call_argument") - 1: return $(await)super($(arguments), varargs__[0]) - @warning_ignore("unsafe_call_argument") - 2: return $(await)super($(arguments), varargs__[0], varargs__[1]) - @warning_ignore("unsafe_call_argument") - 3: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2]) - @warning_ignore("unsafe_call_argument") - 4: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3]) - @warning_ignore("unsafe_call_argument") - 5: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4]) - @warning_ignore("unsafe_call_argument") - 6: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5]) - @warning_ignore("unsafe_call_argument") - 7: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6]) - @warning_ignore("unsafe_call_argument") - 8: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7]) - @warning_ignore("unsafe_call_argument") - 9: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8]) - @warning_ignore("unsafe_call_argument") - 10: return $(await)super($(arguments), varargs__[0], varargs__[1], varargs__[2], varargs__[3], varargs__[4], varargs__[5], varargs__[6], varargs__[7], varargs__[8], varargs__[9]) - - return __get_mocked_return_value_or_default(args__, ${default_return_value}) - -""" - - -func _init(push_errors :bool = false) -> void: - super._init(push_errors) - - -func get_template(fd: GdFunctionDescriptor, _is_callable: bool) -> String: - if fd.is_vararg(): - return TEMPLATE_FUNC_VARARG_RETURN_VALUE - var return_type :Variant = fd.return_type() - if return_type is StringName: - return TEMPLATE_FUNC_WITH_RETURN_VALUE - return TEMPLATE_FUNC_WITH_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_FUNC_WITH_RETURN_VALUE diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid deleted file mode 100644 index 7e82b1c1..00000000 --- a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://jm8onp8m4k25 diff --git a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd index 8c63af04..774ca3e3 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd +++ b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd @@ -3,69 +3,56 @@ class_name DoubledMockClassSourceClassName ################################################################################ # internal mocking stuff ################################################################################ -const __INSTANCE_ID = "${instance_id}" -const __SOURCE_CLASS = "${source_class}" +const __INSTANCE_ID := "gdunit_doubler_instance_id_{instance_id}" -class MockingState: + +class GdUnitMockDoublerState: + const __SOURCE_CLASS := "{gdunit_source_class}" + + var excluded_methods := PackedStringArray() var working_mode := GdUnitMock.RETURN_DEFAULTS - var excluded_methods: PackedStringArray = [] - var return_value: Variant = null var is_prepare_return := false + var return_values := Dictionary() + var return_value: Variant = null - #{ = { - # = - # } - #} - var return_values := Dictionary(): - get: - return return_values + func _init(working_mode_ := GdUnitMock.RETURN_DEFAULTS) -> void: + working_mode = working_mode_ + +var __mock_state := GdUnitMockDoublerState.new() @warning_ignore("unused_private_class_variable") var __verifier_instance := GdUnitObjectInteractionsVerifier.new() -var __mocking_state := MockingState.new() func __init(__script: GDScript, mock_working_mode: String) -> void: - # store self need to access static functions - Engine.set_meta(__INSTANCE_ID, self) super.set_script(__script) - __mocking_state.working_mode = mock_working_mode + __init_doubler() + __mock_state.working_mode = mock_working_mode -func __release_double() -> void: - # we need to release the self reference manually to prevent orphan nodes - Engine.remove_meta(__INSTANCE_ID) +static func __doubler_state() -> GdUnitMockDoublerState: + if Engine.has_meta(__INSTANCE_ID): + return Engine.get_meta(__INSTANCE_ID).__mock_state + return null -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - if Engine.has_meta(__INSTANCE_ID): - Engine.remove_meta(__INSTANCE_ID) +func __init_doubler() -> void: + Engine.set_meta(__INSTANCE_ID, self) -static func __mock_state() -> MockingState: - @warning_ignore("unsafe_property_access") - return __get_instance().__mocking_state +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE and Engine.has_meta(__INSTANCE_ID): + Engine.remove_meta(__INSTANCE_ID) static func __get_verifier() -> GdUnitObjectInteractionsVerifier: - var __instance := __get_instance() - @warning_ignore("unsafe_property_access") - return null if __instance == null else __instance.__verifier_instance - - -static func __get_instance() -> Object: - return null if not Engine.has_meta(__INSTANCE_ID) else Engine.get_meta(__INSTANCE_ID) - - -func __instance_id() -> String: - return __INSTANCE_ID + return Engine.get_meta(__INSTANCE_ID).__verifier_instance static func __is_prepare_return_value() -> bool: - return __mock_state().is_prepare_return + return __doubler_state().is_prepare_return static func __sort_by_argument_matcher(__left_args: Array, __right_args: Array) -> bool: @@ -78,7 +65,6 @@ static func __sort_by_argument_matcher(__left_args: Array, __right_args: Array) # we need to sort by matcher arguments so that they are all at the end of the list static func __sort_dictionary(__unsorted_args: Dictionary) -> Dictionary: - var __instance := __get_instance() # only need to sort if contains more than one entry if __unsorted_args.size() <= 1: return __unsorted_args @@ -91,16 +77,14 @@ static func __sort_dictionary(__unsorted_args: Dictionary) -> Dictionary: return __sorted_result -static func __save_function_return_value(__fuction_args: Array) -> void: - var __mock := __mock_state() - var __func_name: String = __fuction_args[0] - var __func_args: Array = __fuction_args.slice(1) - var mocked_return_value_by_args: Dictionary = __mock.return_values.get(__func_name, {}) +static func __save_function_return_value(__func_name: String, __func_args: Array) -> void: + var doubler_state := __doubler_state() + var mocked_return_value_by_args: Dictionary = doubler_state.return_values.get(__func_name, {}) - mocked_return_value_by_args[__func_args] = __mock.return_value - __mock.return_values[__func_name] = __sort_dictionary(mocked_return_value_by_args) - __mock.return_value = null - __mock.is_prepare_return = false + mocked_return_value_by_args[__func_args] = doubler_state.return_value + doubler_state.return_values[__func_name] = __sort_dictionary(mocked_return_value_by_args) + doubler_state.return_value = null + doubler_state.is_prepare_return = false static func __is_mocked_args_match(__func_args: Array, __mocked_args: Array) -> bool: @@ -125,38 +109,35 @@ static func __is_mocked_args_match(__func_args: Array, __mocked_args: Array) -> return __is_matching -static func __get_mocked_return_value_or_default(__fuction_args: Array, __default_return_value: Variant) -> Variant: - var __mock := __mock_state() - var __func_name: String = __fuction_args[0] - if not __mock.return_values.has(__func_name): +static func __return_mock_value(__func_name: String, __func_args: Array, __default_return_value: Variant) -> Variant: + var doubler_state := __doubler_state() + if not doubler_state.return_values.has(__func_name): return __default_return_value - var __func_args: Array = __fuction_args.slice(1) @warning_ignore("unsafe_method_access") - var __mocked_args: Array = __mock.return_values.get(__func_name).keys() + var __mocked_args: Array = doubler_state.return_values.get(__func_name).keys() for __index in __mocked_args.size(): var __margs: Variant = __mocked_args[__index] if __is_mocked_args_match(__func_args, [__margs]): - return __mock.return_values[__func_name][__margs] + return doubler_state.return_values[__func_name][__margs] return __default_return_value -static func __do_call_real_func(__func_name: String, __func_args := []) -> bool: - var __mock := __mock_state() - var __is_call_real_func: bool = __mock.working_mode == GdUnitMock.CALL_REAL_FUNC and not __mock.excluded_methods.has(__func_name) +static func __is_do_not_call_real_func(__func_name: String, __func_args := []) -> bool: + var doubler_state := __doubler_state() + var __is_call_real_func: bool = doubler_state.working_mode == GdUnitMock.CALL_REAL_FUNC and not doubler_state.excluded_methods.has(__func_name) # do not call real funcions for mocked functions - if __is_call_real_func and __mock.return_values.has(__func_name): - var __fuction_args: Array = __func_args.slice(1) + if __is_call_real_func and doubler_state.return_values.has(__func_name): @warning_ignore("unsafe_method_access") - var __mocked_args: Array = __mock.return_values.get(__func_name).keys() - return not __is_mocked_args_match(__fuction_args, __mocked_args) - return __is_call_real_func + var __mocked_args: Array = doubler_state.return_values.get(__func_name).keys() + return __is_mocked_args_match(__func_args, __mocked_args) + return !__is_call_real_func func __exclude_method_call(exluded_methods: PackedStringArray) -> void: - __mocking_state.excluded_methods.append_array(exluded_methods) + __doubler_state().excluded_methods.append_array(exluded_methods) func __do_return(mock_do_return_value: Variant) -> Object: - __mocking_state.return_value = mock_do_return_value - __mocking_state.is_prepare_return = true + __doubler_state().return_value = mock_do_return_value + __doubler_state().is_prepare_return = true return self diff --git a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid index 75e24f16..5c7780b2 100644 --- a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid +++ b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid @@ -1 +1 @@ -uid://derlwqu7gm3qj +uid://b1dbppoq1hry4 diff --git a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd index 5ee14878..ebbc93f9 100644 --- a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd +++ b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd @@ -11,15 +11,6 @@ enum TYPE { const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") -const PATTERN_SCRIPT_ERROR := "USER SCRIPT ERROR:" -const PATTERN_PUSH_ERROR := "USER ERROR:" -const PATTERN_PUSH_WARNING := "USER WARNING:" -# With Godot 4.4 the pattern has changed -const PATTERN_4x4_SCRIPT_ERROR := "SCRIPT ERROR:" -const PATTERN_4x4_PUSH_ERROR := "ERROR:" -const PATTERN_4x4_PUSH_WARNING := "WARNING:" - -static var _regex_parse_error_line_number: RegEx var _type: TYPE var _line: int @@ -34,39 +25,17 @@ func _init(type: TYPE, line: int, message: String, details: String) -> void: _details = details -static func is_godot4x4() -> bool: - return Engine.get_version_info().hex >= 0x40400 - - -static func extract_push_warning(records: PackedStringArray, index: int) -> ErrorLogEntry: - var pattern := PATTERN_4x4_PUSH_WARNING if is_godot4x4() else PATTERN_PUSH_WARNING - return _extract(records, index, TYPE.PUSH_WARNING, pattern) - - -static func extract_push_error(records: PackedStringArray, index: int) -> ErrorLogEntry: - var pattern := PATTERN_4x4_PUSH_ERROR if is_godot4x4() else PATTERN_PUSH_ERROR - return _extract(records, index, TYPE.PUSH_ERROR, pattern) +func _to_string() -> String: + return _message -static func extract_error(records: PackedStringArray, index: int) -> ErrorLogEntry: - var pattern := PATTERN_4x4_SCRIPT_ERROR if is_godot4x4() else PATTERN_SCRIPT_ERROR - return _extract(records, index, TYPE.SCRIPT_ERROR, pattern) +static func of_push_warning(line: int, message: String, stack_trace: PackedStringArray) -> ErrorLogEntry: + return ErrorLogEntry.new(TYPE.PUSH_WARNING, line, message, "\n".join(stack_trace)) -static func _extract(records: PackedStringArray, index: int, type: TYPE, pattern: String) -> ErrorLogEntry: - var message := records[index] - if message.begins_with(pattern): - var error := message.replace(pattern, "").strip_edges() - var details := records[index+1].strip_edges() - var line := _parse_error_line_number(details) - return ErrorLogEntry.new(type, line, error, details) - return null +static func of_push_error(line: int, message: String, stack_trace: PackedStringArray) -> ErrorLogEntry: + return ErrorLogEntry.new(TYPE.PUSH_ERROR, line, message, "\n".join(stack_trace)) -static func _parse_error_line_number(record: String) -> int: - if _regex_parse_error_line_number == null: - _regex_parse_error_line_number = GdUnitTools.to_regex("at: .*res://.*:(\\d+)") - var matches := _regex_parse_error_line_number.search(record) - if matches != null: - return matches.get_string(1).to_int() - return -1 +static func of_script_error(line: int, message: String, stack_trace: PackedStringArray) -> ErrorLogEntry: + return ErrorLogEntry.new(TYPE.SCRIPT_ERROR, line, message, "\n".join(stack_trace)) diff --git a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid index 1a235d0d..9965b2d7 100644 --- a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid +++ b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid @@ -1 +1 @@ -uid://dkiel0qlk1gk2 +uid://1m1ek171i5f8 diff --git a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid index a5a4a1fa..320494d1 100644 --- a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid @@ -1 +1 @@ -uid://cgjdtqp2vmlvn +uid://87ybms1hr0pu diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd b/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd new file mode 100644 index 00000000..c195760e --- /dev/null +++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd @@ -0,0 +1,70 @@ +class_name GdUnitOrphanNodeInfo +extends RefCounted + +enum GdUnitOrphanType { + member, + variable, + unknown +} + + +var _id: int +var _orphan_type: GdUnitOrphanType +var _type: String +var _name: String +var _script_ref: String +var _func_ref: String +var _next: GdUnitOrphanNodeInfo + +const text_color := Color.ANTIQUE_WHITE +const function_color := Color.SKY_BLUE +const member_variable_color := Color.SALMON +const engine_type_color := Color.LIGHT_GREEN +const script_path_color := Color.CORNFLOWER_BLUE + + +func _init(orphan_type: GdUnitOrphanType, id: int, type: String, name: String, script_ref: String, func_ref: String = "") -> void: + _orphan_type = orphan_type + _id = id + _type = type + _name = name + _script_ref = script_ref + _func_ref = func_ref + + +func as_trace(info: GdUnitOrphanNodeInfo, show_orphan_id := true) -> String: + var trace := "" + if show_orphan_id: + trace += "• <%s> Id:%s\n" % [ + _colored(info._type, engine_type_color), + _colored(info._id, engine_type_color)] + match info._orphan_type: + GdUnitOrphanType.member: + return trace + " at %s script: %s" % [ + _colored(info._name, member_variable_color), + _colored(info._script_ref, script_path_color) + ] + sub_info(info._next) + GdUnitOrphanType.variable: + return trace + " at %s script: %s.%s()" % [ + _colored(info._name, member_variable_color), + _colored(info._script_ref, script_path_color), + _colored(info._func_ref, function_color), + ] + GdUnitOrphanType.unknown: + return trace + " %s" % [ + _colored(info._name, member_variable_color) + ] + + _: + return trace + " No details available" + + +func sub_info(next: GdUnitOrphanNodeInfo) -> String: + if next == null: + return "" + + return "\n" + as_trace(next, false) + + +static func _colored(value: Variant, color: Color) -> String: + return "[color=%s]%s[/color]" % [color.to_html(), value] diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd.uid b/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd.uid new file mode 100644 index 00000000..8e3d6a73 --- /dev/null +++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodeInfo.gd.uid @@ -0,0 +1 @@ +uid://b4o3o7cepdc8f diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd index 725dd1fb..05f11cd5 100644 --- a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd +++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd @@ -1,27 +1,221 @@ class_name GdUnitOrphanNodesMonitor extends GdUnitMonitor -var _initial_count := 0 -var _orphan_count := 0 +const excluded_frame_files: PackedStringArray = [ + "GdUnitOrphanNodesMonitor", + "GdUnitExecutionContext", + "_TestCase", + "IGdUnitExecutionStage", + "GdUnitTestCaseSingleTestStage", + "GdUnitTestCaseSingleExecutionStage", + "GdUnitTestCaseExecutionStage", + "GdUnitTestSuiteExecutionStage", + "GdUnitTestSuiteExecutor" +] + +var _child_monitors: Array[GdUnitOrphanNodesMonitor] = [] var _orphan_detection_enabled :bool +var _initial_orphans: Array[int] = [] +var _orphan_ids_at_start: Array[int] = [] +var _orphan_ids_at_stop: Array[int] = [] +var _collected_orphan_infos: Array[GdUnitOrphanNodeInfo] = [] -func _init(name :String = "") -> void: +func _init(name: String) -> void: super("OrphanNodesMonitor:" + name) _orphan_detection_enabled = GdUnitSettings.is_verbose_orphans() + _initial_orphans = _get_orphan_node_ids() + + +func add_child_monitor(monitor: GdUnitOrphanNodesMonitor) -> void: + if not _orphan_detection_enabled: + return + _child_monitors.append(monitor) func start() -> void: - _initial_count = _orphans() + if not _orphan_detection_enabled: + return + _collected_orphan_infos.clear() + # Collect current orphan id's to be filtered out at `stop` + _orphan_ids_at_start = _get_orphan_node_ids() func stop() -> void: - _orphan_count = max(0, _orphans() - _initial_count) + if not _orphan_detection_enabled: + return + # Collect only new detected orphan id's, we want only to collect orphans between start and stop time + _orphan_ids_at_stop = _get_orphan_node_ids().filter(func(element: int) -> bool: + # Excluding sub monitores orphans + if _collect_child_orphan_ids().has(element): + return false + # Excluding orphans at start + return not _orphan_ids_at_start.has(element) and not _initial_orphans.has(element) + ) + + +func _collect_child_orphan_ids() -> Array[int]: + var collected_ids: Array[int] = [] + for child_monitor in _child_monitors: + collected_ids.append_array(child_monitor._orphan_ids_at_stop) + collected_ids.append_array(child_monitor._collect_child_orphan_ids()) + return collected_ids + + +func detected_orphans() -> Array[GdUnitOrphanNodeInfo]: + if not _orphan_detection_enabled: + return [] + return _collected_orphan_infos.filter(func(info: GdUnitOrphanNodeInfo) -> bool: + return info._id in _orphan_ids_at_stop + ) + + +func orphans_count() -> int: + if not _orphan_detection_enabled: + return 0 + return _orphan_ids_at_stop.size() + + +func collect() -> void: + if not _orphan_detection_enabled: + return + for orphan_id in _get_orphan_node_ids(): + var orphan_to_find := instance_from_id(orphan_id) + _collect_orphan_info(orphan_to_find) + + +func _collect_orphan_info(orphan_to_find: Object) -> void: + if orphan_to_find == null: + return + + var orphan_node := _find_orphan_on_backtraces(orphan_to_find) + if orphan_node: + _collected_orphan_infos.append(orphan_node) + return + + if Engine.has_meta("GdUnitSceneRunner"): + var current_scene_runner:GdUnitSceneRunner = Engine.get_meta("GdUnitSceneRunner") + if is_instance_valid(current_scene_runner): + orphan_node = _find_orphan_at_node(orphan_to_find, current_scene_runner.scene()) + if orphan_node: + _collected_orphan_infos.append(orphan_node) + return + + # not able to find the orphan node via backtrace loaded nodeds + var message := "No details found. Verify called functions manually." + if not EngineDebugger.is_active(): + message = "No details available. [color=yellow]Run tests in debug mode to collect details.[/color]" + + _collected_orphan_infos.append(GdUnitOrphanNodeInfo.new( + GdUnitOrphanNodeInfo.GdUnitOrphanType.unknown, + orphan_to_find.get_instance_id(), + orphan_to_find.get_class(), + message, + "")) + + +func _find_orphan_at_node(orphan_to_find: Object, node: Node) -> GdUnitOrphanNodeInfo: + var script: Script = node.get_script() + if script is not GDScript: + return null + + # First search over all properties + for property in script.get_script_property_list(): + var property_name: String = property["name"] + var property_type: int = property["type"] + # Is untyped or type object + if property_type in [TYPE_NIL, TYPE_OBJECT]: + var property_instance: Variant = node.get(property_name) + @warning_ignore("unsafe_cast") + var property_as_node := property_instance as Node if property_instance != null else null + if property_as_node == null: + continue + if property_as_node == orphan_to_find: + return GdUnitOrphanNodeInfo.new( + GdUnitOrphanNodeInfo.GdUnitOrphanType.member, + orphan_to_find.get_instance_id(), + orphan_to_find.get_class(), + property_name, + script.resource_path) + + # Search on node childs + var orphan_node_info := _find_orphan_at_node(orphan_to_find, property_as_node) + if orphan_node_info: + orphan_node_info._next = GdUnitOrphanNodeInfo.new( + GdUnitOrphanNodeInfo.GdUnitOrphanType.member, + orphan_to_find.get_instance_id(), + orphan_to_find.get_class(), + property_name, + script.resource_path) + return orphan_node_info + + # Second over all children + for child_node in node.get_children(): + var orphan_node_info := _find_orphan_at_node(orphan_to_find, child_node) + if orphan_node_info: + return orphan_node_info + return null + + +func _is_frame_file_excluded(frame_file: String) -> bool: + for file in excluded_frame_files: + if frame_file.contains(file): + return true + return false + + +func _find_orphan_on_backtraces(orphan_to_find: Object) -> GdUnitOrphanNodeInfo: + for script_backtrace in Engine.capture_script_backtraces(true): + for frame in script_backtrace.get_frame_count(): + var frame_file := script_backtrace.get_frame_file(frame) + if _is_frame_file_excluded(frame_file): + continue + # Scan function variables + for l_index in script_backtrace.get_local_variable_count(frame): + var variable_instance: Variant = script_backtrace.get_local_variable_value(frame, l_index) + var variable_name := script_backtrace.get_local_variable_name(frame, l_index) + if typeof(variable_instance) in [TYPE_NIL, TYPE_OBJECT]: + @warning_ignore("unsafe_cast") + var node := variable_instance as Node + if node == null: + continue + if variable_instance == orphan_to_find: + return GdUnitOrphanNodeInfo.new( + GdUnitOrphanNodeInfo.GdUnitOrphanType.variable, + orphan_to_find.get_instance_id(), + orphan_to_find.get_class(), + variable_name, + script_backtrace.get_frame_file(frame), + script_backtrace.get_frame_function(frame)) + else: + var orphan_node_info := _find_orphan_at_node(orphan_to_find, node) + if orphan_node_info: + return orphan_node_info -func _orphans() -> int: - return Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) as int + # Scan class members + for m_index in script_backtrace.get_member_variable_count(frame): + var member_instance: Variant = script_backtrace.get_member_variable_value(frame, m_index) + var member_name := script_backtrace.get_member_variable_name(frame, m_index) + if typeof(member_instance) in [TYPE_NIL, TYPE_OBJECT]: + @warning_ignore("unsafe_cast") + var node := member_instance as Node + if node == null: + continue + if member_instance == orphan_to_find: + return GdUnitOrphanNodeInfo.new( + GdUnitOrphanNodeInfo.GdUnitOrphanType.member, + orphan_to_find.get_instance_id(), + orphan_to_find.get_class(), + member_name, + script_backtrace.get_frame_file(frame)) + else: + var orphan_node_info := _find_orphan_at_node(orphan_to_find, node) + if orphan_node_info: + return orphan_node_info + return null -func orphan_nodes() -> int: - return _orphan_count if _orphan_detection_enabled else 0 +static func _get_orphan_node_ids() -> Array[int]: + @warning_ignore("unsafe_property_access", "unsafe_method_access") + return Engine.get_main_loop().root.get_orphan_node_ids() diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid index 18e6fab6..3a337384 100644 --- a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid @@ -1 +1 @@ -uid://c05r34gpp50to +uid://bm5e3g6a0h1jk diff --git a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd index 2ea2234e..b1b28d7f 100644 --- a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd +++ b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd @@ -1,86 +1,122 @@ class_name GodotGdErrorMonitor extends GdUnitMonitor -var _godot_log_file: String -var _eof: int -var _report_enabled := false -var _entries: Array[ErrorLogEntry] = [] + +var _logger: GdUnitLogger + + +class GdUnitLogger extends Logger: + var _entries: Array[ErrorLogEntry] = [] + var _line_number: int + var _is_report_push_errors: bool + var _is_report_script_errors: bool + + + func _init(is_report_push_errors: bool, is_report_script_errors: bool) -> void: + _is_report_push_errors = is_report_push_errors + _is_report_script_errors = is_report_script_errors + OS.add_logger(self) + + + func entries() -> Array[ErrorLogEntry]: + return _entries + + func erase_log_entry(log_entry: ErrorLogEntry) -> void: + for entry in _entries: + if entry._type == log_entry._type and entry._message == log_entry._message: + _entries.erase(entry) + return + + + func _log_error( + _function: String, + _file: String, + _line: int, + message: String, + _rationale: String, + _editor_notify: bool, + error_type: int, + script_backtraces: Array[ScriptBacktrace] + ) -> void: + match error_type: + ErrorType.ERROR_TYPE_WARNING: + if _is_report_push_errors: + var stack_trace := _build_stack_trace(script_backtraces) + _entries.append(ErrorLogEntry.of_push_warning(_line_number, message, stack_trace)) + + ErrorType.ERROR_TYPE_ERROR: + if _is_report_push_errors: + var stack_trace := _build_stack_trace(script_backtraces) + _entries.append(ErrorLogEntry.of_push_error(_line_number, message, stack_trace)) + + ErrorType.ERROR_TYPE_SCRIPT: + if _is_report_script_errors: + var stack_trace := _build_stack_trace(script_backtraces) + _entries.append(ErrorLogEntry.of_script_error(_line_number, message, stack_trace)) + + ErrorType.ERROR_TYPE_SHADER: + pass + _: + prints("Unknwon log type", message) + + func _log_message(_message: String, _error: bool) -> void: + pass + + func _build_stack_trace(script_backtraces: Array[ScriptBacktrace]) -> PackedStringArray: + for sb in script_backtraces: + for frame in sb.get_frame_count(): + # Find start of test stack + if sb.get_frame_file(frame) == "res://addons/gdUnit4/src/core/_TestCase.gd": + var stack_trace := PackedStringArray() + for test_case_frame in range(0, frame): + _line_number = sb.get_frame_line(test_case_frame) + stack_trace.append(" at %s:%s" % [sb.get_frame_file(test_case_frame), sb.get_frame_line(test_case_frame)]) + return stack_trace + # if no stack trace collected, we in an await function call + var sb := script_backtraces[0] + return [" at %s:%s" % [sb.get_frame_file(0), sb.get_frame_line(0)]] func _init() -> void: - super("GodotGdErrorMonitor") - _godot_log_file = GdUnitSettings.get_log_path() - _report_enabled = _is_reporting_enabled() + super("GdUnitLoggerMonitor") + _logger = GdUnitLogger.new(GdUnitSettings.is_report_push_errors(), GdUnitSettings.is_report_script_errors()) func start() -> void: - var file := FileAccess.open(_godot_log_file, FileAccess.READ) - if file: - file.seek_end(0) - _eof = file.get_length() + clear_logs() func stop() -> void: pass +func log_entries() -> Array[ErrorLogEntry]: + return _logger.entries() + + +func erase_log_entry(log_entry: ErrorLogEntry) -> void: + _logger.erase_log_entry(log_entry) + + func to_reports() -> Array[GdUnitReport]: var reports_: Array[GdUnitReport] = [] - if _report_enabled: - reports_.assign(_entries.map(_to_report)) - _entries.clear() + + reports_.assign(log_entries().map(_to_report)) + return reports_ static func _to_report(errorLog: ErrorLogEntry) -> GdUnitReport: - var failure := "%s\n\t%s\n%s %s" % [ + var failure := """ + %s + %s %s + %s""".dedent().trim_prefix("\n") % [ GdAssertMessages._error("Godot Runtime Error !"), - GdAssertMessages._colored_value(errorLog._details), GdAssertMessages._error("Error:"), - GdAssertMessages._colored_value(errorLog._message)] + GdAssertMessages._colored_value(errorLog._message), + GdAssertMessages._colored(errorLog._details, GdAssertMessages.VALUE_COLOR)] return GdUnitReport.new().create(GdUnitReport.ABORT, errorLog._line, failure) -func scan(force_collect_reports := false) -> Array[ErrorLogEntry]: - await (Engine.get_main_loop() as SceneTree).process_frame - await (Engine.get_main_loop() as SceneTree).physics_frame - _entries.append_array(_collect_log_entries(force_collect_reports)) - return _entries - - -func erase_log_entry(entry: ErrorLogEntry) -> void: - _entries.erase(entry) - - -func _collect_log_entries(force_collect_reports: bool) -> Array[ErrorLogEntry]: - var file := FileAccess.open(_godot_log_file, FileAccess.READ) - file.seek(_eof) - var records := PackedStringArray() - while not file.eof_reached(): - @warning_ignore("return_value_discarded") - records.append(file.get_line()) - file.seek_end(0) - _eof = file.get_length() - var log_entries: Array[ErrorLogEntry]= [] - var is_report_errors := force_collect_reports or _is_report_push_errors() - var is_report_script_errors := force_collect_reports or _is_report_script_errors() - for index in records.size(): - if force_collect_reports: - log_entries.append(ErrorLogEntry.extract_push_warning(records, index)) - if is_report_errors: - log_entries.append(ErrorLogEntry.extract_push_error(records, index)) - if is_report_script_errors: - log_entries.append(ErrorLogEntry.extract_error(records, index)) - return log_entries.filter(func(value: ErrorLogEntry) -> bool: return value != null ) - - -func _is_reporting_enabled() -> bool: - return _is_report_script_errors() or _is_report_push_errors() - - -func _is_report_push_errors() -> bool: - return GdUnitSettings.is_report_push_errors() - - -func _is_report_script_errors() -> bool: - return GdUnitSettings.is_report_script_errors() +func clear_logs() -> void: + log_entries().clear() diff --git a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid index 99621185..7e4dd896 100644 --- a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid +++ b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid @@ -1 +1 @@ -uid://1i2srtcp8q5 +uid://cfuqqvb5ajm0g diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd b/addons/gdUnit4/src/network/GdUnitServer.gd index 6d878a01..4ce2f488 100644 --- a/addons/gdUnit4/src/network/GdUnitServer.gd +++ b/addons/gdUnit4/src/network/GdUnitServer.gd @@ -1,7 +1,9 @@ @tool extends Node -@onready var _server :GdUnitTcpServer = $TcpServer +var _client_id: int + +@onready var _server: GdUnitTcpServer = $TcpServer @warning_ignore("return_value_discarded") @@ -15,10 +17,10 @@ func _ready() -> void: _server.client_connected.connect(_on_client_connected) _server.client_disconnected.connect(_on_client_disconnected) _server.rpc_data.connect(_receive_rpc_data) - GdUnitCommandHandler.instance().gdunit_runner_stop.connect(_on_gdunit_runner_stop) func _on_client_connected(client_id: int) -> void: + _client_id = client_id GdUnitSignals.instance().gdunit_client_connected.emit(client_id) @@ -26,11 +28,6 @@ func _on_client_disconnected(client_id: int) -> void: GdUnitSignals.instance().gdunit_client_disconnected.emit(client_id) -func _on_gdunit_runner_stop(client_id: int) -> void: - if _server: - _server.disconnect_client(client_id) - - func _receive_rpc_data(p_rpc: RPC) -> void: if p_rpc is RPCMessage: var rpc_message: RPCMessage = p_rpc @@ -38,5 +35,7 @@ func _receive_rpc_data(p_rpc: RPC) -> void: return if p_rpc is RPCGdUnitEvent: var rpc_event: RPCGdUnitEvent = p_rpc - GdUnitSignals.instance().gdunit_event.emit(rpc_event.event()) - return + var event := rpc_event.event() + GdUnitSignals.instance().gdunit_event.emit(event) + if event.type() == GdUnitEvent.SESSION_CLOSE and _server != null: + _server.disconnect_client(_client_id) diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd.uid b/addons/gdUnit4/src/network/GdUnitServer.gd.uid index 49b1cb8e..fd955707 100644 --- a/addons/gdUnit4/src/network/GdUnitServer.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitServer.gd.uid @@ -1 +1 @@ -uid://d2b4atsva3n6r +uid://d31u3cwcuw7n4 diff --git a/addons/gdUnit4/src/network/GdUnitServer.tscn b/addons/gdUnit4/src/network/GdUnitServer.tscn index 99620f1e..4dbe8c49 100644 --- a/addons/gdUnit4/src/network/GdUnitServer.tscn +++ b/addons/gdUnit4/src/network/GdUnitServer.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://cn5mp3tmi2gb1"] -[ext_resource type="Script" uid="uid://cw5a5npml5wef" path="res://addons/gdUnit4/src/network/GdUnitServer.gd" id="1"] -[ext_resource type="Script" uid="uid://c7ncb187ucolp" path="res://addons/gdUnit4/src/network/GdUnitTcpServer.gd" id="2"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitServer.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/network/GdUnitTcpServer.gd" id="2"] [node name="Control" type="Node"] script = ExtResource("1") diff --git a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid index a358c1eb..befa13c9 100644 --- a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid @@ -1 +1 @@ -uid://jjy8r2hwxssd +uid://bbsx3wprbae30 diff --git a/addons/gdUnit4/src/network/GdUnitTask.gd.uid b/addons/gdUnit4/src/network/GdUnitTask.gd.uid index b9e21fa8..b9cc4d6c 100644 --- a/addons/gdUnit4/src/network/GdUnitTask.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTask.gd.uid @@ -1 +1 @@ -uid://xe5jb81h1hfm +uid://cow8joglia6u diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd b/addons/gdUnit4/src/network/GdUnitTcpClient.gd index 2cf32a37..d6c8a42d 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpClient.gd +++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd @@ -1,3 +1,4 @@ +@tool class_name GdUnitTcpClient extends GdUnitTcpNode @@ -100,7 +101,8 @@ func process_rpc() -> void: if _stream.get_available_bytes() > 0: var rpc_data := rpc_receive() if rpc_data is RPCClientDisconnect: - stop() + console("RPCClientDisconnect") + GdUnitSignals.instance().gdunit_test_session_terminate.emit.call_deferred() func send(data: RPC) -> void: diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid index 6b9a73fe..a86006fe 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid @@ -1 +1 @@ -uid://cdqknt40422ej +uid://gxguv41deuei diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd b/addons/gdUnit4/src/network/GdUnitTcpNode.gd index b051228b..156c764b 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpNode.gd +++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd @@ -4,7 +4,7 @@ extends Node func rpc_send(stream: StreamPeerTCP, data: RPC) -> void: var package_buffer := StreamPeerBuffer.new() - var buffer := data.serialize().to_ascii_buffer() + var buffer := data.serialize().to_utf16_buffer() package_buffer.put_u32(0xDEADBEEF) package_buffer.put_u32(buffer.size()) var status_code := package_buffer.put_data(buffer) @@ -59,7 +59,7 @@ func receive_packages(stream: StreamPeerTCP, rpc_cb: Callable = noop) -> Array[R else: data_package = buffer[1] - var json := data_package.get_string_from_ascii() + var json := data_package.get_string_from_utf16() if json.is_empty(): push_warning("json is empty, can't process data") continue diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid index 272805ba..293f8943 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid @@ -1 +1 @@ -uid://cbkf0u7jrre8m +uid://j5w27w2a1pb1 diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd b/addons/gdUnit4/src/network/GdUnitTcpServer.gd index 4687ac19..997d6e30 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpServer.gd +++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd @@ -51,6 +51,12 @@ class TcpConnection extends GdUnitTcpNode: ) + func disconnect_from_server() -> void: + if _stream == null or _stream.get_status() != StreamPeerTCP.STATUS_CONNECTED: + return + rpc_send(_stream, RPCClientDisconnect.new().with_id(_id)) + + func console(_value: Variant) -> void: #print_debug("TCP Server: ", value) pass @@ -58,6 +64,11 @@ class TcpConnection extends GdUnitTcpNode: func _init(server_name := "GdUnit4 TCP Server") -> void: _server_name = server_name + GdUnitSignals.instance().gdunit_test_session_terminate.connect(func() -> void: + for connection in get_children(): + if connection is TcpConnection: + (connection as TcpConnection).disconnect_from_server() + ) func _ready() -> void: diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid index e3d48020..b06e641b 100644 --- a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid +++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid @@ -1 +1 @@ -uid://b5rg08xa3h6x3 +uid://bynbl32gwmi05 diff --git a/addons/gdUnit4/src/network/rpc/RPC.gd.uid b/addons/gdUnit4/src/network/rpc/RPC.gd.uid index 7fc771c3..b12600b2 100644 --- a/addons/gdUnit4/src/network/rpc/RPC.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPC.gd.uid @@ -1 +1 @@ -uid://b78q0d0k2xhls +uid://djuth7enxmv6k diff --git a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid index 74ee5a9c..c3f4b0a3 100644 --- a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid @@ -1 +1 @@ -uid://dkro11eh12cxm +uid://cwlywd27woa25 diff --git a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid index e096f60e..484d9b97 100644 --- a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid @@ -1 +1 @@ -uid://chdib4feehlsb +uid://cawo8trfjo4xr diff --git a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid index 23b84604..9357a84e 100644 --- a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid @@ -1 +1 @@ -uid://bd5vwo3ck8v4c +uid://db5nakv3omoa8 diff --git a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid index 8f3132ec..5c756040 100644 --- a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid +++ b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid @@ -1 +1 @@ -uid://dengpjk16on7n +uid://mu3b5qjphqwu diff --git a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd b/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd deleted file mode 100644 index 18e6cc1a..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd +++ /dev/null @@ -1,191 +0,0 @@ -@tool -class_name GdUnitConsoleTestReporter -extends GdUnitTestReporter - -const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") - -var _writer: GdUnitMessageWritter -var _status_indent := 86 -var _detailed: bool -var _text_color: Color = Color.ANTIQUE_WHITE -var _function_color: Color = Color.ANTIQUE_WHITE -var _engine_type_color: Color = Color.ANTIQUE_WHITE - - - -func _init(writer: GdUnitMessageWritter, detailed := false) -> void: - _writer = writer - _writer.clear() - _detailed = detailed - if _detailed: - _status_indent = 20 - init_colors() - - -func init_colors() -> void: - if Engine.is_editor_hint(): - var settings := EditorInterface.get_editor_settings() - _text_color = settings.get_setting("text_editor/theme/highlighting/text_color") - _function_color = settings.get_setting("text_editor/theme/highlighting/function_color") - _engine_type_color = settings.get_setting("text_editor/theme/highlighting/engine_type_color") - - -func clear() -> void: - _writer.clear() - - -func on_gdunit_event(event: GdUnitEvent) -> void: - match event.type(): - GdUnitEvent.INIT: - init_summary() - - GdUnitEvent.STOP: - _print_summary() - println_message(build_executed_test_suite_msg(processed_suite_count(), processed_suite_count()), Color.DARK_SALMON) - println_message(build_executed_test_case_msg(total_test_count(), total_flaky_count()), Color.DARK_SALMON) - println_message("Total execution time: %s" % LocalTime.elapsed(elapsed_time()), Color.DARK_SALMON) - # We need finally to set the wave effect to enable the animations - _writer.effect(GdUnitMessageWritter.Effect.WAVE).print_at("", 0) - - GdUnitEvent.TESTSUITE_BEFORE: - init_statistics() - print_message("Run Test Suite: ", Color.DARK_TURQUOISE) - println_message(event.resource_path(), _engine_type_color) - - GdUnitEvent.TESTSUITE_AFTER: - if not event.reports().is_empty(): - _writer.color(Color.DARK_SALMON)\ - .style(GdUnitMessageWritter.BOLD)\ - .println_message(event.suite_name()+":finalze") - _print_failure_report(event.reports()) - _print_statistics(build_test_suite_statisitcs(event)) - _print_status(event) - println_message("") - if _detailed: - println_message("") - - GdUnitEvent.TESTCASE_BEFORE: - var test := find_test_by_id(event.guid()) - _print_test_path(test, event.guid()) - if _detailed: - _writer.color(Color.FOREST_GREEN).print_at("STARTED", _status_indent) - println_message("") - - GdUnitEvent.TESTCASE_AFTER: - var test := find_test_by_id(event.guid()) - update_statistics(event) - if _detailed: - _print_test_path(test, event.guid()) - _print_status(event) - _print_failure_report(event.reports()) - if _detailed: - println_message("") - - -func _print_test_path(test: GdUnitTestCase, uid: GdUnitGUID) -> void: - if test == null: - prints_warning("Can't print full test info, the test by uid: '%s' was not discovered." % uid) - _writer.indent(1).color(_engine_type_color).print_message("Test ID: %s" % uid) - return - - var suite_name := test.source_file if _detailed else test.suite_name - _writer.indent(1).color(_engine_type_color).print_message(suite_name) - print_message(" > ") - print_message(test.display_name, _function_color) - - -func _print_status(event: GdUnitEvent) -> void: - if event.is_flaky() and event.is_success(): - var retries: int = event.statistic(GdUnitEvent.RETRY_COUNT) - _writer.color(Color.GREEN_YELLOW)\ - .style(GdUnitMessageWritter.ITALIC)\ - .print_at("FLAKY (%d retries)" % retries, _status_indent) - elif event.is_success(): - _writer.color(Color.FOREST_GREEN).print_at("PASSED", _status_indent) - elif event.is_skipped(): - _writer.color(Color.GOLDENROD).style(GdUnitMessageWritter.ITALIC).print_at("SKIPPED", _status_indent) - elif event.is_failed() or event.is_error(): - var retries :int = event.statistic(GdUnitEvent.RETRY_COUNT) - var message := "FAILED (retry %d)" % retries if retries > 1 else "FAILED" - _writer.color(Color.FIREBRICK)\ - .style(GdUnitMessageWritter.BOLD)\ - .effect(GdUnitMessageWritter.Effect.WAVE)\ - .print_at(message, _status_indent) - elif event.is_warning(): - _writer.color(Color.GOLDENROD)\ - .style(GdUnitMessageWritter.UNDERLINE)\ - .print_at("WARNING", _status_indent) - - println_message(" %s" % LocalTime.elapsed(event.elapsed_time()), Color.CORNFLOWER_BLUE) - - -func _print_failure_report(reports: Array[GdUnitReport]) -> void: - for report in reports: - if ( - report.is_failure() - or report.is_error() - or report.is_warning() - or report.is_skipped() - ): - _writer.indent(1)\ - .color(Color.DARK_TURQUOISE)\ - .style(GdUnitMessageWritter.BOLD | GdUnitMessageWritter.UNDERLINE)\ - .println_message("Report:") - var text := GdUnitTools.richtext_normalize(str(report)) - for line in text.split("\n", false): - _writer.indent(2).color(Color.DARK_TURQUOISE).println_message(line) - - if not reports.is_empty(): - println_message("") - - -func _print_statistics(statistics: Dictionary) -> void: - print_message("Statistics:", Color.DODGER_BLUE) - print_message(" %d tests cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" %\ - [statistics["total_count"], - statistics["error_count"], - statistics["failed_count"], - statistics["flaky_count"], - statistics["skipped_count"], - statistics["orphan_nodes"]]) - - -func _print_summary() -> void: - print_message("Overall Summary:", Color.DODGER_BLUE) - _writer\ - .println_message(" %d tests cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" % [ - _summary["total_count"], - _summary["error_count"], - _summary["failed_count"], - _summary["flaky_count"], - _summary["skipped_count"], - _summary["orphan_nodes"] - ]) - - -func build_executed_test_suite_msg(executed_count: int, total_count: int) -> String: - if executed_count == total_count: - return "Executed test suites: (%d/%d)" % [executed_count, total_count] - return "Executed test suites: (%d/%d), %d skipped" % [executed_count, total_count, (total_count - executed_count)] - - -func build_executed_test_case_msg(total_count: int, skipped_count: int) -> String: - if skipped_count == 0: - return "Executed test cases : (%d/%d)" % [total_count, total_count] - return "Executed test cases : (%d/%d), %d skipped" % [total_count-skipped_count, total_count, skipped_count] - - -func print_message(message: String, color: Color = _text_color) -> void: - _writer.color(color).print_message(message) - - -func println_message(message: String, color: Color = _text_color) -> void: - _writer.color(color).println_message(message) - - -func prints_warning(message: String) -> void: - _writer.prints_warning(message) - - -func prints_error(message: String) -> void: - _writer.prints_error(message) diff --git a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid deleted file mode 100644 index 15b89628..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://duffxt287c2xv diff --git a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd deleted file mode 100644 index 4704050c..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd +++ /dev/null @@ -1,74 +0,0 @@ -@tool -class_name GdUnitHtmlTestReporter -extends GdUnitTestReporter - -var _report: GdUnitHtmlReport - - -func _init(report_dir: String, max_reports: int) -> void: - _report = GdUnitHtmlReport.new(report_dir, max_reports) - - -func on_gdunit_event(event: GdUnitEvent) -> void: - match event.type(): - GdUnitEvent.INIT: - init_summary() - GdUnitEvent.STOP: - _report.write() - _report.delete_history() - GdUnitEvent.TESTSUITE_BEFORE: - init_statistics() - _report.add_testsuite_report(event.resource_path(), event.suite_name(), event.total_count()) - GdUnitEvent.TESTSUITE_AFTER: - var statistics := build_test_suite_statisitcs(event) - _report.update_testsuite_counters( - event.resource_path(), - error_count(statistics), - failed_count(statistics), - orphan_nodes(statistics), - skipped_count(statistics), - flaky_count(statistics), - event.elapsed_time()) - _report.add_testsuite_reports( - event.resource_path(), - event.reports() - ) - GdUnitEvent.TESTCASE_BEFORE: - var test := find_test_by_id(event.guid()) - _report.add_testcase(test.source_file, test.suite_name, test.display_name) - GdUnitEvent.TESTCASE_AFTER: - update_statistics(event) - var test := find_test_by_id(event.guid()) - _report.set_testcase_counters(test.source_file, - test.display_name, - event.error_count(), - event.failed_count(), - event.orphan_nodes(), - event.is_skipped(), - event.is_flaky(), - event.elapsed_time()) - _report.add_testcase_reports(test.source_file, test.display_name, event.reports()) - - -func report_file() -> String: - return _report.report_file() - - -func error_count(statistics: Dictionary) -> int: - return statistics["error_count"] - - -func failed_count(statistics: Dictionary) -> int: - return statistics["failed_count"] - - -func orphan_nodes(statistics: Dictionary) -> int: - return statistics["orphan_nodes"] - - -func skipped_count(statistics: Dictionary) -> int: - return statistics["skipped_count"] - - -func flaky_count(statistics: Dictionary) -> int: - return statistics["flaky_count"] diff --git a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid deleted file mode 100644 index 1e086a98..00000000 --- a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ckpa3m7t165g2 diff --git a/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd new file mode 100644 index 00000000..b55b964f --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd @@ -0,0 +1,202 @@ +class_name GdUnitReportSummary +extends RefCounted + +var _resource_path: String +var _name: String +var _test_count := 0 +var _failure_count := 0 +var _error_count := 0 +var _orphan_count := 0 +var _skipped_count := 0 +var _flaky_count := 0 +var _duration := 0 +var _reports: Array[GdUnitReportSummary] = [] +var _text_formatter: Callable + + +func _init(text_formatter: Callable) -> void: + _text_formatter = text_formatter + + +func name() -> String: + return _name + + +func path() -> String: + return _resource_path.get_base_dir().replace("res://", "") + + +func get_resource_path() -> String: + return _resource_path + + +func suite_count() -> int: + return _reports.size() + + +func suite_executed_count() -> int: + var executed := _reports.size() + for report in _reports: + if report.test_count() == report.skipped_count(): + executed -= 1 + return executed + + +func test_count() -> int: + var count := _test_count + for report in _reports: + count += report.test_count() + return count + + +func test_executed_count() -> int: + return test_count() - skipped_count() + + +func success_count() -> int: + return test_count() - error_count() - failure_count() - flaky_count() - skipped_count() + + +func error_count() -> int: + return _error_count + + +func failure_count() -> int: + return _failure_count + + +func skipped_count() -> int: + return _skipped_count + + +func flaky_count() -> int: + return _flaky_count + + +func orphan_count() -> int: + return _orphan_count + + +func duration() -> int: + return _duration + + +func get_reports() -> Array: + return _reports + + +func add_report(report: GdUnitReportSummary) -> void: + _reports.append(report) + + +func report_state() -> String: + return calculate_state(error_count(), failure_count(), orphan_count(), flaky_count(), skipped_count()) + + +func succes_rate() -> String: + return calculate_succes_rate(test_count(), error_count(), failure_count()) + + +@warning_ignore("shadowed_variable") +func add_testcase(resource_path: String, suite_name: String, test_name: String) -> void: + for report: GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == resource_path: + var test_report := GdUnitTestCaseReport.new(resource_path, suite_name, test_name, _text_formatter) + report.add_or_create_test_report(test_report) + + +func add_reports( + p_resource_path: String, + p_test_name: String, + p_reports: Array[GdUnitReport]) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.add_testcase_reports(p_test_name, p_reports) + + +func add_testsuite_report(p_resource_path: String, p_suite_name: String, p_test_count: int) -> void: + _reports.append(GdUnitTestSuiteReport.new(p_resource_path, p_suite_name, p_test_count, _text_formatter)) + + +func add_testsuite_reports( + p_resource_path: String, + p_reports: Array = []) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.set_reports(p_reports) + + +func set_counters( + p_resource_path: String, + p_test_name: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + + for report: GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report.set_testcase_counters(p_test_name, p_error_count, p_failure_count, p_orphan_count, + p_is_skipped, p_is_flaky, p_duration) + + +func update_testsuite_counters( + p_resource_path: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + + for report:GdUnitTestSuiteReport in _reports: + if report.get_resource_path() == p_resource_path: + report._update_testsuite_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, p_duration) + _update_summary_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, 0) + + +func _update_summary_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + + _error_count += p_error_count + _failure_count += p_failure_count + _orphan_count += p_orphan_count + _skipped_count += p_skipped_count + _flaky_count += p_flaky_count + _duration += p_duration + + +func calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int, p_skipped_count: int) -> String: + if p_error_count > 0: + return "ERROR" + if p_failure_count > 0: + return "FAILED" + if p_flaky_count > 0: + return "FLAKY" + if p_orphan_count > 0: + return "WARNING" + if p_skipped_count > 0: + return "SKIPPED" + return "PASSED" + + +func calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String: + if p_failure_count == 0: + return "100%" + var count := p_test_count-p_failure_count-p_error_count + if count < 0: + return "0%" + return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" + + +func create_summary(_report_dir :String) -> String: + return "" diff --git a/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid new file mode 100644 index 00000000..93962144 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportSummary.gd.uid @@ -0,0 +1 @@ +uid://bpcbke3bb3xs0 diff --git a/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd new file mode 100644 index 00000000..bf4c96ea --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd @@ -0,0 +1,12 @@ +class_name GdUnitReportWriter +extends RefCounted + + +func write(_report_path: String, _report: GdUnitReportSummary) -> String: + assert(false, "'write' is not implemented!") + return "" + + +func output_format() -> String: + assert(false, "'output_format' is not implemented!") + return "" diff --git a/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid new file mode 100644 index 00000000..67129617 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitReportWriter.gd.uid @@ -0,0 +1 @@ +uid://bwq0h0g1hq1s6 diff --git a/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd new file mode 100644 index 00000000..2801696c --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd @@ -0,0 +1,47 @@ +class_name GdUnitTestCaseReport +extends GdUnitReportSummary + + +var _suite_name: String +var _failure_reports: Array[GdUnitReport] = [] + + +func _init(p_resource_path: String, p_suite_name: String, p_test_name: String, text_formatter: Callable) -> void: + _resource_path = p_resource_path + _suite_name = p_suite_name + _name = p_test_name + _text_formatter = text_formatter + + +func suite_name() -> String: + return _suite_name + + +func failure_report() -> String: + var report_message := "" + for report in get_test_reports(): + report_message += _text_formatter.call(str(report)) + "\n" + return report_message + + +func add_testcase_reports(reports: Array[GdUnitReport]) -> void: + _failure_reports.append_array(reports) + + +func set_testcase_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + _error_count = p_error_count + _failure_count = p_failure_count + _orphan_count = p_orphan_count + _skipped_count = p_is_skipped + _flaky_count = p_is_flaky as int + _duration = p_duration + + +func get_test_reports() -> Array[GdUnitReport]: + return _failure_reports diff --git a/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid new file mode 100644 index 00000000..cf8196f7 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestCaseReport.gd.uid @@ -0,0 +1 @@ +uid://bns8uncxy30tt diff --git a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd index 0749c140..46b81931 100644 --- a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd +++ b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd @@ -1,15 +1,11 @@ class_name GdUnitTestReporter extends RefCounted -var _guard := GdUnitTestDiscoverGuard.instance() + var _statistics := {} var _summary := {} -func on_gdunit_event(_event: GdUnitEvent) -> void: - push_error("Reporter: 'on_gdunit_event' is not implemented!") - - func init_summary() -> void: _summary["suite_count"] = 0 _summary["total_count"] = 0 @@ -25,18 +21,14 @@ func init_statistics() -> void: _statistics.clear() -func update_statistics(event: GdUnitEvent) -> void: - var test_statisitics: Dictionary = _statistics.get_or_add(event.guid(), { - "error_count" : 0, - "failed_count" : 0, - "skipped_count" : event.is_skipped() as int, - "flaky_count" : 0, - "orphan_nodes" : 0 - }) - test_statisitics["error_count"] = event.is_error() as int - test_statisitics["failed_count"] = event.is_failed() as int - test_statisitics["flaky_count"] = event.is_flaky() as int - test_statisitics["orphan_nodes"] = event.orphan_nodes() +func add_test_statistics(event: GdUnitEvent) -> void: + _statistics[event.guid()] = { + "error_count" : event.error_count(), + "failed_count" : event.failed_count(), + "skipped_count" : event.skipped_count(), + "flaky_count" : event.is_flaky() as int, + "orphan_nodes" : event.orphan_nodes() + } func build_test_suite_statisitcs(event: GdUnitEvent) -> Dictionary: @@ -50,15 +42,15 @@ func build_test_suite_statisitcs(event: GdUnitEvent) -> Dictionary: } _summary["suite_count"] += 1 _summary["total_count"] += _statistics.size() - # Add the suite hook specific counters - _summary["error_count"] += event.error_count() - _summary["failed_count"] += event.failed_count() - _summary["orphan_nodes"] += event.orphan_nodes() + _summary["error_count"] += event.error_count() + _summary["failed_count"] += event.failed_count() + _summary["skipped_count"] += event.skipped_count() + _summary["orphan_nodes"] += event.orphan_nodes() _summary["elapsed_time"] += event.elapsed_time() for key: String in ["error_count", "failed_count", "skipped_count", "flaky_count", "orphan_nodes"]: var value: int = _statistics.values().reduce(get_value.bind(key), 0 ) - statistic[key] = value + statistic[key] += value _summary[key] += value return statistic @@ -68,10 +60,6 @@ func get_value(acc: int, value: Dictionary, key: String) -> int: return acc + value[key] -func find_test_by_id(id: GdUnitGUID) -> GdUnitTestCase: - return _guard.find_test_by_id(id) - - func processed_suite_count() -> int: return _summary["suite_count"] @@ -102,3 +90,23 @@ func total_orphan_count() -> int: func elapsed_time() -> int: return _summary["elapsed_time"] + + +func error_count(statistics: Dictionary) -> int: + return statistics["error_count"] + + +func failed_count(statistics: Dictionary) -> int: + return statistics["failed_count"] + + +func orphan_nodes(statistics: Dictionary) -> int: + return statistics["orphan_nodes"] + + +func skipped_count(statistics: Dictionary) -> int: + return statistics["skipped_count"] + + +func flaky_count(statistics: Dictionary) -> int: + return statistics["flaky_count"] diff --git a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid index df62c4d9..878ec24f 100644 --- a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid +++ b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid @@ -1 +1 @@ -uid://cqyx7s50kvgit +uid://oopqhgo80cai diff --git a/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd new file mode 100644 index 00000000..9be06825 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd @@ -0,0 +1,96 @@ +class_name GdUnitTestSuiteReport +extends GdUnitReportSummary + +var _time_stamp: int +var _failure_reports: Array[GdUnitReport] = [] + + +func _init(p_resource_path: String, p_name: String, p_test_count: int, text_formatter: Callable) -> void: + _resource_path = p_resource_path + _name = p_name + _test_count = p_test_count + _time_stamp = Time.get_unix_time_from_system() as int + _text_formatter = text_formatter + + +func failure_report() -> String: + var report_message := "" + for report in _failure_reports: + report_message += _text_formatter.call(str(report)) + return report_message + + +func set_duration(p_duration :int) -> void: + _duration = p_duration + + +func time_stamp() -> int: + return _time_stamp + + +func duration() -> int: + return _duration + + +func set_skipped(skipped :int) -> void: + _skipped_count += skipped + + +func set_orphans(orphans :int) -> void: + _orphan_count = orphans + + +func set_failed(count :int) -> void: + _failure_count += count + + +func set_reports(failure_reports :Array[GdUnitReport]) -> void: + _failure_reports = failure_reports + + +func add_or_create_test_report(test_report: GdUnitTestCaseReport) -> void: + _reports.append(test_report) + + +func _update_testsuite_counters( + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_skipped_count: int, + p_flaky_count: int, + p_duration: int) -> void: + _error_count += p_error_count + _failure_count += p_failure_count + _orphan_count += p_orphan_count + _skipped_count += p_skipped_count + _flaky_count += p_flaky_count + _duration += p_duration + + +func set_testcase_counters( + test_name: String, + p_error_count: int, + p_failure_count: int, + p_orphan_count: int, + p_is_skipped: bool, + p_is_flaky: bool, + p_duration: int) -> void: + if _reports.is_empty(): + return + var test_report: GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: + return report.name() == test_name + ).back() + if test_report: + test_report.set_testcase_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration) + + +func add_testcase_reports(test_name: String, reports: Array[GdUnitReport]) -> void: + if reports.is_empty(): + return + # we lookup to latest matching report because of flaky tests could be retry the tests + # and resultis in multipe report entries with the same name + var test_report: GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: + return report.name() == test_name + ).back() + if test_report: + test_report.add_testcase_reports(reports) diff --git a/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid new file mode 100644 index 00000000..23b17aa8 --- /dev/null +++ b/addons/gdUnit4/src/reporters/GdUnitTestSuiteReport.gd.uid @@ -0,0 +1 @@ +uid://irglqriwgfeg diff --git a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd b/addons/gdUnit4/src/reporters/JUnitXmlReport.gd deleted file mode 100644 index c9bdd460..00000000 --- a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd +++ /dev/null @@ -1,145 +0,0 @@ -# This class implements the JUnit XML file format -# based checked https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd -class_name JUnitXmlReport -extends RefCounted - -const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") - -const ATTR_CLASSNAME := "classname" -const ATTR_ERRORS := "errors" -const ATTR_FAILURES := "failures" -const ATTR_HOST := "hostname" -const ATTR_ID := "id" -const ATTR_MESSAGE := "message" -const ATTR_NAME := "name" -const ATTR_PACKAGE := "package" -const ATTR_SKIPPED := "skipped" -const ATTR_FLAKY := "flaky" -const ATTR_TESTS := "tests" -const ATTR_TIME := "time" -const ATTR_TIMESTAMP := "timestamp" -const ATTR_TYPE := "type" - -const HEADER := '\n' - -var _report_path :String -var _iteration :int - - -func _init(path :String, iteration :int) -> void: - _iteration = iteration - _report_path = path - - -func write(report :GdUnitReportSummary) -> String: - var result_file: String = "%s/results.xml" % _report_path - var file := FileAccess.open(result_file, FileAccess.WRITE) - if file == null: - push_warning("Can't saving the result to '%s'\n Error: %s" % [result_file, error_string(FileAccess.get_open_error())]) - file.store_string(build_junit_report(report)) - return result_file - - -func build_junit_report(report :GdUnitReportSummary) -> String: - var iso8601_datetime := Time.get_date_string_from_system() - var test_suites := XmlElement.new("testsuites")\ - .attribute(ATTR_ID, iso8601_datetime)\ - .attribute(ATTR_NAME, "report_%s" % _iteration)\ - .attribute(ATTR_TESTS, report.test_count())\ - .attribute(ATTR_FAILURES, report.failure_count())\ - .attribute(ATTR_SKIPPED, report.skipped_count())\ - .attribute(ATTR_FLAKY, report.flaky_count())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\ - .add_childs(build_test_suites(report)) - var as_string := test_suites.to_xml() - test_suites.dispose() - return HEADER + as_string - - -func build_test_suites(summary :GdUnitReportSummary) -> Array: - var test_suites :Array[XmlElement] = [] - for index in summary.get_reports().size(): - var suite_report :GdUnitTestSuiteReport = summary.get_reports()[index] - var iso8601_datetime := Time.get_datetime_string_from_unix_time(suite_report.time_stamp()) - test_suites.append(XmlElement.new("testsuite")\ - .attribute(ATTR_ID, index)\ - .attribute(ATTR_NAME, suite_report.name())\ - .attribute(ATTR_PACKAGE, suite_report.path())\ - .attribute(ATTR_TIMESTAMP, iso8601_datetime)\ - .attribute(ATTR_HOST, "localhost")\ - .attribute(ATTR_TESTS, suite_report.test_count())\ - .attribute(ATTR_FAILURES, suite_report.failure_count())\ - .attribute(ATTR_ERRORS, suite_report.error_count())\ - .attribute(ATTR_SKIPPED, suite_report.skipped_count())\ - .attribute(ATTR_FLAKY, suite_report.flaky_count())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(suite_report.duration()))\ - .add_childs(build_test_cases(suite_report))) - return test_suites - - -func build_test_cases(suite_report :GdUnitTestSuiteReport) -> Array: - var test_cases :Array[XmlElement] = [] - for index in suite_report.get_reports().size(): - var report :GdUnitTestCaseReport = suite_report.get_reports()[index] - test_cases.append( XmlElement.new("testcase")\ - .attribute(ATTR_NAME, JUnitXmlReport.encode_xml(report.name()))\ - .attribute(ATTR_CLASSNAME, report.suite_name())\ - .attribute(ATTR_TIME, JUnitXmlReport.to_time(report.duration()))\ - .add_childs(build_reports(report))) - return test_cases - - -func build_reports(test_report: GdUnitTestCaseReport) -> Array: - var failure_reports :Array[XmlElement] = [] - - for report: GdUnitReport in test_report.get_test_reports(): - if report.is_failure(): - failure_reports.append(XmlElement.new("failure")\ - .attribute(ATTR_MESSAGE, "FAILED: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ - .attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\ - .text(convert_rtf_to_text(report.message()))) - elif report.is_error(): - failure_reports.append(XmlElement.new("error")\ - .attribute(ATTR_MESSAGE, "ERROR: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ - .attribute(ATTR_TYPE, JUnitXmlReport.to_type(report.type()))\ - .text(convert_rtf_to_text(report.message()))) - elif report.is_skipped(): - failure_reports.append(XmlElement.new("skipped")\ - .attribute(ATTR_MESSAGE, "SKIPPED: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ - .text(convert_rtf_to_text(report.message()))) - return failure_reports - - -func convert_rtf_to_text(bbcode :String) -> String: - return GdUnitTools.richtext_normalize(bbcode) - - -static func to_type(type :int) -> String: - match type: - GdUnitReport.SUCCESS: - return "SUCCESS" - GdUnitReport.WARN: - return "WARN" - GdUnitReport.FAILURE: - return "FAILURE" - GdUnitReport.ORPHAN: - return "ORPHAN" - GdUnitReport.TERMINATED: - return "TERMINATED" - GdUnitReport.INTERUPTED: - return "INTERUPTED" - GdUnitReport.ABORT: - return "ABORT" - return "UNKNOWN" - - -static func to_time(duration :int) -> String: - return "%4.03f" % (duration / 1000.0) - - -static func encode_xml(value :String) -> String: - return value.xml_escape(true) - - -#static func to_ISO8601_datetime() -> String: - #return "%04d-%02d-%02dT%02d:%02d:%02d" % [date["year"], date["month"], date["day"], date["hour"], date["minute"], date["second"]] diff --git a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid b/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid deleted file mode 100644 index 08b83e47..00000000 --- a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ckt5bbakcyu1m diff --git a/addons/gdUnit4/src/reporters/XmlElement.gd.uid b/addons/gdUnit4/src/reporters/XmlElement.gd.uid deleted file mode 100644 index 670bd0ad..00000000 --- a/addons/gdUnit4/src/reporters/XmlElement.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cq27dcqhpeglp diff --git a/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd new file mode 100644 index 00000000..9978deb3 --- /dev/null +++ b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd @@ -0,0 +1,235 @@ +@tool +class_name GdUnitConsoleTestReporter + + +var test_session: GdUnitTestSession: + get: + return test_session + set(value): + # disconnect first possible connected listener + if test_session != null: + test_session.test_event.disconnect(on_gdunit_event) + # add listening to current session + test_session = value + if test_session != null: + test_session.test_event.connect(on_gdunit_event) + + +var _writer: GdUnitMessageWriter +var _reporter: GdUnitTestReporter = GdUnitTestReporter.new() +var _status_indent := 86 +var _detailed: bool +var _text_color: Color = Color.ANTIQUE_WHITE +var _function_color: Color = Color.ANTIQUE_WHITE +var _engine_type_color: Color = Color.ANTIQUE_WHITE + + +func _init(writer: GdUnitMessageWriter, detailed := false) -> void: + _writer = writer + _writer.clear() + _detailed = detailed + if _detailed: + _status_indent = 20 + init_colors() + + +func init_colors() -> void: + if Engine.is_editor_hint(): + var settings := EditorInterface.get_editor_settings() + _text_color = settings.get_setting("text_editor/theme/highlighting/text_color") + _function_color = settings.get_setting("text_editor/theme/highlighting/function_color") + _engine_type_color = settings.get_setting("text_editor/theme/highlighting/engine_type_color") + + +func clear() -> void: + _writer.clear() + + +func on_gdunit_event(event: GdUnitEvent) -> void: + match event.type(): + GdUnitEvent.INIT: + _reporter.init_summary() + + GdUnitEvent.STOP: + _print_summary() + println_message(build_executed_test_suite_msg(processed_suite_count(), processed_suite_count()), Color.DARK_SALMON) + println_message(build_executed_test_case_msg(total_test_count(), total_skipped_count()), Color.DARK_SALMON) + println_message("Total execution time: %s" % LocalTime.elapsed(elapsed_time()), Color.DARK_SALMON) + # We need finally to set the wave effect to enable the animations + _writer.effect(GdUnitMessageWriter.Effect.WAVE).print_at("", 0) + + GdUnitEvent.TESTSUITE_BEFORE: + _reporter.init_statistics() + print_message("Run Test Suite: ", Color.DARK_TURQUOISE) + println_message(event.resource_path(), _engine_type_color) + + GdUnitEvent.TESTSUITE_AFTER: + if not event.reports().is_empty(): + _writer.indent(1).color(_engine_type_color).print_message(event._suite_name) + print_message(" > ") + print_message("finalize()", _function_color) + _print_failure_report(event.reports()) + _print_statistics(_reporter.build_test_suite_statisitcs(event)) + _print_status(event) + println_message("") + if _detailed: + println_message("") + + GdUnitEvent.TESTCASE_BEFORE: + var test := test_session.find_test_by_id(event.guid()) + _print_test_path(test, event.guid()) + if _detailed: + _writer.color(Color.FOREST_GREEN).print_at("STARTED", _status_indent) + println_message("") + + GdUnitEvent.TESTCASE_AFTER: + _reporter.add_test_statistics(event) + if _detailed: + var test := test_session.find_test_by_id(event.guid()) + _print_test_path(test, event.guid()) + _print_status(event) + _print_failure_report(event.reports()) + if _detailed: + println_message("") + + +func _print_test_path(test: GdUnitTestCase, uid: GdUnitGUID) -> void: + if test == null: + prints_warning("Can't print full test info, the test by uid: '%s' was not discovered." % uid) + _writer.indent(1).color(_engine_type_color).print_message("Test ID: %s" % uid) + return + + var suite_name := test.source_file if _detailed else test.suite_name + _writer.indent(1).color(_engine_type_color).print_message(suite_name) + print_message(" > ") + print_message(test.display_name, _function_color) + + +func _print_status(event: GdUnitEvent) -> void: + if event.is_flaky() and event.is_success(): + var retries: int = event.statistic(GdUnitEvent.RETRY_COUNT) + _writer.color(Color.GREEN_YELLOW) \ + .style(GdUnitMessageWriter.ITALIC) \ + .print_at("FLAKY (%d retries)" % retries, _status_indent) + elif event.is_success(): + _writer.color(Color.FOREST_GREEN).print_at("PASSED", _status_indent) + elif event.is_skipped(): + _writer.color(Color.GOLDENROD).style(GdUnitMessageWriter.ITALIC).print_at("SKIPPED", _status_indent) + elif event.is_failed() or event.is_error(): + var retries: int = event.statistic(GdUnitEvent.RETRY_COUNT) + var message := "FAILED (retry %d)" % retries if retries > 1 else "FAILED" + _writer.color(Color.FIREBRICK) \ + .style(GdUnitMessageWriter.BOLD) \ + .effect(GdUnitMessageWriter.Effect.WAVE) \ + .print_at(message, _status_indent) + elif event.is_warning(): + _writer.color(Color.GOLDENROD) \ + .style(GdUnitMessageWriter.UNDERLINE) \ + .print_at("WARNING", _status_indent) + + println_message(" %s" % LocalTime.elapsed(event.elapsed_time()), Color.CORNFLOWER_BLUE) + + +func _print_failure_report(reports: Array[GdUnitReport]) -> void: + for report in reports: + if ( + report.is_failure() + or report.is_error() + or report.is_warning() + or report.is_skipped() + or report.is_orphan() + ): + _writer.indent(1) \ + .color(Color.DARK_TURQUOISE) \ + .style(GdUnitMessageWriter.BOLD | GdUnitMessageWriter.UNDERLINE) \ + .println_message("Report:") + var text := str(report) + for line in text.split("\n", false): + _writer.indent(2).color(Color.DARK_TURQUOISE).println_message(line) + + if not reports.is_empty(): + println_message("") + + +func _print_statistics(statistics: Dictionary) -> void: + print_message("Statistics:", Color.DODGER_BLUE) + print_message(" %d test cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" % \ + [statistics["total_count"], + statistics["error_count"], + statistics["failed_count"], + statistics["flaky_count"], + statistics["skipped_count"], + statistics["orphan_nodes"]]) + + +func _print_summary() -> void: + print_message("Overall Summary:", Color.DODGER_BLUE) + _writer \ + .println_message(" %d test cases | %d errors | %d failures | %d flaky | %d skipped | %d orphans |" % [ + total_test_count(), + total_error_count(), + total_failure_count(), + total_flaky_count(), + total_skipped_count(), + total_orphan_count() + ]) + + +func build_executed_test_suite_msg(executed_count: int, total_count: int) -> String: + if executed_count == total_count: + return "Executed test suites: (%d/%d)" % [executed_count, total_count] + return "Executed test suites: (%d/%d), %d skipped" % [executed_count, total_count, (total_count - executed_count)] + + +func build_executed_test_case_msg(total_count: int, p_skipped_count: int) -> String: + if p_skipped_count == 0: + return "Executed test cases : (%d/%d)" % [total_count, total_count] + return "Executed test cases : (%d/%d), %d skipped" % [total_count - p_skipped_count, total_count, p_skipped_count] + + +func print_message(message: String, color: Color = _text_color) -> void: + _writer.color(color).print_message(message) + + +func println_message(message: String, color: Color = _text_color) -> void: + _writer.color(color).println_message(message) + + +func prints_warning(message: String) -> void: + _writer.prints_warning(message) + + +func prints_error(message: String) -> void: + _writer.prints_error(message) + + +func total_test_count() -> int: + return _reporter.total_test_count() + + +func total_error_count() -> int: + return _reporter.total_error_count() + + +func total_failure_count() -> int: + return _reporter.total_failure_count() + + +func total_flaky_count() -> int: + return _reporter.total_flaky_count() + + +func total_skipped_count() -> int: + return _reporter.total_skipped_count() + + +func total_orphan_count() -> int: + return _reporter.total_orphan_count() + + +func processed_suite_count() -> int: + return _reporter.processed_suite_count() + + +func elapsed_time() -> int: + return _reporter.elapsed_time() diff --git a/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid new file mode 100644 index 00000000..d6714d38 --- /dev/null +++ b/addons/gdUnit4/src/reporters/console/GdUnitConsoleTestReporter.gd.uid @@ -0,0 +1 @@ +uid://3i5akcq5nfnt diff --git a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd index 272f76c4..74b961b4 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd +++ b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd @@ -44,9 +44,9 @@ func write(report_dir :String) -> String: func apply_testsuite_reports(report_dir :String, template :String, test_suite_reports :Array[GdUnitReportSummary]) -> String: var table_records := PackedStringArray() for report:GdUnitTestSuiteReport in test_suite_reports: - var report_link := report.output_path(report_dir).replace(report_dir, "..") + var report_link := GdUnitHtmlReportWriter.create_output_path(report_dir, report.path(), report.name()).replace(report_dir, "..") @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link)) + table_records.append(GdUnitHtmlPatterns.create_suite_record(report_link, report)) return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid index c80e9b0f..baaab64f 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid +++ b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid @@ -1 +1 @@ -uid://yo22m618tcs3 +uid://v64b7bx0thci diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd index 03492d5f..b8be904f 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd @@ -78,6 +78,11 @@ ${failure-report} """ +const CHARACTERS_TO_ENCODE := { + '<' : '<', + '>' : '>' +} + const TABLE_BY_PATHS = "${report_table_paths}" const TABLE_BY_TESTSUITES = "${report_table_testsuites}" const TABLE_BY_TESTCASES = "${report_table_tests}" @@ -121,7 +126,7 @@ static func build(template: String, report: GdUnitReportSummary, report_link: St .replace(PATH, get_report_path(report))\ .replace(BREADCRUMP_PATH_LINK, get_path_as_link(report))\ .replace(RESOURCE_PATH, report.get_resource_path())\ - .replace(TESTSUITE_NAME, report.name_html_encoded())\ + .replace(TESTSUITE_NAME, html_encoded(report.name()))\ .replace(TESTSUITE_COUNT, str(report.suite_count()))\ .replace(TESTCASE_COUNT, str(report.test_count()))\ .replace(FAILURE_COUNT, str(report.error_count() + report.failure_count()))\ @@ -161,3 +166,34 @@ static func calculate_percentage(p_test_count: int, count: int) -> String: if count <= 0: return "0%" return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" + + +static func html_encoded(value: String) -> String: + for key: String in CHARACTERS_TO_ENCODE.keys(): + @warning_ignore("unsafe_cast") + value = value.replace(key, CHARACTERS_TO_ENCODE[key] as String) + return value + + +static func create_suite_record(report_link: String, report: GdUnitTestSuiteReport) -> String: + return GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_TESTSUITE, report, report_link) + + +static func create_test_failure_report(_report_dir :String, report: GdUnitTestCaseReport) -> String: + return GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report.report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report.report_state())\ + .replace(GdUnitHtmlPatterns.TESTCASE_NAME, report.name())\ + .replace(GdUnitHtmlPatterns.SKIPPED_COUNT, str(report.skipped_count()))\ + .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(report.orphan_count()))\ + .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(report._duration))\ + .replace(GdUnitHtmlPatterns.FAILURE_REPORT, report.failure_report()) + + +static func create_suite_failure_report(report: GdUnitTestSuiteReport) -> String: + return GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\ + .replace(GdUnitHtmlPatterns.REPORT_STATE, report.report_state().to_lower())\ + .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report.report_state())\ + .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(report.orphan_count()))\ + .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(report._duration))\ + .replace(GdUnitHtmlPatterns.FAILURE_REPORT, report.failure_report()) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid index e3e8a692..88035461 100644 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid @@ -1 +1 @@ -uid://bd6iibim1mjbg +uid://srjc1swl2lpo diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd deleted file mode 100644 index 8ff856c6..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd +++ /dev/null @@ -1,145 +0,0 @@ -class_name GdUnitHtmlReport -extends GdUnitReportSummary - -const REPORT_DIR_PREFIX = "report_" - -var _report_path: String -var _iteration: int -var _max_reports: int - - -func _init(report_path :String, max_reports: int) -> void: - _max_reports = max_reports - if max_reports > 1: - _iteration = GdUnitFileAccess.find_last_path_index(report_path, REPORT_DIR_PREFIX) + 1 - else: - _iteration = 1 - _report_path = "%s/%s%d" % [report_path, REPORT_DIR_PREFIX, _iteration] - @warning_ignore("return_value_discarded") - DirAccess.make_dir_recursive_absolute(_report_path) - - -func add_testsuite_report(p_resource_path: String, p_suite_name: String, p_test_count: int) -> void: - _reports.append(GdUnitTestSuiteReport.new(p_resource_path, p_suite_name, p_test_count)) - - -@warning_ignore("shadowed_variable") -func add_testcase(resource_path :String, suite_name :String, test_name: String) -> void: - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == resource_path: - var test_report := GdUnitTestCaseReport.new(resource_path, suite_name, test_name) - report.add_or_create_test_report(test_report) - - -func add_testsuite_reports( - p_resource_path :String, - p_reports :Array = []) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.set_reports(p_reports) - - -func add_testcase_reports( - p_resource_path: String, - p_test_name: String, - p_reports: Array[GdUnitReport]) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.add_testcase_reports(p_test_name, p_reports) - - -func update_testsuite_counters( - p_resource_path :String, - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.update_testsuite_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, p_duration) - update_summary_counters(p_error_count, p_failure_count, p_orphan_count, p_skipped_count, p_flaky_count, 0) - - -func set_testcase_counters( - p_resource_path: String, - p_test_name: String, - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_is_skipped: bool, - p_is_flaky: bool, - p_duration: int) -> void: - - for report:GdUnitTestSuiteReport in _reports: - if report.get_resource_path() == p_resource_path: - report.set_testcase_counters(p_test_name, p_error_count, p_failure_count, p_orphan_count, - p_is_skipped, p_is_flaky, p_duration) - - -func update_summary_counters( - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - - _error_count += p_error_count - _failure_count += p_failure_count - _orphan_count += p_orphan_count - _skipped_count += p_skipped_count - _flaky_count += p_flaky_count - _duration += p_duration - - -func write() -> void: - var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/index.html") - var to_write := GdUnitHtmlPatterns.build(template, self, "") - to_write = apply_path_reports(_report_path, to_write, _reports) - to_write = apply_testsuite_reports(_report_path, to_write, _reports) - # write report - FileAccess.open(report_file(), FileAccess.WRITE).store_string(to_write) - @warning_ignore("return_value_discarded") - GdUnitFileAccess.copy_directory("res://addons/gdUnit4/src/reporters/html/template/css/", _report_path + "/css") - - -func report_file() -> String: - return "%s/index.html" % _report_path - - -func delete_history() -> int: - return GdUnitFileAccess.delete_path_index_lower_equals_than(_report_path.get_base_dir(), REPORT_DIR_PREFIX, _iteration-_max_reports) - - -func apply_path_reports(report_dir :String, template :String, report_summaries :Array) -> String: - #Dictionary[String, Array[GdUnitReportSummary]] - var path_report_mapping := GdUnitByPathReport.sort_reports_by_path(report_summaries) - var table_records := PackedStringArray() - var paths :Array[String] = [] - paths.append_array(path_report_mapping.keys()) - paths.sort() - for report_path in paths: - var reports: Array[GdUnitReportSummary] = path_report_mapping.get(report_path) - var report := GdUnitByPathReport.new(report_path, reports) - var report_link :String = report.write(report_dir).replace(report_dir, ".") - @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link)) - return template.replace(GdUnitHtmlPatterns.TABLE_BY_PATHS, "\n".join(table_records)) - - -func apply_testsuite_reports(report_dir: String, template: String, test_suite_reports: Array[GdUnitReportSummary]) -> String: - var table_records := PackedStringArray() - for report: GdUnitTestSuiteReport in test_suite_reports: - var report_link :String = report.write(report_dir).replace(report_dir, ".") - @warning_ignore("return_value_discarded") - table_records.append(report.create_record(report_link) as String) - return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) - - -func iteration() -> int: - return _iteration diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid deleted file mode 100644 index f01d91b3..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dlwag4dpcgbo8 diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd new file mode 100644 index 00000000..a3d2bfff --- /dev/null +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd @@ -0,0 +1,72 @@ +class_name GdUnitHtmlReportWriter +extends GdUnitReportWriter + + +func output_format() -> String: + return "HTML" + + +func write(report_path: String, report: GdUnitReportSummary) -> String: + var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/index.html") + var to_write := GdUnitHtmlPatterns.build(template, report, "") + to_write = _apply_path_reports(report_path, to_write, report.get_reports()) + to_write = _apply_testsuite_reports(report_path, to_write, report.get_reports()) + # write report + DirAccess.make_dir_recursive_absolute(report_path) + var html_report_file := "%s/index.html" % report_path + FileAccess.open(html_report_file, FileAccess.WRITE).store_string(to_write) + @warning_ignore("return_value_discarded") + GdUnitFileAccess.copy_directory("res://addons/gdUnit4/src/reporters/html/template/css/", report_path + "/css") + return html_report_file + + +func _apply_path_reports(report_dir: String, template: String, report_summaries: Array) -> String: + #Dictionary[String, Array[GdUnitReportSummary]] + var path_report_mapping := GdUnitByPathReport.sort_reports_by_path(report_summaries) + var table_records := PackedStringArray() + var paths: Array[String] = [] + paths.append_array(path_report_mapping.keys()) + paths.sort() + for report_at_path in paths: + var reports: Array[GdUnitReportSummary] = path_report_mapping.get(report_at_path) + var report := GdUnitByPathReport.new(report_at_path, reports) + var report_link: String = report.write(report_dir).replace(report_dir, ".") + @warning_ignore("return_value_discarded") + table_records.append(report.create_record(report_link)) + return template.replace(GdUnitHtmlPatterns.TABLE_BY_PATHS, "\n".join(table_records)) + + +func _apply_testsuite_reports(report_dir: String, template: String, test_suite_reports: Array[GdUnitReportSummary]) -> String: + var table_records := PackedStringArray() + for report: GdUnitTestSuiteReport in test_suite_reports: + var report_link: String = _write(report_dir, report).replace(report_dir, ".") + @warning_ignore("return_value_discarded") + table_records.append(GdUnitHtmlPatterns.create_suite_record(report_link, report)) + return template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTSUITES, "\n".join(table_records)) + + +func _write(report_dir :String, report: GdUnitTestSuiteReport) -> String: + var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/suite_report.html") + template = GdUnitHtmlPatterns.build(template, report, "") + + var report_output_path := create_output_path(report_dir, report.path(), report.name()) + var test_report_table := PackedStringArray() + if not report._failure_reports.is_empty(): + @warning_ignore("return_value_discarded") + test_report_table.append(GdUnitHtmlPatterns.create_suite_failure_report(report)) + for test_report: GdUnitTestCaseReport in report._reports: + @warning_ignore("return_value_discarded") + test_report_table.append(GdUnitHtmlPatterns.create_test_failure_report(report_output_path, test_report)) + + template = template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTCASES, "\n".join(test_report_table)) + + var dir := report_output_path.get_base_dir() + if not DirAccess.dir_exists_absolute(dir): + @warning_ignore("return_value_discarded") + DirAccess.make_dir_recursive_absolute(dir) + FileAccess.open(report_output_path, FileAccess.WRITE).store_string(template) + return report_output_path + + +static func create_output_path(report_dir :String, path: String, name: String) -> String: + return "%s/test_suites/%s.%s.html" % [report_dir, path.replace("/", "."), name] diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid new file mode 100644 index 00000000..a4fae913 --- /dev/null +++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReportWriter.gd.uid @@ -0,0 +1 @@ +uid://b41cdqrv775ur diff --git a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd deleted file mode 100644 index 976b91f0..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd +++ /dev/null @@ -1,140 +0,0 @@ -class_name GdUnitReportSummary -extends RefCounted - -const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") - -const CHARACTERS_TO_ENCODE := { - '<' : '<', - '>' : '>' -} - -var _resource_path :String -var _name :String -var _test_count := 0 -var _failure_count := 0 -var _error_count := 0 -var _orphan_count := 0 -var _skipped_count := 0 -var _flaky_count := 0 -var _duration := 0 -var _reports :Array[GdUnitReportSummary] = [] - -func name() -> String: - return _name - - -func name_html_encoded() -> String: - return html_encode(_name) - - -func path() -> String: - return _resource_path.get_base_dir().replace("res://", "") - - -func get_resource_path() -> String: - return _resource_path - - -func suite_count() -> int: - return _reports.size() - - -func suite_executed_count() -> int: - var executed := _reports.size() - for report in _reports: - if report.test_count() == report.skipped_count(): - executed -= 1 - return executed - - -func test_count() -> int: - var count := _test_count - for report in _reports: - count += report.test_count() - return count - - -func test_executed_count() -> int: - return test_count() - skipped_count() - - -func success_count() -> int: - return test_count() - error_count() - failure_count() - flaky_count() - skipped_count() - - -func error_count() -> int: - return _error_count - - -func failure_count() -> int: - return _failure_count - - -func skipped_count() -> int: - return _skipped_count - - -func flaky_count() -> int: - return _flaky_count - - -func orphan_count() -> int: - return _orphan_count - - -func duration() -> int: - return _duration - - -func get_reports() -> Array: - return _reports - - -func add_report(report :GdUnitReportSummary) -> void: - _reports.append(report) - - -func report_state() -> String: - return calculate_state(error_count(), failure_count(), orphan_count(), flaky_count(), skipped_count()) - - -func succes_rate() -> String: - return calculate_succes_rate(test_count(), error_count(), failure_count()) - - -func calculate_state(p_error_count :int, p_failure_count :int, p_orphan_count :int, p_flaky_count: int, p_skipped_count: int) -> String: - if p_error_count > 0: - return "ERROR" - if p_failure_count > 0: - return "FAILED" - if p_flaky_count > 0: - return "FLAKY" - if p_orphan_count > 0: - return "WARNING" - if p_skipped_count > 0: - return "SKIPPED" - return "PASSED" - - -func calculate_succes_rate(p_test_count :int, p_error_count :int, p_failure_count :int) -> String: - if p_failure_count == 0: - return "100%" - var count := p_test_count-p_failure_count-p_error_count - if count < 0: - return "0%" - return "%d" % (( 0 if count < 0 else count) * 100.0 / p_test_count) + "%" - - -func create_summary(_report_dir :String) -> String: - return "" - - -func html_encode(value: String) -> String: - for key: String in CHARACTERS_TO_ENCODE.keys(): - @warning_ignore("unsafe_cast") - value = value.replace(key, CHARACTERS_TO_ENCODE[key] as String) - return value - - -func convert_rtf_to_html(bbcode :String) -> String: - return GdUnitTools.richtext_normalize(bbcode) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid deleted file mode 100644 index 1be2ad00..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bmdjjeedfuqnf diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd deleted file mode 100644 index 5eec799e..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd +++ /dev/null @@ -1,52 +0,0 @@ -class_name GdUnitTestCaseReport -extends GdUnitReportSummary - -var _suite_name :String -var _failure_reports :Array[GdUnitReport] - - -@warning_ignore("shadowed_variable") -func _init(p_resource_path: String, p_suite_name: String, p_test_name: String) -> void: - _resource_path = p_resource_path - _suite_name = p_suite_name - _name = p_test_name - - -func suite_name() -> String: - return _suite_name - - -func failure_report() -> String: - var html_report := "" - for report in get_test_reports(): - html_report += convert_rtf_to_html(str(report)) - return html_report - - -func create_record(_report_dir :String) -> String: - return GdUnitHtmlPatterns.TABLE_RECORD_TESTCASE\ - .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\ - .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\ - .replace(GdUnitHtmlPatterns.TESTCASE_NAME, name())\ - .replace(GdUnitHtmlPatterns.SKIPPED_COUNT, str(skipped_count()))\ - .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\ - .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\ - .replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report()) - - -func add_testcase_reports(reports: Array[GdUnitReport]) -> void: - _failure_reports.append_array(reports) - - -func set_testcase_counters(p_error_count: int, p_failure_count: int, p_orphan_count: int, - p_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void: - _error_count = p_error_count - _failure_count = p_failure_count - _orphan_count = p_orphan_count - _skipped_count = p_is_skipped - _flaky_count = p_is_flaky as int - _duration = p_duration - - -func get_test_reports() -> Array[GdUnitReport]: - return _failure_reports diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid deleted file mode 100644 index 8dab443f..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://coymnbc7qpl3o diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd deleted file mode 100644 index 88677741..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd +++ /dev/null @@ -1,129 +0,0 @@ -class_name GdUnitTestSuiteReport -extends GdUnitReportSummary - -var _time_stamp: int -var _failure_reports: Array[GdUnitReport] = [] - - -func _init(p_resource_path: String, p_name: String, p_test_count: int) -> void: - _resource_path = p_resource_path - _name = p_name - _test_count = p_test_count - _time_stamp = Time.get_unix_time_from_system() as int - - -func create_record(report_link :String) -> String: - return GdUnitHtmlPatterns.build(GdUnitHtmlPatterns.TABLE_RECORD_TESTSUITE, self, report_link) - - -func output_path(report_dir :String) -> String: - return "%s/test_suites/%s.%s.html" % [report_dir, path().replace("/", "."), name()] - - -func failure_report() -> String: - var html_report := "" - for report in _failure_reports: - html_report += convert_rtf_to_html(str(report)) - return html_report - - -func test_suite_failure_report() -> String: - return GdUnitHtmlPatterns.TABLE_REPORT_TESTSUITE\ - .replace(GdUnitHtmlPatterns.REPORT_STATE, report_state().to_lower())\ - .replace(GdUnitHtmlPatterns.REPORT_STATE_LABEL, report_state())\ - .replace(GdUnitHtmlPatterns.ORPHAN_COUNT, str(orphan_count()))\ - .replace(GdUnitHtmlPatterns.DURATION, LocalTime.elapsed(_duration))\ - .replace(GdUnitHtmlPatterns.FAILURE_REPORT, failure_report()) - - -func write(report_dir :String) -> String: - var template := GdUnitHtmlPatterns.load_template("res://addons/gdUnit4/src/reporters/html/template/suite_report.html") - template = GdUnitHtmlPatterns.build(template, self, "") - - var report_output_path := output_path(report_dir) - var test_report_table := PackedStringArray() - if not _failure_reports.is_empty(): - @warning_ignore("return_value_discarded") - test_report_table.append(test_suite_failure_report()) - for test_report: GdUnitTestCaseReport in _reports: - @warning_ignore("return_value_discarded") - test_report_table.append(test_report.create_record(report_output_path)) - - template = template.replace(GdUnitHtmlPatterns.TABLE_BY_TESTCASES, "\n".join(test_report_table)) - - var dir := report_output_path.get_base_dir() - if not DirAccess.dir_exists_absolute(dir): - @warning_ignore("return_value_discarded") - DirAccess.make_dir_recursive_absolute(dir) - FileAccess.open(report_output_path, FileAccess.WRITE).store_string(template) - return report_output_path - - -func set_duration(p_duration :int) -> void: - _duration = p_duration - - -func time_stamp() -> int: - return _time_stamp - - -func duration() -> int: - return _duration - - -func set_skipped(skipped :int) -> void: - _skipped_count += skipped - - -func set_orphans(orphans :int) -> void: - _orphan_count = orphans - - -func set_failed(count :int) -> void: - _failure_count += count - - -func set_reports(failure_reports :Array[GdUnitReport]) -> void: - _failure_reports = failure_reports - - -func add_or_create_test_report(test_report: GdUnitTestCaseReport) -> void: - _reports.append(test_report) - - -func update_testsuite_counters( - p_error_count: int, - p_failure_count: int, - p_orphan_count: int, - p_skipped_count: int, - p_flaky_count: int, - p_duration: int) -> void: - _error_count += p_error_count - _failure_count += p_failure_count - _orphan_count += p_orphan_count - _skipped_count += p_skipped_count - _flaky_count += p_flaky_count - _duration += p_duration - - -func set_testcase_counters(test_name: String, p_error_count: int, p_failure_count: int, p_orphan_count: int, - p_is_skipped: bool, p_is_flaky: bool, p_duration: int) -> void: - if _reports.is_empty(): - return - var test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: - return report.name() == test_name - ).back() - if test_report: - test_report.set_testcase_counters(p_error_count, p_failure_count, p_orphan_count, p_is_skipped, p_is_flaky, p_duration) - - -func add_testcase_reports(test_name: String, reports: Array[GdUnitReport] ) -> void: - if reports.is_empty(): - return - # we lookup to latest matching report because of flaky tests could be retry the tests - # and resultis in multipe report entries with the same name - var test_report:GdUnitTestCaseReport = _reports.filter(func (report: GdUnitTestCaseReport) -> bool: - return report.name() == test_name - ).back() - if test_report: - test_report.add_testcase_reports(reports) diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid deleted file mode 100644 index 0974ce65..00000000 --- a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cvl6b5i5uhnci diff --git a/common/layouts/character_edit/image_picker_line_edit.gd b/addons/gdUnit4/src/reporters/html/template/.gdignore similarity index 100% rename from common/layouts/character_edit/image_picker_line_edit.gd rename to addons/gdUnit4/src/reporters/html/template/.gdignore diff --git a/addons/gdUnit4/src/reporters/html/template/css/logo.png.import b/addons/gdUnit4/src/reporters/html/template/css/logo.png.import deleted file mode 100644 index 95c30a28..00000000 --- a/addons/gdUnit4/src/reporters/html/template/css/logo.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://2n0cbwv2t23g" -path="res://.godot/imported/logo.png-d555ca92b260de08658ae2e1d4572e8c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/gdUnit4/src/reporters/html/template/css/logo.png" -dest_files=["res://.godot/imported/logo.png-d555ca92b260de08658ae2e1d4572e8c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd new file mode 100644 index 00000000..0b9674f3 --- /dev/null +++ b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd @@ -0,0 +1,143 @@ +# This class implements the JUnit XML file format +# based checked https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd +class_name JUnitXmlReportWriter +extends GdUnitReportWriter + +const GdUnitTools := preload("res://addons/gdUnit4/src/core/GdUnitTools.gd") + +const ATTR_CLASSNAME := "classname" +const ATTR_ERRORS := "errors" +const ATTR_FAILURES := "failures" +const ATTR_HOST := "hostname" +const ATTR_ID := "id" +const ATTR_MESSAGE := "message" +const ATTR_NAME := "name" +const ATTR_PACKAGE := "package" +const ATTR_SKIPPED := "skipped" +const ATTR_FLAKY := "flaky" +const ATTR_TESTS := "tests" +const ATTR_TIME := "time" +const ATTR_TIMESTAMP := "timestamp" +const ATTR_TYPE := "type" + +const HEADER := '\n' + + +func output_format() -> String: + return "XML" + + +func write(report_path: String, report: GdUnitReportSummary) -> String: + var result_file: String = "%s/results.xml" % report_path + DirAccess.make_dir_recursive_absolute(report_path) + var file := FileAccess.open(result_file, FileAccess.WRITE) + if file == null: + push_warning("Can't saving the result to '%s'\n Error: %s" % [result_file, error_string(FileAccess.get_open_error())]) + else: + file.store_string(build_junit_report(report_path, report)) + return result_file + + +func build_junit_report(report_path: String, report: GdUnitReportSummary) -> String: + var iso8601_datetime := Time.get_date_string_from_system() + var test_suites := XmlElement.new("testsuites")\ + .attribute(ATTR_ID, iso8601_datetime)\ + .attribute(ATTR_NAME, report_path.get_file())\ + .attribute(ATTR_TESTS, report.test_count())\ + .attribute(ATTR_FAILURES, report.failure_count())\ + .attribute(ATTR_SKIPPED, report.skipped_count())\ + .attribute(ATTR_FLAKY, report.flaky_count())\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(report.duration()))\ + .add_childs(build_test_suites(report)) + var as_string := test_suites.to_xml() + test_suites.dispose() + return HEADER + as_string + + +func build_test_suites(summary: GdUnitReportSummary) -> Array: + var test_suites: Array[XmlElement] = [] + for index in summary.get_reports().size(): + var suite_report :GdUnitTestSuiteReport = summary.get_reports()[index] + var iso8601_datetime := Time.get_datetime_string_from_unix_time(suite_report.time_stamp()) + test_suites.append(XmlElement.new("testsuite")\ + .attribute(ATTR_ID, index)\ + .attribute(ATTR_NAME, suite_report.name())\ + .attribute(ATTR_PACKAGE, suite_report.path())\ + .attribute(ATTR_TIMESTAMP, iso8601_datetime)\ + .attribute(ATTR_HOST, "localhost")\ + .attribute(ATTR_TESTS, suite_report.test_count())\ + .attribute(ATTR_FAILURES, suite_report.failure_count())\ + .attribute(ATTR_ERRORS, suite_report.error_count())\ + .attribute(ATTR_SKIPPED, suite_report.skipped_count())\ + .attribute(ATTR_FLAKY, suite_report.flaky_count())\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(suite_report.duration()))\ + .add_childs(build_test_cases(suite_report))) + return test_suites + + +func build_test_cases(suite_report: GdUnitTestSuiteReport) -> Array: + var test_cases: Array[XmlElement] = [] + for index in suite_report.get_reports().size(): + var report :GdUnitTestCaseReport = suite_report.get_reports()[index] + test_cases.append( XmlElement.new("testcase")\ + .attribute(ATTR_NAME, JUnitXmlReportWriter.encode_xml(report.name()))\ + .attribute(ATTR_CLASSNAME, report.suite_name())\ + .attribute(ATTR_TIME, JUnitXmlReportWriter.to_time(report.duration()))\ + .add_childs(build_reports(report))) + return test_cases + + +func build_reports(test_report: GdUnitTestCaseReport) -> Array: + var failure_reports: Array[XmlElement] = [] + + for report: GdUnitReport in test_report.get_test_reports(): + if report.is_failure(): + failure_reports.append(XmlElement.new("failure")\ + .attribute(ATTR_MESSAGE, "FAILED: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ + .attribute(ATTR_TYPE, JUnitXmlReportWriter.to_type(report.type()))\ + .text(convert_rtf_to_text(report.message()))) + elif report.is_error(): + failure_reports.append(XmlElement.new("error")\ + .attribute(ATTR_MESSAGE, "ERROR: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ + .attribute(ATTR_TYPE, JUnitXmlReportWriter.to_type(report.type()))\ + .text(convert_rtf_to_text(report.message()))) + elif report.is_skipped(): + failure_reports.append(XmlElement.new("skipped")\ + .attribute(ATTR_MESSAGE, "SKIPPED: %s:%d" % [test_report.get_resource_path(), report.line_number()])\ + .text(convert_rtf_to_text(report.message()))) + return failure_reports + + +func convert_rtf_to_text(bbcode: String) -> String: + return GdUnitTools.richtext_normalize(bbcode) + + +static func to_type(type: int) -> String: + match type: + GdUnitReport.SUCCESS: + return "SUCCESS" + GdUnitReport.WARN: + return "WARN" + GdUnitReport.FAILURE: + return "FAILURE" + GdUnitReport.ORPHAN: + return "ORPHAN" + GdUnitReport.TERMINATED: + return "TERMINATED" + GdUnitReport.INTERUPTED: + return "INTERUPTED" + GdUnitReport.ABORT: + return "ABORT" + return "UNKNOWN" + + +static func to_time(duration: int) -> String: + return "%4.03f" % (duration / 1000.0) + + +static func encode_xml(value: String) -> String: + return value.xml_escape(true) + + +#static func to_ISO8601_datetime() -> String: + #return "%04d-%02d-%02dT%02d:%02d:%02d" % [date["year"], date["month"], date["day"], date["hour"], date["minute"], date["second"]] diff --git a/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid new file mode 100644 index 00000000..7985b66e --- /dev/null +++ b/addons/gdUnit4/src/reporters/xml/JUnitXmlReportWriter.gd.uid @@ -0,0 +1 @@ +uid://cutwohfvld8yp diff --git a/addons/gdUnit4/src/reporters/XmlElement.gd b/addons/gdUnit4/src/reporters/xml/XmlElement.gd similarity index 100% rename from addons/gdUnit4/src/reporters/XmlElement.gd rename to addons/gdUnit4/src/reporters/xml/XmlElement.gd diff --git a/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid b/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid new file mode 100644 index 00000000..7d2ae600 --- /dev/null +++ b/addons/gdUnit4/src/reporters/xml/XmlElement.gd.uid @@ -0,0 +1 @@ +uid://bmfm0kuehgl8 diff --git a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd index bee38326..5d8b6318 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd +++ b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd @@ -41,7 +41,7 @@ static func build(to_spy: Variant, debug_write := false) -> Variant: var spy_instance: Object = spy.new() @warning_ignore("unsafe_method_access") # we do not call the original implementation for _ready and all input function, this is actualy done by the engine - spy_instance.__init(to_spy, ["_input", "_gui_input", "_input_event", "_unhandled_input"]) + spy_instance.__init(["_input", "_gui_input", "_input_event", "_unhandled_input"]) @warning_ignore("unsafe_cast") copy_properties(to_spy as Object, spy_instance) @warning_ignore("return_value_discarded") @@ -58,7 +58,7 @@ static func get_class_info(clazz :Variant) -> Dictionary: } -static func spy_on_script(instance :Variant, function_excludes :PackedStringArray, debug_write :bool) -> GDScript: +static func spy_on_script(instance: Variant, function_excludes: PackedStringArray, debug_write: bool) -> GDScript: if GdArrayTools.is_array_type(instance): if GdUnitSettings.is_verbose_assert_errors(): push_error("Can't build spy checked type '%s'! Spy checked Container Built-In Type not supported!" % type_string(typeof(instance))) @@ -70,10 +70,20 @@ static func spy_on_script(instance :Variant, function_excludes :PackedStringArra if GdUnitSettings.is_verbose_assert_errors(): push_error("Can't build spy for class type '%s'! Using an instance instead e.g. 'spy()'" % [clazz_name]) return null + + @warning_ignore("unsafe_method_access") + var spy_template := SPY_TEMPLATE.source_code.format({ + "instance_id" : abs(instance.get_instance_id()), + "gdunit_source_class": clazz_name if clazz_path.is_empty() else clazz_path[0] + }) @warning_ignore("unsafe_cast") - var lines := load_template(SPY_TEMPLATE.source_code, class_info, instance as Object) + var lines := load_template(spy_template, class_info) @warning_ignore("unsafe_cast") lines += double_functions(instance as Object, clazz_name, clazz_path, GdUnitSpyFunctionDoubler.new(), function_excludes) + # We disable warning/errors for inferred_declaration + if Engine.get_version_info().hex >= 0x40400: + lines.insert(0, '@warning_ignore_start("inferred_declaration")') + lines.append('@warning_ignore_restore("inferred_declaration")') var spy := GDScript.new() spy.source_code = "\n".join(lines) @@ -120,7 +130,7 @@ static func spy_on_scene(scene :Node, debug_write :bool) -> Object: scene.set(property_name, original_properties[property_name]) @warning_ignore("unsafe_method_access") - scene.__init(scene, []) + scene.__init() return register_auto_free(scene) diff --git a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid index d8975049..0b32275d 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid +++ b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid @@ -1 +1 @@ -uid://dw53ujxolkr5m +uid://bm563ah2fv8qn diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd deleted file mode 100644 index c71cea7e..00000000 --- a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd +++ /dev/null @@ -1,114 +0,0 @@ -class_name GdUnitSpyFunctionDoubler -extends GdFunctionDoubler - - -const TEMPLATE_RETURN_VARIANT = """ - var args__: Array = ["$(func_name)", $(arguments)] - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - return $(await)super($(arguments)) - return ${default_return_value} - -""" - - -const TEMPLATE_RETURN_VOID = """ - var args__: Array = ["$(func_name)", $(arguments)] - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - if __do_call_real_func("$(func_name)"): - @warning_ignore("unsafe_call_argument") - $(await)super($(arguments)) - -""" - - -const TEMPLATE_RETURN_VOID_VARARG = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return - else: - __verifier.save_function_interaction(args__) - - $(await)__call_func("$(func_name)", [$(arguments)] + varargs__) - -""" - - -const TEMPLATE_RETURN_VARIANT_VARARG = """ - var varargs__: Array = __get_verifier().filter_vargs([$(varargs)]) - var args__: Array = ["$(func_name)", $(arguments)] + varargs__ - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(args__) - return ${default_return_value}$(return_as) - else: - __verifier.save_function_interaction(args__) - - return $(await)__call_func("$(func_name)", [$(arguments)] + varargs__) - -""" - - -const TEMPLATE_CALLABLE_CALL = """ - var used_arguments__ := __get_verifier().filter_vargs([$(arguments)]) - - # verify block - var __verifier := __get_verifier() - if __verifier != null: - if __verifier.is_verify_interactions(): - __verifier.verify_interactions(["call", used_arguments__]) - return ${default_return_value}$(return_as) - else: - var args__ := used_arguments__.duplicate() - args__.append_array(super.get_bound_arguments()) - __verifier.save_function_interaction(["call", args__]) - - if __do_call_real_func("call"): - return _cb.callv(used_arguments__) - - return ${default_return_value} - -""" - - -func _init(push_errors: bool = false) -> void: - super._init(push_errors) - - -func get_template(fd: GdFunctionDescriptor, is_callable: bool) -> String: - if is_callable and fd.name() == "call": - return TEMPLATE_CALLABLE_CALL - if fd.is_vararg(): - return TEMPLATE_RETURN_VOID_VARARG if fd.return_type() == TYPE_NIL else TEMPLATE_RETURN_VARIANT_VARARG - var return_type :Variant = fd.return_type() - if return_type is StringName: - return TEMPLATE_RETURN_VARIANT - return TEMPLATE_RETURN_VOID if (return_type == TYPE_NIL or return_type == GdObjects.TYPE_VOID) else TEMPLATE_RETURN_VARIANT diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid deleted file mode 100644 index d7803326..00000000 --- a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://drnkfb6u0ggpu diff --git a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd index e3d2fe86..e0bcaf2b 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd +++ b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd @@ -1,64 +1,46 @@ class_name DoubledSpyClassSourceClassName -const __INSTANCE_ID = "${instance_id}" -const __SOURCE_CLASS = "${source_class}" +const __INSTANCE_ID := "gdunit_doubler_instance_id_{instance_id}" -class SpyState: - var instance_delegator :Object - var excluded_methods :PackedStringArray = [] +class GdUnitSpyDoublerState: + const __SOURCE_CLASS := "{gdunit_source_class}" + var excluded_methods := PackedStringArray() - func call_func(func_name: String, arguments: Array) -> Variant: - return instance_delegator.callv(func_name, arguments) + func _init(excluded_methods__ := PackedStringArray()) -> void: + excluded_methods = excluded_methods__ +var __spy_state := GdUnitSpyDoublerState.new() @warning_ignore("unused_private_class_variable") var __verifier_instance := GdUnitObjectInteractionsVerifier.new() -var __spying_state := SpyState.new() -func __init(__delegator: Object, __exluded_methods := PackedStringArray()) -> void: - # store self need to access static functions - Engine.set_meta(__INSTANCE_ID, self) - __spying_state.instance_delegator = __delegator - __spying_state.excluded_methods = __exluded_methods - - -func __release_double() -> void: - # we need to release the self reference manually to prevent orphan nodes - Engine.remove_meta(__INSTANCE_ID) - __spying_state.instance_delegator = null - - -func _notification(what: int) -> void: - if what == NOTIFICATION_PREDELETE: - if Engine.has_meta(__INSTANCE_ID): - Engine.remove_meta(__INSTANCE_ID) +func __init(__excluded_methods := PackedStringArray()) -> void: + __init_doubler() + __spy_state.excluded_methods = __excluded_methods -static func __get_verifier() -> GdUnitObjectInteractionsVerifier: - var __instance := __get_instance() - @warning_ignore("unsafe_property_access") - return null if __instance == null else __instance.__verifier_instance +static func __doubler_state() -> GdUnitSpyDoublerState: + if Engine.has_meta(__INSTANCE_ID): + return Engine.get_meta(__INSTANCE_ID).__spy_state + return null -static func __spy_state() -> SpyState: - @warning_ignore("unsafe_property_access") - return __get_instance().__spying_state +func __init_doubler() -> void: + Engine.set_meta(__INSTANCE_ID, self) -static func __get_instance() -> Object: - return null if not Engine.has_meta(__INSTANCE_ID) else Engine.get_meta(__INSTANCE_ID) +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE and Engine.has_meta(__INSTANCE_ID): + Engine.remove_meta(__INSTANCE_ID) -func __instance_id() -> String: - return __INSTANCE_ID +static func __get_verifier() -> GdUnitObjectInteractionsVerifier: + return Engine.get_meta(__INSTANCE_ID).__verifier_instance static func __do_call_real_func(__func_name: String) -> bool: - return not __spy_state().excluded_methods.has(__func_name) - - -static func __call_func(__func_name: String, __arguments: Array) -> Variant: - return __spy_state().call_func(__func_name, __arguments) + @warning_ignore("unsafe_method_access") + return not __doubler_state().excluded_methods.has(__func_name) diff --git a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid index 2ac39b47..d652e61a 100644 --- a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid +++ b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid @@ -1 +1 @@ -uid://cytpqfrmm0pt3 +uid://nljeelpoyj6b diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.gd b/addons/gdUnit4/src/ui/GdUnitConsole.gd index 36a94d43..eb1e6254 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.gd +++ b/addons/gdUnit4/src/ui/GdUnitConsole.gd @@ -71,7 +71,11 @@ func setup_update_notification(control: Button) -> void: func _on_gdunit_event(event: GdUnitEvent) -> void: - _test_reporter.on_gdunit_event(event) + match event.type(): + GdUnitEvent.SESSION_START: + _test_reporter.test_session = GdUnitTestSession.new(GdUnitTestDiscoverGuard.instance().get_discovered_tests(), "") + GdUnitEvent.SESSION_CLOSE: + _test_reporter.test_session = null func _on_gdunit_client_connected(client_id: int) -> void: diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid index 3d53d9db..06a01916 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid @@ -1 +1 @@ -uid://bc2216ij7fvk1 +uid://d0tknhexlql6d diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.tscn b/addons/gdUnit4/src/ui/GdUnitConsole.tscn index 924859eb..c3c7e29f 100644 --- a/addons/gdUnit4/src/ui/GdUnitConsole.tscn +++ b/addons/gdUnit4/src/ui/GdUnitConsole.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://dm0wvfyeew7vd"] -[ext_resource type="Script" uid="uid://b0ab52wtehlss" path="res://addons/gdUnit4/src/ui/GdUnitConsole.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/GdUnitConsole.gd" id="1"] [node name="Control" type="Control"] use_parent_material = true diff --git a/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid b/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid index b5e252dc..89d8ecb9 100644 --- a/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid @@ -1 +1 @@ -uid://dgf16dlm5ms6k +uid://dly8segcatiql diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.gd b/addons/gdUnit4/src/ui/GdUnitInspector.gd index a6d53f9b..81d896f1 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspector.gd +++ b/addons/gdUnit4/src/ui/GdUnitInspector.gd @@ -4,12 +4,16 @@ extends Panel var _command_handler := GdUnitCommandHandler.instance() +var _wait_time := 0.0 func _ready() -> void: @warning_ignore("return_value_discarded") - GdUnitCommandHandler.instance().gdunit_runner_start.connect(func() -> void: - var control :Control = get_parent_control() + GdUnitSignals.instance().gdunit_event.connect(func(event: GdUnitEvent) -> void: + if event.type() != GdUnitEvent.SESSION_START: + return + + var control: Control = get_parent_control() # if the tab is floating we dont need to set as current if control is TabContainer: var tab_container :TabContainer = control @@ -22,10 +26,9 @@ func _ready() -> void: @warning_ignore("unsafe_property_access", "unsafe_method_access") %MainPanel.test_counters_changed.connect(%ProgressBar._on_test_counter_changed) -func _process(_delta: float) -> void: - _command_handler._do_process() - -@warning_ignore("redundant_await") -func _on_status_bar_request_discover_tests() -> void: - await _command_handler.cmd_discover_tests() +func _process(delta: float) -> void: + _wait_time += delta + if _wait_time > 5.0: + _wait_time = 0 + _command_handler._do_process() diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid index 9bc99846..db0a2ad9 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid @@ -1 +1 @@ -uid://u4ddpnglc1kf +uid://bekhj4fw0c6d7 diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.tscn b/addons/gdUnit4/src/ui/GdUnitInspector.tscn index 9b5a55fa..537c7dc0 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspector.tscn +++ b/addons/gdUnit4/src/ui/GdUnitInspector.tscn @@ -1,12 +1,12 @@ [gd_scene load_steps=8 format=3 uid="uid://mpo5o6d4uybu"] -[ext_resource type="PackedScene" uid="uid://dx7xy4dgi3wwb" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn" id="1"] -[ext_resource type="PackedScene" uid="uid://dva3tonxsxrlk" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn" id="2"] -[ext_resource type="PackedScene" uid="uid://c22l4odk7qesc" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn" id="3"] -[ext_resource type="PackedScene" uid="uid://djp8ait0bxpsc" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn" id="4"] -[ext_resource type="Script" uid="uid://crbw6kef7nktv" path="res://addons/gdUnit4/src/ui/GdUnitInspector.gd" id="5"] -[ext_resource type="PackedScene" uid="uid://bqfpidewtpeg0" path="res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn" id="7"] -[ext_resource type="PackedScene" uid="uid://cn5mp3tmi2gb1" path="res://addons/gdUnit4/src/network/GdUnitServer.tscn" id="7_721no"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn" id="1"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn" id="2"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn" id="3"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn" id="4"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/GdUnitInspector.gd" id="5"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn" id="7"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/network/GdUnitServer.tscn" id="7_721no"] [node name="GdUnit" type="Panel"] use_parent_material = true @@ -26,12 +26,14 @@ layout_mode = 0 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_vertical = 11 +theme_override_constants/separation = 0 [node name="Header" type="VBoxContainer" parent="VBoxContainer"] use_parent_material = true clip_contents = true layout_mode = 2 size_flags_horizontal = 9 +size_flags_vertical = 0 [node name="ToolBar" parent="VBoxContainer/Header" instance=ExtResource("1")] layout_mode = 2 @@ -56,12 +58,13 @@ layout_mode = 2 [node name="event_server" parent="." instance=ExtResource("7_721no")] -[connection signal="request_discover_tests" from="VBoxContainer/Header/StatusBar" to="." method="_on_status_bar_request_discover_tests"] -[connection signal="select_error_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [6]] -[connection signal="select_error_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [6]] -[connection signal="select_failure_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [5]] -[connection signal="select_failure_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [5]] -[connection signal="select_flaky_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [4]] -[connection signal="select_flaky_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [4]] +[connection signal="select_error_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [7]] +[connection signal="select_error_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [7]] +[connection signal="select_failure_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [6]] +[connection signal="select_failure_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [6]] +[connection signal="select_flaky_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [5]] +[connection signal="select_flaky_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [5]] +[connection signal="select_skipped_next" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_next_item_by_state" binds= [2]] +[connection signal="select_skipped_prevous" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_select_previous_item_by_state" binds= [2]] [connection signal="tree_view_mode_changed" from="VBoxContainer/Header/StatusBar" to="VBoxContainer/MainPanel" method="_on_status_bar_tree_view_mode_changed"] [connection signal="jump_to_orphan_nodes" from="VBoxContainer/Monitor" to="VBoxContainer/MainPanel" method="select_first_orphan"] diff --git a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd index 01315d83..8dd0265d 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd +++ b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd @@ -21,11 +21,11 @@ enum SORT_MODE { enum STATE { INITIAL, RUNNING, + SKIPPED, SUCCESS, WARNING, FLAKY, FAILED, ERROR, ABORDED, - SKIPPED } diff --git a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid index 9e51e2d6..7eb678a6 100644 --- a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid @@ -1 +1 @@ -uid://s3jesnpl5w4 +uid://c7fnn170r0myh diff --git a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid index 68288f5f..ff5d6882 100644 --- a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid +++ b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid @@ -1 +1 @@ -uid://3w6n0vrjwmo8 +uid://cl5pihru5ravk diff --git a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid index e1923cbc..5ed518d8 100644 --- a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid +++ b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid @@ -1 +1 @@ -uid://li3m7sxmxbog +uid://dgf8b386owp0r diff --git a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd deleted file mode 100644 index 044b9efc..00000000 --- a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd +++ /dev/null @@ -1,79 +0,0 @@ -@tool -extends Control - -var _context_menus := Dictionary() -var _command_handler := GdUnitCommandHandler.instance() - - -func _init() -> void: - set_name("EditorFileSystemContextMenuHandler") - - var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: - if script == null: - return false - return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts - var context_menus :Array[GdUnitContextMenuItem] = [ - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, "Run Testsuites", "Play", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE)), - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, "Debug Testsuites", "PlayStart", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE_DEBUG)), - ] - for menu in context_menus: - _context_menus[menu.id] = menu - var popup := _menu_popup() - var file_tree := _file_tree() - @warning_ignore("return_value_discarded") - popup.about_to_popup.connect(on_context_menu_show.bind(popup, file_tree)) - @warning_ignore("return_value_discarded") - popup.id_pressed.connect(on_context_menu_pressed.bind(file_tree)) - - -func on_context_menu_show(context_menu: PopupMenu, file_tree: Tree) -> void: - context_menu.add_separator() - var current_index := context_menu.get_item_count() - - for menu_id: int in _context_menus.keys(): - var menu_item: GdUnitContextMenuItem = _context_menus[menu_id] - - context_menu.add_item(menu_item.name, menu_id) - #context_menu.set_item_icon_modulate(current_index, Color.MEDIUM_PURPLE) - context_menu.set_item_disabled(current_index, !menu_item.is_enabled(null)) - context_menu.set_item_icon(current_index, GdUnitUiTools.get_icon(menu_item.icon)) - current_index += 1 - - -func on_context_menu_pressed(id: int, file_tree: Tree) -> void: - if !_context_menus.has(id): - return - var menu_item: GdUnitContextMenuItem = _context_menus[id] - var test_suites := collect_testsuites(menu_item, file_tree) - - menu_item.execute([test_suites]) - - -func collect_testsuites(_menu_item: GdUnitContextMenuItem, file_tree: Tree) -> Array[Script]: - var file_system := EditorInterface.get_resource_filesystem() - var selected_item := file_tree.get_selected() - var selected_test_suites: Array[Script] = [] - var suite_scaner := GdUnitTestSuiteScanner.new() - - while selected_item: - var resource_path: String = selected_item.get_metadata(0) - var file_type := file_system.get_file_type(resource_path) - var is_dir := DirAccess.dir_exists_absolute(resource_path) - if is_dir: - selected_test_suites.append_array(suite_scaner.scan_directory(resource_path)) - elif is_dir or file_type == "GDScript" or file_type == "CSharpScript": - # find a performant way to check if the selected item a testsuite - var resource: Script = ResourceLoader.load(resource_path, "Script", ResourceLoader.CACHE_MODE_REUSE) - if _menu_item.is_visible(resource): - @warning_ignore("return_value_discarded") - selected_test_suites.append(resource) - selected_item = file_tree.get_next_selected(selected_item) - return selected_test_suites - - -func _file_tree() -> Tree: - return GdObjects.find_nodes_by_class(EditorInterface.get_file_system_dock(), "Tree", true)[-1] - - -func _menu_popup() -> PopupMenu: - return GdObjects.find_nodes_by_class(EditorInterface.get_file_system_dock(), "PopupMenu")[-1] diff --git a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid deleted file mode 100644 index 67d7dd23..00000000 --- a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://7fd7477usa3w diff --git a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx deleted file mode 100644 index 108450e5..00000000 --- a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandlerV44.gdx +++ /dev/null @@ -1,47 +0,0 @@ -@tool -extends EditorContextMenuPlugin - -var _context_menus := Dictionary() -var _command_handler := GdUnitCommandHandler.instance() - - -func _init() -> void: - var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: - if script == null: - return false - return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts - _context_menus[GdUnitContextMenuItem.MENU_ID.TEST_RUN] = GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, "Run Testsuites", "Play", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE)) - _context_menus[GdUnitContextMenuItem.MENU_ID.TEST_DEBUG] = GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, "Debug Testsuites", "PlayStart", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTSUITE_DEBUG)) - - # setup shortcuts - for menu_item: GdUnitContextMenuItem in _context_menus.values(): - var cb := func call(files: Array) -> void: - menu_item.execute([files]) - add_menu_shortcut(menu_item.shortcut(), cb) - - -func _popup_menu(paths: PackedStringArray) -> void: - var test_suites: Array[Script] = [] - var suite_scaner := GdUnitTestSuiteScanner.new() - - for resource_path in paths: - # directories and test-suites are valid to enable the menu - if DirAccess.dir_exists_absolute(resource_path): - test_suites.append_array(suite_scaner.scan_directory(resource_path)) - continue - - var file_type := resource_path.get_extension() - if file_type == "gd" or file_type == "cs": - var script: Script = ResourceLoader.load(resource_path, "Script", ResourceLoader.CACHE_MODE_REUSE) - if GdUnitTestSuiteScanner.is_test_suite(script): - test_suites.append(script) - - # no direcory or test-suites selected? - if test_suites.is_empty(): - return - - for menu_item: GdUnitContextMenuItem in _context_menus.values(): - @warning_ignore("unused_parameter") - var cb := func call(files: Array) -> void: - menu_item.execute([test_suites]) - add_context_menu_item(menu_item.name, cb, GdUnitUiTools.get_icon(menu_item.icon)) diff --git a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd index 2f89c61e..3849e31d 100644 --- a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd +++ b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd @@ -1,18 +1,11 @@ class_name GdUnitContextMenuItem -enum MENU_ID { - UNDEFINED = 0, - TEST_RUN = 1000, - TEST_DEBUG = 1001, - TEST_RERUN = 1002, - CREATE_TEST = 1010, -} -var id: MENU_ID = MENU_ID.UNDEFINED: +var command_id: String: set(value): - id = value + command_id = value get: - return id + return command_id var name: StringName: set(value): @@ -20,50 +13,33 @@ var name: StringName: get: return name -var command: GdUnitCommand: - set(value): - command = value - get: - return command - var visible: Callable: set(value): visible = value get: return visible -var icon: String: - set(value): - icon = value +var icon: Texture2D: get: - return icon + return GdUnitCommandHandler.instance().command_icon(command_id) -func _init(p_id: MENU_ID, p_name: StringName, p_icon :String, p_is_visible: Callable, p_command: GdUnitCommand) -> void: - assert(p_id != null, "(%s) missing parameter 'MENU_ID'" % p_name) +func _init(p_command_id: String, p_name: StringName, p_is_visible: Callable) -> void: + assert(p_command_id != null and not p_command_id.is_empty(), "(%s) missing command id " % p_command_id) assert(p_is_visible != null, "(%s) missing parameter 'GdUnitCommand'" % p_name) - assert(p_command != null, "(%s) missing parameter 'GdUnitCommand'" % p_name) - self.id = p_id + + self.command_id = p_command_id self.name = p_name - self.icon = p_icon - self.command = p_command self.visible = p_is_visible func shortcut() -> Shortcut: - return GdUnitCommandHandler.instance().get_shortcut(command.shortcut) - - -func is_enabled(script: Script) -> bool: - return command.is_enabled.call(script) + return GdUnitCommandHandler.instance().command_shortcut(command_id) -func is_visible(script: Script) -> bool: - return visible.call(script) +func is_visible(...args: Array) -> bool: + return visible.callv(args) -func execute(arguments:=[]) -> void: - if arguments.is_empty(): - command.runnable.call() - else: - command.runnable.callv(arguments) +func execute(...args: Array) -> void: + GdUnitCommandHandler.instance().command_execute(command_id, args) diff --git a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid index 832ee47e..1bc2d6ba 100644 --- a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid +++ b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid @@ -1 +1 @@ -uid://bsdgpryghi78m +uid://khvx4q2mkntr diff --git a/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd b/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd new file mode 100644 index 00000000..0c92b923 --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd @@ -0,0 +1,37 @@ +@tool +extends EditorContextMenuPlugin + +var _context_menus: Array[GdUnitContextMenuItem] = [] + + +func _init() -> void: + var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: + if script == null: + return false + return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts + _context_menus.append(GdUnitContextMenuItem.new( + GdUnitCommandFileSystemRunTests.ID, + "Run Testsuites", + is_test_suite.bind(true) + )) + _context_menus.append(GdUnitContextMenuItem.new( + GdUnitCommandFileSystemDebugTests.ID, + "Debug Testsuites", + is_test_suite.bind(true) + )) + + # setup shortcuts + for menu_item in _context_menus: + if menu_item.shortcut(): + var cb := func call(...files: Array) -> void: + var paths: Array = files[0] + menu_item.execute.callv(paths) + add_menu_shortcut(menu_item.shortcut(), cb) + + +func _popup_menu(paths: PackedStringArray) -> void: + for menu_item in _context_menus: + if menu_item.shortcut(): + add_context_menu_item_from_shortcut(menu_item.name, menu_item.shortcut(), menu_item.icon) + else: + add_context_menu_item(menu_item.name, menu_item.execute.bindv(paths).unbind(1), menu_item.icon) diff --git a/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd.uid new file mode 100644 index 00000000..7d1e94c6 --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitEditorFileSystemContextMenuHandler.gd.uid @@ -0,0 +1 @@ +uid://y7ocy6dvnb6w diff --git a/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd new file mode 100644 index 00000000..2d6071a4 --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd @@ -0,0 +1,63 @@ +@tool +class_name GdUnitInspectorContextMenu +extends PopupMenu + + +const CONTEXT_MENU_RUN_ID = 0 +const CONTEXT_MENU_DEBUG_ID = 1 +const CONTEXT_MENU_RERUN_UNTIL_ID = 2 +# id 3 is the seperator +const CONTEXT_MENU_COLLAPSE_ALL = 4 +const CONTEXT_MENU_EXPAND_ALL = 5 + + +var command_handler: GdUnitCommandHandler + + +func _ready() -> void: + if not Engine.is_editor_hint(): + return + command_handler = GdUnitCommandHandler.instance() + _setup_item(CONTEXT_MENU_RUN_ID, "Run Tests", GdUnitCommandInspectorRunTests.ID) + _setup_item(CONTEXT_MENU_DEBUG_ID, "Debug Tests", GdUnitCommandInspectorDebugTests.ID) + _setup_item(CONTEXT_MENU_RERUN_UNTIL_ID, "Run Tests Until Fail", GdUnitCommandInspectorRerunTestsUntilFailure.ID) + _setup_item(CONTEXT_MENU_EXPAND_ALL, "Expand All", GdUnitCommandInspectorTreeExpand.ID) + _setup_item(CONTEXT_MENU_COLLAPSE_ALL, "Collapse All", GdUnitCommandInspectorTreeCollapse.ID) + + +func _setup_item(item_id: int, item_name: String, command_id: String) -> void: + set_item_text(item_id, item_name) + set_item_icon(item_id, command_handler.command_icon(command_id)) + set_item_shortcut(item_id, command_handler.command_shortcut(command_id)) + + +func disable_items() -> void: + set_item_disabled(CONTEXT_MENU_RUN_ID, true) + set_item_disabled(CONTEXT_MENU_DEBUG_ID, true) + set_item_disabled(CONTEXT_MENU_RERUN_UNTIL_ID, true) + + +func enable_items() -> void: + set_item_disabled(CONTEXT_MENU_RUN_ID, false) + set_item_disabled(CONTEXT_MENU_DEBUG_ID, false) + set_item_disabled(CONTEXT_MENU_RERUN_UNTIL_ID, false) + + +func _on_tree_item_mouse_selected(mouse_position: Vector2, mouse_button_index: int, source: Tree) -> void: + if mouse_button_index == MOUSE_BUTTON_RIGHT: + position = source.get_screen_position() + mouse_position + popup() + + +func _on_index_pressed(index: int) -> void: + match index: + CONTEXT_MENU_RUN_ID: + command_handler.command_execute(GdUnitCommandInspectorRunTests.ID) + CONTEXT_MENU_DEBUG_ID: + command_handler.command_execute(GdUnitCommandInspectorDebugTests.ID) + CONTEXT_MENU_RERUN_UNTIL_ID: + command_handler.command_execute(GdUnitCommandInspectorRerunTestsUntilFailure.ID) + CONTEXT_MENU_EXPAND_ALL: + command_handler.command_execute(GdUnitCommandInspectorTreeExpand.ID) + CONTEXT_MENU_COLLAPSE_ALL: + command_handler.command_execute(GdUnitCommandInspectorTreeCollapse.ID) diff --git a/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd.uid new file mode 100644 index 00000000..8aca6525 --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd.uid @@ -0,0 +1 @@ +uid://y7kby5mckwiv diff --git a/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.tscn b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.tscn new file mode 100644 index 00000000..5d87a413 --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.tscn @@ -0,0 +1,70 @@ +[gd_scene format=3 uid="uid://bjcc71p2h1trr"] + +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.gd" id="1_0x0vc"] + +[sub_resource type="DPITexture" id="DPITexture_miuuy"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} + +[sub_resource type="DPITexture" id="DPITexture_ern2r"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} + +[sub_resource type="DPITexture" id="DPITexture_qdci2"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} + +[sub_resource type="DPITexture" id="DPITexture_hed0i"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} + +[node name="contextMenu" type="PopupMenu" unique_id=1824556050] +auto_translate_mode = 2 +oversampling_override = 1.0 +size = Vector2i(241, 170) +visible = true +item_count = 6 +item_0/text = "Run Tests" +item_0/icon = SubResource("DPITexture_miuuy") +item_0/id = 0 +item_1/text = "Debug Tests" +item_1/icon = SubResource("DPITexture_ern2r") +item_1/id = 1 +item_2/text = "Run Tests Until Fail" +item_2/icon = SubResource("DPITexture_miuuy") +item_2/id = 2 +item_3/id = 3 +item_3/separator = true +item_4/text = "Collapse All" +item_4/icon = SubResource("DPITexture_qdci2") +item_4/id = 4 +item_5/text = "Expand All" +item_5/icon = SubResource("DPITexture_hed0i") +item_5/id = 5 +script = ExtResource("1_0x0vc") + +[connection signal="index_pressed" from="." to="." method="_on_index_pressed"] diff --git a/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd b/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd new file mode 100644 index 00000000..26f5612e --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd @@ -0,0 +1,41 @@ +@tool +extends EditorContextMenuPlugin + + +var _context_menus: Array[GdUnitContextMenuItem] = [] + + +func _init() -> void: + var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: + return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts + _context_menus.append(GdUnitContextMenuItem.new( + GdUnitCommandScriptEditorRunTests.ID, + "Run Tests", + is_test_suite.bind(true) + )) + _context_menus.append(GdUnitContextMenuItem.new( + GdUnitCommandScriptEditorDebugTests.ID, + "Debug Tests", + is_test_suite.bind(true) + )) + _context_menus.append(GdUnitContextMenuItem.new( + GdUnitCommandScriptEditorCreateTest.ID, + "Create Test", + is_test_suite.bind(false) + )) + + # setup shortcuts + for menu_item in _context_menus: + if menu_item.shortcut(): + add_menu_shortcut(menu_item.shortcut(), menu_item.execute.unbind(1)) + + +func _popup_menu(_paths: PackedStringArray) -> void: + var current_script := EditorInterface.get_script_editor().get_current_script() + + for menu_item in _context_menus: + if menu_item.is_visible(current_script): + if menu_item.shortcut(): + add_context_menu_item_from_shortcut(menu_item.name, menu_item.shortcut()) + else: + add_context_menu_item(menu_item.name, menu_item.execute.unbind(1)) diff --git a/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd.uid new file mode 100644 index 00000000..1b6c1bba --- /dev/null +++ b/addons/gdUnit4/src/ui/menu/GdUnitScriptEditorContextMenuHandler.gd.uid @@ -0,0 +1 @@ +uid://djxtfj12n7cav diff --git a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd deleted file mode 100644 index 550c6d61..00000000 --- a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd +++ /dev/null @@ -1,81 +0,0 @@ -@tool -extends Control - -var _context_menus := Dictionary() -var _editor: ScriptEditor -var _command_handler := GdUnitCommandHandler.instance() - - -func _init() -> void: - set_name("ScriptEditorContextMenuHandler") - - var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: - return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts - var context_menus :Array[GdUnitContextMenuItem] = [ - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, "Run Tests", "Play", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE)), - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, "Debug Tests", "PlayStart", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE_DEBUG)), - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.CREATE_TEST, "Create Test", "New", is_test_suite.bind(false), _command_handler.command(GdUnitCommandHandler.CMD_CREATE_TESTCASE)) - ] - for menu in context_menus: - _context_menus[menu.id] = menu - _editor = EditorInterface.get_script_editor() - @warning_ignore("return_value_discarded") - _editor.editor_script_changed.connect(on_script_changed) - on_script_changed(active_script()) - - -func _input(event: InputEvent) -> void: - if event is InputEventKey and event.is_pressed(): - for action: GdUnitContextMenuItem in _context_menus.values(): - if action.shortcut().matches_event(event) and action.is_visible(active_script()): - #if not has_editor_focus(): - # return - action.execute() - accept_event() - return - - -func has_editor_focus() -> bool: - return (Engine.get_main_loop() as SceneTree).root.gui_get_focus_owner() == active_base_editor() - - -func on_script_changed(script: Script) -> void: - if script is Script: - var popups: Array[Node] = GdObjects.find_nodes_by_class(active_editor(), "PopupMenu", true) - for popup: PopupMenu in popups: - if not popup.about_to_popup.is_connected(on_context_menu_show): - popup.about_to_popup.connect(on_context_menu_show.bind(script, popup)) - if not popup.id_pressed.is_connected(on_context_menu_pressed): - popup.id_pressed.connect(on_context_menu_pressed) - - -func on_context_menu_show(script: Script, context_menu: PopupMenu) -> void: - #prints("on_context_menu_show", _context_menus.keys(), context_menu, self) - context_menu.add_separator() - var current_index := context_menu.get_item_count() - for menu_id: int in _context_menus.keys(): - var menu_item: GdUnitContextMenuItem = _context_menus[menu_id] - if menu_item.is_visible(script): - context_menu.add_item(menu_item.name, menu_id) - context_menu.set_item_disabled(current_index, !menu_item.is_enabled(script)) - context_menu.set_item_shortcut(current_index, menu_item.shortcut(), true) - current_index += 1 - - -func on_context_menu_pressed(id: int) -> void: - if !_context_menus.has(id): - return - var menu_item: GdUnitContextMenuItem = _context_menus[id] - menu_item.execute() - - -func active_editor() -> ScriptEditorBase: - return _editor.get_current_editor() - - -func active_base_editor() -> TextEdit: - return active_editor().get_base_editor() - - -func active_script() -> Script: - return _editor.get_current_script() diff --git a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid deleted file mode 100644 index 7ba54248..00000000 --- a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dix5adosojsc diff --git a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx deleted file mode 100644 index a879e378..00000000 --- a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandlerV44.gdx +++ /dev/null @@ -1,33 +0,0 @@ -@tool -extends EditorContextMenuPlugin - -var _context_menus := Dictionary() -var _editor: ScriptEditor -var _command_handler := GdUnitCommandHandler.instance() - - -func _init() -> void: - var is_test_suite := func is_visible(script: Script, is_ts: bool) -> bool: - return GdUnitTestSuiteScanner.is_test_suite(script) == is_ts - var context_menus :Array[GdUnitContextMenuItem] = [ - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_RUN, "Run Tests", "Play", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE)), - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.TEST_DEBUG, "Debug Tests", "PlayStart", is_test_suite.bind(true), _command_handler.command(GdUnitCommandHandler.CMD_RUN_TESTCASE_DEBUG)), - GdUnitContextMenuItem.new(GdUnitContextMenuItem.MENU_ID.CREATE_TEST, "Create Test", "New", is_test_suite.bind(false), _command_handler.command(GdUnitCommandHandler.CMD_CREATE_TESTCASE)) - ] - for menu in context_menus: - _context_menus[menu.id] = menu - _editor = EditorInterface.get_script_editor() - @warning_ignore("return_value_discarded") - - -func _popup_menu(paths: PackedStringArray) -> void: - var script_path := paths[0] - var script: Script = ResourceLoader.load(script_path, "Script", ResourceLoader.CACHE_MODE_REUSE) - - for menu_id: int in _context_menus.keys(): - var menu_item: GdUnitContextMenuItem = _context_menus[menu_id] - if menu_item.is_visible(script): - add_context_menu_item(menu_item.name, - func call(files: Array) -> void: - menu_item.execute([script_path]), - GdUnitUiTools.get_icon(menu_item.icon)) diff --git a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid index 908d8e01..4ab25cdd 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid @@ -1 +1 @@ -uid://crw1egthht6dn +uid://gs4jy5c3ncet diff --git a/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn b/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn index d627bf28..0262b8cd 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://djp8ait0bxpsc"] -[ext_resource type="Script" uid="uid://dwc45di05dw2s" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd" id="3"] [sub_resource type="Image" id="Image_sx31i"] data = { diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd index 0404bd2c..d368ed3f 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd @@ -5,6 +5,7 @@ extends ProgressBar @onready var status: Label = $Label @onready var style: StyleBoxFlat = get("theme_override_styles/fill") +var _state: GdUnitInspectorTreeConstants.STATE func _ready() -> void: style.bg_color = Color.DARK_GREEN @@ -12,6 +13,7 @@ func _ready() -> void: max_value = 0 update_text() + func update_text() -> void: status.text = "%d:%d" % [value, max_value] @@ -19,14 +21,21 @@ func update_text() -> void: func _on_test_counter_changed(index: int, total: int, state: GdUnitInspectorTreeConstants.STATE) -> void: value = index max_value = total + update_text() + # inital state if index == 0: style.bg_color = Color.DARK_GREEN + + # do only update the state is higher prio than current state + if state <= _state: + return + _state = state + if is_flaky(state): style.bg_color = Color.WEB_GREEN if is_failed(state): style.bg_color = Color.DARK_RED - update_text() func is_failed(state: GdUnitInspectorTreeConstants.STATE) -> bool: diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid index ed4e4d1d..14de1b2a 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid @@ -1 +1 @@ -uid://b5sffcmr4tagj +uid://c64pjf1jr50br diff --git a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn index c21b3504..1824230a 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://dva3tonxsxrlk"] -[ext_resource type="Script" uid="uid://clrglsyemovs2" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd" id="1"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ayfir"] bg_color = Color(0, 0.392157, 0, 1) diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd index ee6b3d9b..18634595 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd @@ -7,7 +7,8 @@ signal select_error_next() signal select_error_prevous() signal select_flaky_next() signal select_flaky_prevous() -signal request_discover_tests() +signal select_skipped_next() +signal select_skipped_prevous() @warning_ignore("unused_signal") signal tree_view_mode_changed(flat :bool) @@ -15,19 +16,22 @@ signal tree_view_mode_changed(flat :bool) @onready var _errors: Label = %error_value @onready var _failures: Label = %failure_value @onready var _flaky_value: Label = %flaky_value -@onready var _button_failure_up: Button = %btn_failure_up -@onready var _button_failure_down: Button = %btn_failure_down +@onready var _skipped_value: Label = %skipped_value +#@onready var _button_failure_up: Button = %btn_failure_up +#@onready var _button_failure_down: Button = %btn_failure_down @onready var _button_sync: Button = %btn_tree_sync -@onready var _button_view_mode: Button = %btn_tree_mode -@onready var _button_sort_mode: Button = %btn_tree_sort +@onready var _button_view_mode: MenuButton = %btn_tree_mode +@onready var _button_sort_mode: MenuButton = %btn_tree_sort @onready var _icon_errors: TextureRect = %icon_errors @onready var _icon_failures: TextureRect = %icon_failures @onready var _icon_flaky: TextureRect = %icon_flaky +@onready var _icon_skipped: TextureRect = %icon_skipped var total_failed := 0 var total_errors := 0 var total_flaky := 0 +var total_skipped := 0 var icon_mappings := { @@ -46,21 +50,20 @@ var icon_mappings := { func _ready() -> void: _failures.text = "0" _errors.text = "0" + _flaky_value.text = "0" + _skipped_value.text = "0" _icon_failures.texture = GdUnitUiTools.get_icon("StatusError", Color.SKY_BLUE) _icon_errors.texture = GdUnitUiTools.get_icon("StatusError", Color.DARK_RED) _icon_flaky.texture = GdUnitUiTools.get_icon("CheckBox", Color.GREEN_YELLOW) + _icon_skipped.texture = GdUnitUiTools.get_icon("CheckBox", Color.WEB_GRAY) - _button_failure_up.icon = GdUnitUiTools.get_icon("ArrowUp") - _button_failure_down.icon = GdUnitUiTools.get_icon("ArrowDown") + #_button_failure_up.icon = GdUnitUiTools.get_icon("ArrowUp") + #_button_failure_down.icon = GdUnitUiTools.get_icon("ArrowDown") _button_sync.icon = GdUnitUiTools.get_icon("Loop") _set_sort_mode_menu_options() _set_view_mode_menu_options() GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event) GdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed) - var command_handler := GdUnitCommandHandler.instance() - command_handler.gdunit_runner_start.connect(_on_gdunit_runner_start) - command_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop) - func _set_sort_mode_menu_options() -> void: @@ -105,13 +108,15 @@ func normalise(value: String) -> String: return " ".join(parts) -func status_changed(errors: int, failed: int, flaky: int) -> void: +func status_changed(errors: int, failed: int, flaky: int, skipped: int) -> void: total_failed += failed total_errors += errors total_flaky += flaky + total_skipped += skipped _failures.text = str(total_failed) _errors.text = str(total_errors) _flaky_value.text = str(total_flaky) + _skipped_value.text = str(total_skipped) func disable_buttons(value :bool) -> void: @@ -129,16 +134,23 @@ func _on_gdunit_event(event: GdUnitEvent) -> void: disable_buttons(false) GdUnitEvent.INIT: - total_failed = 0 total_errors = 0 + total_failed = 0 total_flaky = 0 - status_changed(0, 0, 0) + total_skipped = 0 + status_changed(total_errors, total_failed, total_flaky, total_skipped) GdUnitEvent.TESTCASE_AFTER: - status_changed(event.error_count(), event.failed_count(), event.is_flaky()) + status_changed(event.error_count(), event.failed_count(), event.is_flaky(), event.is_skipped()) + + GdUnitEvent.TESTSUITE_AFTER: + status_changed(event.error_count(), event.failed_count(), event.is_flaky(), 0) - #GdUnitEvent.TESTSUITE_AFTER: - # status_changed(event.error_count(), event.failed_count(), event.is_flaky()) + GdUnitEvent.SESSION_START: + disable_buttons(true) + + GdUnitEvent.SESSION_CLOSE: + disable_buttons(false) func _on_btn_error_up_pressed() -> void: @@ -165,8 +177,16 @@ func _on_btn_flaky_down_pressed() -> void: select_flaky_next.emit() -func _on_tree_sync_pressed() -> void: - request_discover_tests.emit() +func _on_btn_skipped_up_pressed() -> void: + select_skipped_prevous.emit() + + +func _on_btn_skipped_down_pressed() -> void: + select_skipped_next.emit() + + +func _on_btn_tree_sync_pressed() -> void: + await GdUnitTestDiscoverer.run() func _on_sort_mode_changed(index: int) -> void: @@ -182,14 +202,6 @@ func _on_tree_view_mode_changed(index: int) ->void: ################################################################################ # external signal receiver ################################################################################ -func _on_gdunit_runner_start() -> void: - disable_buttons(true) - - -func _on_gdunit_runner_stop(_client_id: int) -> void: - disable_buttons(false) - - func _on_settings_changed(property :GdUnitProperty) -> void: if property.name() == GdUnitSettings.INSPECTOR_TREE_SORT_MODE: _set_sort_mode_menu_options() diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid index 5580160e..93388985 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid @@ -1 +1 @@ -uid://dvrxigojhylsy +uid://cbpkkmejfdflg diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn index 182338b8..d4ed816f 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn @@ -1,116 +1,104 @@ -[gd_scene load_steps=32 format=3 uid="uid://c22l4odk7qesc"] +[gd_scene load_steps=26 format=3 uid="uid://c22l4odk7qesc"] -[ext_resource type="Script" uid="uid://kv33is2tf6np" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd" id="3"] -[sub_resource type="Image" id="Image_mb3ih"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 160, 230, 230, 230, 10, 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, 224, 224, 224, 255, 224, 224, 224, 213, 225, 225, 225, 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, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 75, 224, 224, 224, 188, 224, 224, 224, 238, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 224, 224, 224, 96, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 245, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 77, 224, 224, 224, 255, 224, 224, 224, 253, 225, 225, 225, 117, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 212, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 129, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 224, 224, 224, 113, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 159, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 242, 224, 224, 224, 255, 224, 224, 224, 24, 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, 225, 225, 225, 25, 224, 224, 224, 255, 224, 224, 224, 238, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 243, 224, 224, 224, 254, 233, 233, 233, 23, 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, 229, 229, 229, 29, 224, 224, 224, 255, 224, 224, 224, 236, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 189, 224, 224, 224, 255, 225, 225, 225, 68, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 160, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 121, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 72, 224, 224, 224, 121, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 43, 224, 224, 224, 213, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 225, 225, 225, 124, 224, 224, 224, 254, 224, 224, 224, 255, 226, 226, 226, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 125, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 95, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 224, 224, 224, 185, 226, 226, 226, 70, 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, 225, 225, 225, 42, 224, 224, 224, 213, 224, 224, 224, 255, 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, 230, 230, 230, 10, 225, 225, 225, 159, 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, 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="DPITexture" id="DPITexture_mb3ih"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_jvn24"] -image = SubResource("Image_mb3ih") - -[sub_resource type="Image" id="Image_wo03e"] -data = { -"data": PackedByteArray(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, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 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, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 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, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 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, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 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, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 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, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_wo03e"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_k82x4"] -image = SubResource("Image_wo03e") - -[sub_resource type="Image" id="Image_ixycx"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 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, 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="DPITexture" id="DPITexture_ixycx"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_bs7qq"] -image = SubResource("Image_ixycx") - [sub_resource type="Image" id="Image_c80wp"] data = { -"data": PackedByteArray(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, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 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, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 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, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 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, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 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, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 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, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 196, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 232, 232, 232, 22, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 194, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 176, 224, 224, 224, 200, 224, 224, 224, 253, 224, 224, 224, 255, 225, 225, 225, 199, 224, 224, 224, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 252, 224, 224, 224, 255, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 171, 224, 224, 224, 195, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 195, 225, 225, 225, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 233, 233, 23, 224, 224, 224, 198, 224, 224, 224, 201, 224, 224, 224, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_0ck6a"] +[sub_resource type="ImageTexture" id="ImageTexture_eis20"] image = SubResource("Image_c80wp") -[sub_resource type="Image" id="Image_eis20"] -data = { -"data": PackedByteArray(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, 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, 255, 255, 255, 0, 255, 255, 255, 3, 224, 224, 224, 105, 224, 224, 224, 192, 224, 224, 224, 244, 224, 224, 224, 238, 224, 224, 224, 197, 224, 224, 224, 105, 255, 255, 255, 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, 233, 233, 233, 23, 225, 225, 225, 207, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 226, 226, 226, 26, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 224, 224, 224, 205, 224, 224, 224, 255, 224, 224, 224, 218, 225, 225, 225, 83, 237, 237, 237, 14, 237, 237, 237, 14, 224, 224, 224, 82, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 102, 224, 224, 224, 255, 224, 224, 224, 218, 227, 227, 227, 18, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 16, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 198, 224, 224, 224, 255, 225, 225, 225, 84, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 86, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 4, 224, 224, 224, 238, 224, 224, 224, 255, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 229, 229, 229, 19, 224, 224, 224, 255, 224, 224, 224, 233, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 160, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 159, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 237, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 10, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 90, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 245, 224, 224, 224, 245, 225, 225, 225, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 232, 232, 232, 22, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 98, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 96, 226, 226, 226, 95, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 20, 224, 224, 224, 88, 224, 224, 224, 221, 224, 224, 224, 255, 225, 225, 225, 199, 255, 255, 255, 2, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 227, 227, 227, 18, 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, 224, 224, 224, 236, 224, 224, 224, 195, 224, 224, 224, 96, 255, 255, 255, 5, 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, 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="DPITexture" id="DPITexture_t2qd7"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_t7ac1"] -image = SubResource("Image_eis20") - -[sub_resource type="Image" id="Image_t2qd7"] +[sub_resource type="Image" id="Image_jh28t"] data = { -"data": PackedByteArray(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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_03vfp"] -image = SubResource("Image_t2qd7") +[sub_resource type="ImageTexture" id="ImageTexture_1mh1t"] +image = SubResource("Image_jh28t") -[sub_resource type="Image" id="Image_jh28t"] +[sub_resource type="Image" id="Image_lpjla"] data = { -"data": PackedByteArray(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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 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, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 255, 255, 255, 0, 255, 255, 255, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 237, 247, 245, 248, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 237, 247, 245, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_fv3i4"] -image = SubResource("Image_jh28t") +[sub_resource type="ImageTexture" id="ImageTexture_bq8kn"] +image = SubResource("Image_lpjla") -[sub_resource type="Image" id="Image_1mh1t"] +[sub_resource type="Image" id="Image_bwbka"] data = { -"data": PackedByteArray(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, 249, 249, 255, 230, 246, 246, 252, 230, 249, 249, 255, 230, 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, 246, 246, 252, 237, 246, 246, 252, 255, 246, 246, 252, 248, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 252, 236, 246, 246, 252, 254, 246, 246, 252, 247, 255, 255, 255, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 246, 246, 253, 231, 246, 246, 253, 232, 246, 246, 252, 230, 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, 246, 246, 252, 243, 246, 246, 252, 255, 246, 246, 252, 242, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 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, 246, 246, 252, 242, 246, 246, 252, 253, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 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, 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, 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, 255, 255, 255, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 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, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 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, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 249, 255, 230, 246, 246, 252, 230, 249, 249, 255, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 237, 246, 246, 252, 255, 246, 246, 252, 248, 0, 0, 0, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 236, 246, 246, 252, 254, 246, 246, 252, 247, 0, 0, 0, 0, 246, 246, 252, 254, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 253, 231, 246, 246, 253, 232, 246, 246, 252, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 243, 246, 246, 252, 255, 246, 246, 252, 242, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 242, 246, 246, 252, 253, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 246, 246, 252, 244, 246, 246, 252, 255, 246, 246, 252, 241, 246, 246, 252, 230, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 246, 246, 252, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_ab51p"] -image = SubResource("Image_1mh1t") +[sub_resource type="ImageTexture" id="ImageTexture_8lbfl"] +image = SubResource("Image_bwbka") -[sub_resource type="Image" id="Image_lpjla"] +[sub_resource type="Image" id="Image_ki3oo"] data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 122, 111, 23, 255, 121, 107, 126, 255, 120, 108, 206, 255, 120, 107, 240, 255, 120, 107, 240, 255, 120, 108, 206, 255, 121, 107, 124, 255, 128, 116, 22, 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, 121, 108, 80, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 121, 107, 239, 255, 123, 109, 77, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 108, 78, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 240, 255, 120, 107, 255, 255, 120, 107, 254, 255, 122, 109, 75, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 121, 107, 239, 255, 120, 107, 255, 255, 122, 107, 107, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 121, 108, 40, 255, 121, 107, 112, 255, 120, 107, 255, 255, 120, 107, 238, 255, 128, 115, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 124, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 120, 107, 233, 255, 120, 107, 232, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 119, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 121, 109, 42, 255, 121, 109, 42, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 202, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 232, 255, 121, 109, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 122, 110, 44, 255, 120, 107, 234, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 108, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 207, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 231, 255, 124, 112, 41, 255, 255, 255, 0, 255, 125, 108, 45, 255, 122, 110, 44, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 233, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 200, 255, 255, 255, 0, 255, 255, 255, 0, 255, 120, 108, 123, 255, 120, 107, 255, 255, 120, 107, 240, 255, 121, 108, 40, 255, 255, 255, 0, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 234, 255, 125, 107, 43, 255, 255, 255, 0, 255, 125, 107, 43, 255, 120, 107, 242, 255, 120, 107, 255, 255, 121, 108, 116, 255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 116, 22, 255, 120, 107, 238, 255, 120, 107, 255, 255, 121, 107, 112, 255, 125, 108, 45, 255, 120, 107, 235, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 233, 255, 125, 107, 43, 255, 120, 107, 117, 255, 120, 107, 255, 255, 120, 107, 235, 255, 128, 113, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 121, 107, 76, 255, 120, 107, 254, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 242, 255, 120, 107, 255, 255, 120, 107, 253, 255, 120, 109, 70, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 123, 109, 77, 255, 121, 107, 239, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 255, 255, 120, 107, 236, 255, 122, 108, 71, 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, 121, 109, 21, 255, 121, 107, 122, 255, 121, 107, 203, 255, 120, 107, 238, 255, 120, 107, 238, 255, 120, 107, 202, 255, 120, 107, 119, 255, 128, 115, 20, 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, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 232, 151, 12, 11, 242, 151, 12, 11, 250, 151, 12, 11, 254, 151, 12, 11, 254, 151, 12, 11, 250, 151, 12, 11, 242, 151, 12, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 238, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 10, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 232, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 240, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 151, 12, 11, 241, 151, 12, 11, 255, 151, 12, 11, 253, 151, 11, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 242, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 241, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 250, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 250, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 10, 234, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 250, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 10, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 250, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 242, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 234, 0, 0, 0, 0, 151, 12, 11, 234, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 10, 241, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 10, 232, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 241, 151, 12, 11, 234, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 234, 151, 12, 11, 241, 151, 12, 11, 255, 151, 12, 11, 253, 151, 11, 10, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 254, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 237, 151, 12, 11, 253, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 255, 151, 12, 11, 253, 151, 12, 11, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 12, 11, 232, 151, 12, 11, 242, 151, 12, 11, 250, 151, 12, 11, 253, 151, 12, 11, 253, 151, 12, 11, 250, 151, 12, 11, 241, 151, 11, 10, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_2rpr0"] -image = SubResource("Image_lpjla") +[sub_resource type="ImageTexture" id="ImageTexture_ivm1h"] +image = SubResource("Image_ki3oo") -[sub_resource type="Image" id="Image_bq8kn"] +[sub_resource type="Image" id="Image_uqb0l"] data = { "data": PackedByteArray(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, 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, 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, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 180, 224, 224, 224, 180, 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, 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, 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, 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", @@ -120,9 +108,9 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_1oriu"] -image = SubResource("Image_bq8kn") +image = SubResource("Image_uqb0l") -[sub_resource type="Image" id="Image_bwbka"] +[sub_resource type="Image" id="Image_j00vj"] data = { "data": PackedByteArray(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, 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, 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, 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, 224, 224, 224, 181, 224, 224, 224, 180, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 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, 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, 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", @@ -132,55 +120,43 @@ data = { } [sub_resource type="ImageTexture" id="ImageTexture_ikyhk"] -image = SubResource("Image_bwbka") +image = SubResource("Image_j00vj") -[sub_resource type="Image" id="Image_8lbfl"] +[sub_resource type="Image" id="Image_0oden"] data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 198, 222, 242, 147, 197, 222, 250, 147, 197, 222, 254, 147, 197, 222, 254, 147, 197, 222, 250, 147, 198, 222, 242, 147, 198, 223, 232, 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, 147, 198, 222, 238, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 253, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 198, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 198, 222, 253, 147, 197, 222, 255, 147, 198, 222, 240, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 198, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 232, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 198, 223, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 255, 255, 255, 0, 255, 255, 255, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 198, 222, 234, 255, 255, 255, 0, 147, 198, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 198, 222, 241, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 223, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 198, 222, 241, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 223, 231, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 147, 198, 222, 237, 147, 198, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 237, 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, 147, 198, 222, 232, 147, 198, 222, 242, 147, 198, 222, 250, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 250, 147, 197, 222, 241, 147, 198, 223, 232, 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, 255, 255, 255, 0, 255, 255, 255, 0), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 223, 232, 147, 197, 222, 242, 147, 197, 222, 250, 147, 197, 222, 254, 147, 197, 222, 254, 147, 197, 222, 250, 147, 197, 222, 242, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 238, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 240, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 0, 0, 0, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 147, 198, 222, 234, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 198, 222, 234, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 250, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 250, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 242, 147, 197, 222, 255, 147, 197, 222, 254, 147, 198, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 234, 0, 0, 0, 0, 147, 197, 222, 234, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 241, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 232, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 241, 147, 197, 222, 234, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 234, 147, 197, 222, 241, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 221, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 237, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 254, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 197, 222, 237, 147, 197, 222, 253, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 255, 147, 197, 222, 253, 147, 197, 222, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 198, 222, 232, 147, 197, 222, 242, 147, 197, 222, 250, 147, 197, 222, 253, 147, 197, 222, 253, 147, 197, 222, 250, 147, 197, 222, 241, 147, 197, 222, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_i2d73"] -image = SubResource("Image_8lbfl") +[sub_resource type="ImageTexture" id="ImageTexture_suo5c"] +image = SubResource("Image_0oden") -[sub_resource type="Image" id="Image_ki3oo"] +[sub_resource type="Image" id="Image_ipq44"] data = { -"data": PackedByteArray(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, 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, 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, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 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, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 178, 224, 224, 224, 194, 230, 230, 230, 20, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 194, 224, 224, 224, 179, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 180, 224, 224, 224, 180, 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, 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, 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, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 237, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 247, 171, 255, 57, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 58, 232, 170, 254, 57, 232, 0, 0, 0, 0, 170, 253, 58, 234, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 170, 254, 58, 232, 170, 253, 57, 251, 170, 253, 57, 251, 170, 254, 58, 236, 170, 253, 57, 253, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 170, 254, 58, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 251, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 170, 254, 58, 242, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 251, 170, 253, 57, 255, 170, 254, 57, 247, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 232, 170, 253, 57, 244, 171, 255, 58, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 255, 170, 253, 57, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 254, 57, 237, 170, 253, 57, 252, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 255, 170, 253, 57, 252, 170, 254, 57, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_mph2m"] -image = SubResource("Image_ki3oo") +[sub_resource type="ImageTexture" id="ImageTexture_d5kq4"] +image = SubResource("Image_ipq44") -[sub_resource type="Image" id="Image_ivm1h"] +[sub_resource type="Image" id="Image_8d0da"] data = { -"data": PackedByteArray(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, 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, 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, 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, 224, 224, 224, 181, 224, 224, 224, 180, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 180, 224, 224, 224, 195, 231, 231, 231, 21, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 195, 224, 224, 224, 178, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 195, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 21, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 231, 231, 231, 21, 224, 224, 224, 255, 224, 224, 224, 255, 230, 230, 230, 20, 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, 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, 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), +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 237, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 247, 130, 141, 130, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 131, 232, 129, 140, 130, 232, 0, 0, 0, 0, 129, 139, 131, 234, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 130, 140, 131, 232, 129, 139, 130, 251, 129, 139, 130, 251, 129, 139, 130, 236, 129, 139, 130, 253, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 129, 139, 130, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 129, 140, 130, 232, 129, 139, 130, 251, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 129, 139, 130, 242, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 130, 232, 129, 139, 130, 251, 129, 139, 130, 255, 129, 139, 130, 247, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 140, 130, 232, 129, 139, 130, 244, 131, 141, 131, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 255, 129, 139, 130, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 139, 130, 237, 129, 139, 130, 252, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 255, 129, 139, 130, 252, 129, 139, 130, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_k6fqi"] -image = SubResource("Image_ivm1h") - -[sub_resource type="Image" id="Image_uqb0l"] -data = { -"data": PackedByteArray(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, 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, 144, 239, 151, 76, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 239, 152, 229, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 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, 142, 244, 153, 45, 143, 239, 152, 175, 149, 255, 170, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 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, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 130, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 144, 244, 155, 23, 151, 244, 151, 22, 255, 255, 255, 0, 142, 244, 153, 45, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 144, 244, 155, 23, 143, 239, 151, 213, 142, 239, 152, 212, 145, 240, 152, 67, 142, 239, 151, 235, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 151, 244, 151, 22, 142, 239, 152, 212, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 142, 240, 152, 128, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 151, 211, 142, 239, 151, 255, 143, 240, 151, 177, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 146, 243, 158, 21, 143, 239, 152, 141, 153, 255, 153, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 255, 142, 239, 151, 255, 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, 142, 239, 151, 255, 142, 239, 151, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 142, 239, 151, 228, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 143, 241, 154, 73, 142, 239, 151, 226, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 239, 151, 255, 142, 240, 151, 225, 142, 241, 153, 70, 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_04e57"] -image = SubResource("Image_uqb0l") +[sub_resource type="ImageTexture" id="ImageTexture_qagbu"] +image = SubResource("Image_8d0da") [node name="StatusBar" type="PanelContainer"] clip_contents = true @@ -195,6 +171,7 @@ script = ExtResource("3") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 +size_flags_vertical = 0 [node name="tree_tools" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 @@ -203,7 +180,7 @@ size_flags_vertical = 0 [node name="Label" type="Label" parent="VBoxContainer/tree_tools"] layout_mode = 2 size_flags_horizontal = 0 -text = "Statisitics" +text = "Statistics" [node name="tree_buttons" type="HBoxContainer" parent="VBoxContainer/tree_tools"] layout_mode = 2 @@ -218,212 +195,271 @@ layout_mode = 2 unique_name_in_owner = true layout_mode = 2 tooltip_text = "Run discover tests." -disabled = true -icon = SubResource("ImageTexture_jvn24") +icon = SubResource("DPITexture_mb3ih") [node name="btn_tree_sort" type="MenuButton" parent="VBoxContainer/tree_tools/tree_buttons"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Sets tree sorting mode." -disabled = true -icon = SubResource("ImageTexture_k82x4") +icon = SubResource("DPITexture_wo03e") flat = false item_count = 4 popup/item_0/text = "Unsorted" -popup/item_0/icon = SubResource("ImageTexture_bs7qq") +popup/item_0/icon = SubResource("DPITexture_ixycx") popup/item_0/checkable = 1 +popup/item_0/id = 0 popup/item_1/text = "Name ascending" -popup/item_1/icon = SubResource("ImageTexture_k82x4") +popup/item_1/icon = SubResource("DPITexture_wo03e") popup/item_1/checkable = 1 popup/item_1/checked = true popup/item_1/id = 1 popup/item_2/text = "Name descending" -popup/item_2/icon = SubResource("ImageTexture_0ck6a") +popup/item_2/icon = SubResource("ImageTexture_eis20") popup/item_2/checkable = 1 popup/item_2/id = 2 popup/item_3/text = "Execution time" -popup/item_3/icon = SubResource("ImageTexture_t7ac1") +popup/item_3/icon = SubResource("DPITexture_t2qd7") popup/item_3/checkable = 1 popup/item_3/id = 3 [node name="btn_tree_mode" type="MenuButton" parent="VBoxContainer/tree_tools/tree_buttons"] unique_name_in_owner = true layout_mode = 2 -tooltip_text = "Sets tree presentaion mode." -disabled = true -icon = SubResource("ImageTexture_03vfp") +tooltip_text = "Sets tree presentation mode." +icon = SubResource("ImageTexture_1mh1t") flat = false item_count = 2 popup/item_0/text = "Tree" -popup/item_0/icon = SubResource("ImageTexture_fv3i4") +popup/item_0/icon = SubResource("ImageTexture_bq8kn") popup/item_0/checkable = 1 popup/item_0/checked = true +popup/item_0/id = 0 popup/item_1/text = "Flat" -popup/item_1/icon = SubResource("ImageTexture_ab51p") +popup/item_1/icon = SubResource("ImageTexture_8lbfl") popup/item_1/checkable = 1 popup/item_1/id = 1 [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] layout_mode = 2 +theme_override_constants/separation = 0 [node name="status_bar" type="HFlowContainer" parent="VBoxContainer"] +layout_direction = 2 layout_mode = 2 -last_wrap_alignment = 1 +size_flags_vertical = 2 -[node name="errors" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="error" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) layout_mode = 2 -size_flags_vertical = 4 +size_flags_vertical = 0 +theme_override_constants/separation = -2 -[node name="error_value" type="Label" parent="VBoxContainer/status_bar/errors"] -unique_name_in_owner = true -use_parent_material = true -custom_minimum_size = Vector2(24, 0) +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/error"] layout_mode = 2 -size_flags_horizontal = 2 -text = "0" -horizontal_alignment = 2 -justification_flags = 0 +size_flags_horizontal = 3 -[node name="icon_errors" type="TextureRect" parent="VBoxContainer/status_bar/errors"] +[node name="icon_errors" type="TextureRect" parent="VBoxContainer/status_bar/error/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_2rpr0") -stretch_mode = 2 +tooltip_text = "Error Tests" +texture = SubResource("ImageTexture_ivm1h") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/errors"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/error/icon"] layout_mode = 2 -text = "Errors" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous error test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/errors"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/error"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_error_up" type="Button" parent="VBoxContainer/status_bar/errors/navigation"] +[node name="error_value" type="Label" parent="VBoxContainer/status_bar/error/counter"] +unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_1oriu") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_error_down" type="Button" parent="VBoxContainer/status_bar/errors/navigation"] +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/error/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next error test" icon = SubResource("ImageTexture_ikyhk") [node name="VSeparator" type="VSeparator" parent="VBoxContainer/status_bar"] layout_mode = 2 -[node name="failures" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="failure" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) layout_mode = 2 -size_flags_vertical = 4 +theme_override_constants/separation = -2 -[node name="failure_value" type="Label" parent="VBoxContainer/status_bar/failures"] -unique_name_in_owner = true -use_parent_material = true -custom_minimum_size = Vector2(24, 0) +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/failure"] layout_mode = 2 -size_flags_horizontal = 0 -text = "0" -horizontal_alignment = 2 -vertical_alignment = 1 -justification_flags = 160 -max_lines_visible = 1 +size_flags_horizontal = 3 -[node name="icon_failures" type="TextureRect" parent="VBoxContainer/status_bar/failures"] +[node name="icon_failures" type="TextureRect" parent="VBoxContainer/status_bar/failure/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_i2d73") -stretch_mode = 2 +tooltip_text = "Failed Tests" +texture = SubResource("ImageTexture_suo5c") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/failures"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/failure/icon"] layout_mode = 2 -text = "Failures" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous failed test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/failures"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/failure"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_failure_up" type="Button" parent="VBoxContainer/status_bar/failures/navigation"] +[node name="failure_value" type="Label" parent="VBoxContainer/status_bar/failure/counter"] unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_mph2m") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_failure_down" type="Button" parent="VBoxContainer/status_bar/failures/navigation"] -unique_name_in_owner = true +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/failure/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_k6fqi") +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next failed test" +icon = SubResource("ImageTexture_ikyhk") [node name="VSeparator2" type="VSeparator" parent="VBoxContainer/status_bar"] layout_mode = 2 -[node name="flaky" type="HBoxContainer" parent="VBoxContainer/status_bar"] +[node name="flaky" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) +layout_mode = 2 +theme_override_constants/separation = -2 + +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="icon_flaky" type="TextureRect" parent="VBoxContainer/status_bar/flaky/icon"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Flaky Tests" +texture = SubResource("ImageTexture_d5kq4") +stretch_mode = 3 + +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/flaky/icon"] layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 +tooltip_text = "Jump to the previous flaky test" +icon = SubResource("ImageTexture_1oriu") + +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +auto_translate_mode = 2 +layout_mode = 2 +localize_numeral_system = false -[node name="flaky_value" type="Label" parent="VBoxContainer/status_bar/flaky"] +[node name="flaky_value" type="Label" parent="VBoxContainer/status_bar/flaky/counter"] unique_name_in_owner = true use_parent_material = true -custom_minimum_size = Vector2(24, 0) +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_horizontal = 0 +size_flags_horizontal = 10 text = "0" horizontal_alignment = 2 -vertical_alignment = 1 -justification_flags = 160 -max_lines_visible = 1 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 + +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/flaky/counter"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next flaky test" +icon = SubResource("ImageTexture_ikyhk") + +[node name="VSeparator3" type="VSeparator" parent="VBoxContainer/status_bar"] +layout_mode = 2 + +[node name="skipped" type="VBoxContainer" parent="VBoxContainer/status_bar"] +custom_minimum_size = Vector2(0, 48) +layout_mode = 2 +theme_override_constants/separation = -2 + +[node name="icon" type="HBoxContainer" parent="VBoxContainer/status_bar/skipped"] +layout_mode = 2 +size_flags_horizontal = 3 -[node name="icon_flaky" type="TextureRect" parent="VBoxContainer/status_bar/flaky"] +[node name="icon_skipped" type="TextureRect" parent="VBoxContainer/status_bar/skipped/icon"] unique_name_in_owner = true layout_mode = 2 +size_flags_horizontal = 10 size_flags_vertical = 4 -texture = SubResource("ImageTexture_04e57") -stretch_mode = 2 +tooltip_text = "Skipped Tests" +texture = SubResource("ImageTexture_qagbu") +stretch_mode = 3 -[node name="Label" type="Label" parent="VBoxContainer/status_bar/flaky"] +[node name="btn_up" type="Button" parent="VBoxContainer/status_bar/skipped/icon"] layout_mode = 2 -text = "Flaky" -justification_flags = 0 +size_flags_horizontal = 10 +size_flags_vertical = 4 +tooltip_text = "Jump to the previous skipped test" +icon = SubResource("ImageTexture_1oriu") -[node name="navigation" type="HBoxContainer" parent="VBoxContainer/status_bar/flaky"] +[node name="counter" type="HBoxContainer" parent="VBoxContainer/status_bar/skipped"] auto_translate_mode = 2 layout_mode = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 localize_numeral_system = false -[node name="btn_flaky_up" type="Button" parent="VBoxContainer/status_bar/flaky/navigation"] +[node name="skipped_value" type="Label" parent="VBoxContainer/status_bar/skipped/counter"] +unique_name_in_owner = true +use_parent_material = true +custom_minimum_size = Vector2(32, 0) layout_mode = 2 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." -icon = SubResource("ImageTexture_1oriu") +size_flags_horizontal = 10 +text = "0" +horizontal_alignment = 2 +justification_flags = 0 +visible_characters = 3 +visible_ratio = 3.0 -[node name="btn_flaky_down" type="Button" parent="VBoxContainer/status_bar/flaky/navigation"] +[node name="btn_down" type="Button" parent="VBoxContainer/status_bar/skipped/counter"] layout_mode = 2 -size_flags_horizontal = 0 -size_flags_vertical = 3 -tooltip_text = "Shows the total test errors." +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Jump to the next skipped test" icon = SubResource("ImageTexture_ikyhk") -[connection signal="pressed" from="VBoxContainer/tree_tools/tree_buttons/btn_tree_sync" to="." method="_on_tree_sync_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/errors/navigation/btn_error_up" to="." method="_on_btn_error_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/errors/navigation/btn_error_down" to="." method="_on_btn_error_down_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/failures/navigation/btn_failure_up" to="." method="_on_failure_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/failures/navigation/btn_failure_down" to="." method="_on_failure_down_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/flaky/navigation/btn_flaky_up" to="." method="_on_btn_flaky_up_pressed"] -[connection signal="pressed" from="VBoxContainer/status_bar/flaky/navigation/btn_flaky_down" to="." method="_on_btn_flaky_down_pressed"] +[connection signal="pressed" from="VBoxContainer/tree_tools/tree_buttons/btn_tree_sync" to="." method="_on_btn_tree_sync_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/error/icon/btn_up" to="." method="_on_btn_error_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/error/counter/btn_down" to="." method="_on_btn_error_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/failure/icon/btn_up" to="." method="_on_failure_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/failure/counter/btn_down" to="." method="_on_failure_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/flaky/icon/btn_up" to="." method="_on_btn_flaky_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/flaky/counter/btn_down" to="." method="_on_btn_flaky_down_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/skipped/icon/btn_up" to="." method="_on_btn_skipped_up_pressed"] +[connection signal="pressed" from="VBoxContainer/status_bar/skipped/counter/btn_down" to="." method="_on_btn_skipped_down_pressed"] diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd index 3407048d..4a92c528 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd @@ -1,9 +1,8 @@ @tool extends PanelContainer -signal run_overall_pressed(debug: bool) -signal run_pressed(debug: bool) -signal stop_pressed() + +const InspectorTreeMainPanel := preload("res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd") @onready var _version_label: Control = %version @onready var _button_wiki: Button = %help @@ -14,83 +13,74 @@ signal stop_pressed() @onready var _button_stop: Button = %stop - -const SETTINGS_SHORTCUT_MAPPING := { - GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST: GdUnitShortcut.ShortCut.RERUN_TESTS, - GdUnitSettings.SHORTCUT_INSPECTOR_RERUN_TEST_DEBUG: GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG, - GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_OVERALL: GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL, - GdUnitSettings.SHORTCUT_INSPECTOR_RUN_TEST_STOP: GdUnitShortcut.ShortCut.STOP_TEST_RUN, -} +var inspector: InspectorTreeMainPanel +var command_handler: GdUnitCommandHandler -@warning_ignore("return_value_discarded") func _ready() -> void: + command_handler = GdUnitCommandHandler.instance() + inspector = get_parent().get_parent().find_child("MainPanel", false, false) + if inspector == null: + push_error("Internal error, can't connect to the test inspector!") + else: + inspector.tree_item_selected.connect(_on_inspector_selected) + GdUnit4Version.init_version_label(_version_label) - var command_handler := GdUnitCommandHandler.instance() - run_pressed.connect(command_handler._on_run_pressed) - run_overall_pressed.connect(command_handler._on_run_overall_pressed) - stop_pressed.connect(command_handler._on_stop_pressed) - command_handler.gdunit_runner_start.connect(_on_gdunit_runner_start) - command_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop) - GdUnitSignals.instance().gdunit_settings_changed.connect(_on_gdunit_settings_changed) - init_buttons() - init_shortcuts(command_handler) + GdUnitSignals.instance().gdunit_event.connect(_on_gdunit_event) + GdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed) + _init_buttons() -func init_buttons() -> void: - _button_run_overall.icon = GdUnitUiTools.get_run_overall_icon() - _button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show() - _button_run.icon = GdUnitUiTools.get_icon("Play") - _button_run_debug.icon = GdUnitUiTools.get_icon("PlayStart") - _button_stop.icon = GdUnitUiTools.get_icon("Stop") +func _init_buttons() -> void: + _init_button(_button_run_overall, GdUnitCommandRunTestsOverall.ID) + _init_button(_button_run, GdUnitCommandInspectorRunTests.ID) + _init_button(_button_run_debug, GdUnitCommandInspectorDebugTests.ID) + _init_button(_button_stop, GdUnitCommandStopTestSession.ID) + + _button_stop.icon = command_handler.command_icon(GdUnitCommandStopTestSession.ID) _tool_button.icon = GdUnitUiTools.get_icon("Tools") _button_wiki.icon = GdUnitUiTools.get_icon("HelpSearch") + # Set run buttons initial disabled + _button_run.disabled = true + _button_run_debug.disabled = true -func init_shortcuts(command_handler: GdUnitCommandHandler) -> void: - _button_run.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RERUN_TESTS) - _button_run_overall.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL) - _button_run_debug.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG) - _button_stop.shortcut = command_handler.get_shortcut(GdUnitShortcut.ShortCut.STOP_TEST_RUN) - # register for shortcut changes - @warning_ignore("return_value_discarded") - GdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed.bind(command_handler)) - - -func _on_runoverall_pressed(debug:=false) -> void: - run_overall_pressed.emit(debug) - - -func _on_run_pressed(debug := false) -> void: - run_pressed.emit(debug) - +func _init_button(button: Button, comand_id: String) -> void: + button.set_meta("GdUnitCommand", comand_id) + button.icon = command_handler.command_icon(comand_id) + button.shortcut = command_handler.command_shortcut(comand_id) + if button == _button_run_overall: + button.visible = GdUnitSettings.is_inspector_toolbar_button_show() -func _on_stop_pressed() -> void: - stop_pressed.emit() +func _on_inspector_selected(item: TreeItem) -> void: + var button_disabled := item == null + _button_run.disabled = button_disabled + _button_run_debug.disabled = button_disabled -func _on_gdunit_runner_start() -> void: - _button_run_overall.disabled = true - _button_run.disabled = true - _button_run_debug.disabled = true - _button_stop.disabled = false +func _on_gdunit_event(event: GdUnitEvent) -> void: + if event.type() == GdUnitEvent.SESSION_START: + _button_run_overall.disabled = true + _button_run.disabled = true + _button_run_debug.disabled = true + _button_stop.disabled = false + return + if event.type() == GdUnitEvent.SESSION_CLOSE: + _button_run_overall.disabled = false + _button_stop.disabled = true -func _on_gdunit_runner_stop(_client_id: int) -> void: - _button_run_overall.disabled = false - _button_run.disabled = false - _button_run_debug.disabled = false - _button_stop.disabled = true - -func _on_gdunit_settings_changed(_property: GdUnitProperty) -> void: - _button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show() +func _on_button_pressed(source: BaseButton) -> void: + var command_id: String = source.get_meta("GdUnitCommand") + await command_handler.command_execute(command_id) func _on_wiki_pressed() -> void: - @warning_ignore("return_value_discarded") - OS.shell_open("https://mikeschulze.github.io/gdUnit4/") + var status := OS.shell_open("https://godot-gdunit-labs.github.io/gdUnit4/latest") + if status != OK: + push_error("Can't open GdUnit4 documentaion page: %s" % error_string(status)) func _on_btn_tool_pressed() -> void: @@ -101,17 +91,14 @@ func _on_btn_tool_pressed() -> void: settings_dlg.popup_centered_ratio(.60) -func _on_settings_changed(property: GdUnitProperty, command_handler: GdUnitCommandHandler) -> void: +func _on_settings_changed(property: GdUnitProperty) -> void: # needs to wait a frame to be command handler notified first for settings changes await get_tree().process_frame - if SETTINGS_SHORTCUT_MAPPING.has(property.name()): - var shortcut: GdUnitShortcut.ShortCut = SETTINGS_SHORTCUT_MAPPING.get(property.name(), GdUnitShortcut.ShortCut.NONE) - match shortcut: - GdUnitShortcut.ShortCut.RERUN_TESTS: - _button_run.shortcut = command_handler.get_shortcut(shortcut) - GdUnitShortcut.ShortCut.RUN_TESTS_OVERALL: - _button_run_overall.shortcut = command_handler.get_shortcut(shortcut) - GdUnitShortcut.ShortCut.RERUN_TESTS_DEBUG: - _button_run_debug.shortcut = command_handler.get_shortcut(shortcut) - GdUnitShortcut.ShortCut.STOP_TEST_RUN: - _button_stop.shortcut = command_handler.get_shortcut(shortcut) + + _button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show() + + if property.name().begins_with(GdUnitSettings.GROUP_SHORTCUT_INSPECTOR): + _button_run.shortcut = command_handler.command_shortcut(GdUnitCommandInspectorRunTests.ID) + _button_run_debug.shortcut = command_handler.command_shortcut(GdUnitCommandInspectorDebugTests.ID) + _button_run_overall.shortcut = command_handler.command_shortcut(GdUnitCommandRunTestsOverall.ID) + _button_stop.shortcut = command_handler.command_shortcut(GdUnitCommandStopTestSession.ID) diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid index 2addee17..f0716418 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid @@ -1 +1 @@ -uid://d311g1tcmdbtv +uid://7ef1yiaqphsc diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn index 210fa9e3..8cd529e7 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn @@ -1,41 +1,35 @@ -[gd_scene load_steps=22 format=3 uid="uid://dx7xy4dgi3wwb"] +[gd_scene load_steps=17 format=3 uid="uid://dx7xy4dgi3wwb"] -[ext_resource type="Script" uid="uid://25styuldjg2p" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd" id="3"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd" id="3"] -[sub_resource type="Image" id="Image_c7rhl"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 168, 224, 224, 224, 233, 224, 224, 224, 236, 224, 224, 224, 170, 231, 231, 231, 31, 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, 227, 227, 227, 36, 224, 224, 224, 234, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 230, 230, 230, 30, 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, 224, 224, 224, 168, 224, 224, 224, 255, 224, 224, 224, 186, 224, 224, 224, 32, 224, 224, 224, 33, 224, 224, 224, 187, 224, 224, 224, 255, 225, 225, 225, 167, 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, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 255, 224, 224, 224, 234, 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, 224, 224, 224, 237, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 38, 224, 224, 224, 255, 224, 224, 224, 229, 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, 224, 224, 224, 164, 224, 224, 224, 255, 224, 224, 224, 187, 225, 225, 225, 34, 227, 227, 227, 36, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 162, 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, 224, 224, 224, 24, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 229, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 24, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 210, 224, 224, 224, 161, 224, 224, 224, 232, 224, 224, 224, 231, 225, 225, 225, 159, 230, 230, 230, 30, 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, 224, 224, 224, 107, 224, 224, 224, 255, 224, 224, 224, 210, 230, 230, 230, 20, 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, 224, 224, 224, 105, 230, 230, 230, 20, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 224, 224, 224, 130, 255, 255, 255, 1, 255, 255, 255, 1, 225, 225, 225, 134, 224, 224, 224, 224, 225, 225, 225, 223, 224, 224, 224, 132, 255, 255, 255, 1, 255, 255, 255, 6, 224, 224, 224, 137, 224, 224, 224, 231, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 130, 225, 225, 225, 133, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 129, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 220, 225, 225, 225, 223, 224, 224, 224, 255, 226, 226, 226, 61, 224, 224, 224, 65, 224, 224, 224, 255, 224, 224, 224, 222, 224, 224, 224, 231, 224, 224, 224, 255, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 222, 224, 224, 224, 255, 227, 227, 227, 63, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 230, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 127, 224, 224, 224, 129, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 126, 225, 225, 225, 135, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 1, 224, 224, 224, 128, 224, 224, 224, 220, 224, 224, 224, 219, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 134, 224, 224, 224, 229, 224, 224, 224, 255, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_c7rhl"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_t52y3"] -image = SubResource("Image_c7rhl") - -[sub_resource type="Image" id="Image_3erui"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 224, 224, 224, 64, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 142, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 138, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 192, 224, 224, 224, 255, 225, 225, 225, 191, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 142, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 255, 224, 224, 224, 137, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 192, 224, 224, 224, 255, 225, 225, 225, 191, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 236, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 64, 224, 224, 224, 255, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 235, 224, 224, 224, 255, 224, 224, 224, 65, 225, 225, 225, 67, 224, 224, 224, 255, 224, 224, 224, 230, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 140, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 134, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 137, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 135, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 247, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 246, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 183, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 179, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 179, 224, 224, 224, 237, 224, 224, 224, 179, 230, 230, 230, 20, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 190, 224, 224, 224, 188, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_3erui"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_wip2b"] -image = SubResource("Image_3erui") - -[sub_resource type="InputEventKey" id="InputEventKey_6jdrj"] -ctrl_pressed = true +[sub_resource type="InputEventKey" id="InputEventKey_p22nw"] +alt_pressed = true pressed = true keycode = 4194338 physical_keycode = 4194338 -[sub_resource type="Shortcut" id="Shortcut_t0ytp"] -events = [SubResource("InputEventKey_6jdrj")] +[sub_resource type="Shortcut" id="Shortcut_3lcek"] +events = [SubResource("InputEventKey_p22nw")] -[sub_resource type="Image" id="Image_p22nw"] +[sub_resource type="Image" id="Image_ndw0i"] data = { "data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), "format": "RGBA8", @@ -44,72 +38,63 @@ data = { "width": 16 } -[sub_resource type="ImageTexture" id="ImageTexture_mggmt"] -image = SubResource("Image_p22nw") +[sub_resource type="ImageTexture" id="ImageTexture_eoihf"] +image = SubResource("Image_ndw0i") -[sub_resource type="InputEventKey" id="InputEventKey_pl3pi"] -ctrl_pressed = true +[sub_resource type="InputEventKey" id="InputEventKey_6gwbs"] +alt_pressed = true pressed = true keycode = 4194336 physical_keycode = 4194336 -[sub_resource type="Shortcut" id="Shortcut_77xhn"] -events = [SubResource("InputEventKey_pl3pi")] +[sub_resource type="Shortcut" id="Shortcut_2i8uq"] +events = [SubResource("InputEventKey_6gwbs")] -[sub_resource type="Image" id="Image_3lcek"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 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, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 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, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_87jj1"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_q6qbp"] -image = SubResource("Image_3lcek") - -[sub_resource type="InputEventKey" id="InputEventKey_qk8q5"] -ctrl_pressed = true +[sub_resource type="InputEventKey" id="InputEventKey_fewwl"] +alt_pressed = true pressed = true keycode = 4194337 physical_keycode = 4194337 -[sub_resource type="Shortcut" id="Shortcut_ae6em"] -events = [SubResource("InputEventKey_qk8q5")] +[sub_resource type="Shortcut" id="Shortcut_f3lkx"] +events = [SubResource("InputEventKey_fewwl")] -[sub_resource type="Image" id="Image_ndw0i"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 202, 228, 228, 228, 37, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 224, 224, 224, 74, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 123, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 168, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 118, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 226, 226, 226, 70, 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, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 201, 225, 225, 225, 34, 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, 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, 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="DPITexture" id="DPITexture_7oobd"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_q0wt0"] -image = SubResource("Image_ndw0i") - -[sub_resource type="InputEventKey" id="InputEventKey_l8obn"] -ctrl_pressed = true +[sub_resource type="InputEventKey" id="InputEventKey_lvbnb"] +alt_pressed = true pressed = true keycode = 4194339 physical_keycode = 4194339 -[sub_resource type="Shortcut" id="Shortcut_2mb87"] -events = [SubResource("InputEventKey_l8obn")] +[sub_resource type="Shortcut" id="Shortcut_6idxu"] +events = [SubResource("InputEventKey_lvbnb")] -[sub_resource type="Image" id="Image_eoihf"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 176, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 177, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 177, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 176, 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, 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, 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="DPITexture" id="DPITexture_qf2s1"] +_source = " +" +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(1, 0.87, 0.4, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_1wiyx"] -image = SubResource("Image_eoihf") - [node name="ToolBar" type="PanelContainer"] anchors_preset = 10 anchor_right = 1.0 @@ -131,13 +116,13 @@ size_flags_vertical = 4 [node name="help" type="Button" parent="HBoxContainer/tools"] unique_name_in_owner = true layout_mode = 2 -icon = SubResource("ImageTexture_t52y3") +icon = SubResource("DPITexture_c7rhl") [node name="tool" type="Button" parent="HBoxContainer/tools"] unique_name_in_owner = true layout_mode = 2 tooltip_text = "GdUnit Settings" -icon = SubResource("ImageTexture_wip2b") +icon = SubResource("DPITexture_3erui") [node name="controls" type="HBoxContainer" parent="HBoxContainer"] layout_mode = 2 @@ -150,28 +135,32 @@ layout_mode = 2 [node name="run_overall" type="Button" parent="HBoxContainer/controls"] unique_name_in_owner = true -visible = false use_parent_material = true layout_mode = 2 tooltip_text = "Run overall tests" -shortcut = SubResource("Shortcut_t0ytp") -icon = SubResource("ImageTexture_mggmt") +shortcut = SubResource("Shortcut_3lcek") +icon = SubResource("ImageTexture_eoihf") +metadata/GdUnitCommand = "Run Tests Overall" [node name="run" type="Button" parent="HBoxContainer/controls"] unique_name_in_owner = true use_parent_material = true layout_mode = 2 tooltip_text = "Rerun unit tests" -shortcut = SubResource("Shortcut_77xhn") -icon = SubResource("ImageTexture_q6qbp") +disabled = true +shortcut = SubResource("Shortcut_2i8uq") +icon = SubResource("DPITexture_87jj1") +metadata/GdUnitCommand = "Run Inspector Tests" [node name="debug" type="Button" parent="HBoxContainer/controls"] unique_name_in_owner = true use_parent_material = true layout_mode = 2 tooltip_text = "Rerun unit tests (Debug)" -shortcut = SubResource("Shortcut_ae6em") -icon = SubResource("ImageTexture_q0wt0") +disabled = true +shortcut = SubResource("Shortcut_f3lkx") +icon = SubResource("DPITexture_7oobd") +metadata/GdUnitCommand = "Debug Inspector Tests" [node name="stop" type="Button" parent="HBoxContainer/controls"] unique_name_in_owner = true @@ -179,8 +168,9 @@ use_parent_material = true layout_mode = 2 tooltip_text = "Stops runing unit tests" disabled = true -shortcut = SubResource("Shortcut_2mb87") -icon = SubResource("ImageTexture_1wiyx") +shortcut = SubResource("Shortcut_6idxu") +icon = SubResource("DPITexture_qf2s1") +metadata/GdUnitCommand = "Stop Test Session" [node name="VSeparator4" type="VSeparator" parent="HBoxContainer/controls"] layout_mode = 2 @@ -194,19 +184,19 @@ alignment = 2 [node name="version" type="Label" parent="HBoxContainer/CenterContainer"] unique_name_in_owner = true +auto_translate_mode = 2 use_parent_material = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 13 -auto_translate = false localize_numeral_system = false -text = "gdUnit4 4.3.0" +text = "gdUnit4 6.1.0" horizontal_alignment = 1 justification_flags = 160 [connection signal="pressed" from="HBoxContainer/tools/help" to="." method="_on_wiki_pressed"] [connection signal="pressed" from="HBoxContainer/tools/tool" to="." method="_on_btn_tool_pressed"] -[connection signal="pressed" from="HBoxContainer/controls/run_overall" to="." method="_on_runoverall_pressed"] -[connection signal="pressed" from="HBoxContainer/controls/run" to="." method="_on_run_pressed"] -[connection signal="pressed" from="HBoxContainer/controls/debug" to="." method="_on_run_pressed" binds= [true]] -[connection signal="pressed" from="HBoxContainer/controls/stop" to="." method="_on_stop_pressed"] +[connection signal="pressed" from="HBoxContainer/controls/run_overall" to="." method="_on_button_pressed" flags=18] +[connection signal="pressed" from="HBoxContainer/controls/run" to="." method="_on_button_pressed" flags=18] +[connection signal="pressed" from="HBoxContainer/controls/debug" to="." method="_on_button_pressed" flags=18] +[connection signal="pressed" from="HBoxContainer/controls/stop" to="." method="_on_button_pressed" flags=18] diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd index ac383025..f55dbeef 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd +++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd @@ -3,17 +3,13 @@ extends VSplitContainer ## Will be emitted when the test index counter is changed signal test_counters_changed(index: int, total: int, state: GdUnitInspectorTreeConstants.STATE) - -const CONTEXT_MENU_RUN_ID = 0 -const CONTEXT_MENU_DEBUG_ID = 1 -const CONTEXT_MENU_COLLAPSE_ALL = 3 -const CONTEXT_MENU_EXPAND_ALL = 4 +signal tree_item_selected(item: TreeItem) @onready var _tree: Tree = $Panel/Tree @onready var _report_list: Node = $report/ScrollContainer/list @onready var _report_template: RichTextLabel = $report/report_template -@onready var _context_menu: PopupMenu = $contextMenu +@onready var _context_menu: GdUnitInspectorContextMenu = $contextMenu @onready var _discover_hint: Control = %discover_hint @onready var _spinner: Button = %spinner @@ -61,21 +57,9 @@ const STATE = GdUnitInspectorTreeConstants.STATE var _tree_root: TreeItem -var _item_hash := Dictionary() +var _current_selected_item: TreeItem = null var _current_tree_view_mode := GdUnitSettings.get_inspector_tree_view_mode() - - -func _build_cache_key(resource_path: String, test_name: String) -> Array: - return [resource_path, test_name] - - -func add_tree_item_to_cache(resource_path: String, test_name: String, item: TreeItem) -> void: - var key := _build_cache_key(resource_path, test_name) - _item_hash[key] = item - - -func clear_tree_item_cache() -> void: - _item_hash.clear() +var _run_test_recovery := true ## Used for debugging purposes only @@ -110,15 +94,16 @@ func _find_tree_item_by_id(parent: TreeItem, id: GdUnitGUID) -> TreeItem: return null -func _find_tree_item_by_path(resource_path: String, item_name: String) -> TreeItem: - var key := _build_cache_key(resource_path, item_name) - return _item_hash.get(key, null) - - -func _find_by_resource_path(current: TreeItem, resource_path: String) -> TreeItem: - for item in current.get_children(): - if get_item_source_file(item) == resource_path: - return item +func _find_tree_item_by_test_suite(parent: TreeItem, suite_path: String, suite_name: String) -> TreeItem: + for child in parent.get_children(): + if child.get_meta(META_GDUNIT_TYPE) == GdUnitType.TEST_SUITE: + var test_case: GdUnitTestCase = child.get_meta(META_TEST_CASE) + if test_case.suite_resource_path == suite_path and test_case.suite_name == suite_name: + return child + if child.get_child_count() > 0: + var item := _find_tree_item_by_test_suite(child, suite_path, suite_name) + if item != null: + return item return null @@ -195,15 +180,16 @@ func is_test_id(item: TreeItem, id: GdUnitGUID) -> bool: var test_case: GdUnitTestCase = item.get_meta(META_TEST_CASE) return test_case.guid.equals(id) + +func disable_test_recovery() -> void: + _run_test_recovery = false + + @warning_ignore("return_value_discarded") func _ready() -> void: - _context_menu.set_item_icon(CONTEXT_MENU_RUN_ID, GdUnitUiTools.get_icon("Play")) - _context_menu.set_item_icon(CONTEXT_MENU_DEBUG_ID, GdUnitUiTools.get_icon("PlayStart")) - _context_menu.set_item_icon(CONTEXT_MENU_EXPAND_ALL, GdUnitUiTools.get_icon("ExpandTree")) - _context_menu.set_item_icon(CONTEXT_MENU_COLLAPSE_ALL, GdUnitUiTools.get_icon("CollapseTree")) - # do colorize the icons - #for index in _context_menu.item_count: - # _context_menu.set_item_icon_modulate(index, Color.MEDIUM_PURPLE) + if Engine.is_editor_hint(): + var base_control := EditorInterface.get_base_control() + base_control.set_meta("GdUnit4Inspector", self) _spinner.icon = GdUnitUiTools.get_spinner() init_tree() @@ -212,9 +198,8 @@ func _ready() -> void: GdUnitSignals.instance().gdunit_test_discover_added.connect(on_test_case_discover_added) GdUnitSignals.instance().gdunit_test_discover_deleted.connect(on_test_case_discover_deleted) GdUnitSignals.instance().gdunit_test_discover_modified.connect(on_test_case_discover_modified) - var command_handler := GdUnitCommandHandler.instance() - command_handler.gdunit_runner_start.connect(_on_gdunit_runner_start) - command_handler.gdunit_runner_stop.connect(_on_gdunit_runner_stop) + if _run_test_recovery: + GdUnitTestDiscoverer.restore_last_session() # we need current to manually redraw bacause of the animation bug @@ -226,6 +211,7 @@ func _process(_delta: float) -> void: func init_tree() -> void: cleanup_tree() + _tree.deselect_all() _tree.set_hide_root(true) _tree.ensure_cursor_is_visible() _tree.set_allow_reselect(true) @@ -250,11 +236,11 @@ func init_tree() -> void: func cleanup_tree() -> void: clear_reports() - clear_tree_item_cache() if not _tree_root: return _free_recursive() _tree.clear() + _current_selected_item = null func _free_recursive(items:=_tree_root.get_children()) -> void: @@ -263,12 +249,20 @@ func _free_recursive(items:=_tree_root.get_children()) -> void: item.call_deferred("free") -func sort_tree_items(parent :TreeItem) -> void: +func sort_tree_items(parent: TreeItem) -> void: + _sort_tree_items(parent, GdUnitSettings.get_inspector_tree_sort_mode()) + _tree.queue_redraw() + + +static func _sort_tree_items(parent: TreeItem, sort_mode: GdUnitInspectorTreeConstants.SORT_MODE) -> void: parent.visible = false var items := parent.get_children() + # first remove all childs before sorting + for item in items: + parent.remove_child(item) # do sort by selected sort mode - match GdUnitSettings.get_inspector_tree_sort_mode(): + match sort_mode: GdUnitInspectorTreeConstants.SORT_MODE.UNSORTED: items.sort_custom(sort_items_by_original_index) @@ -281,32 +275,42 @@ func sort_tree_items(parent :TreeItem) -> void: GdUnitInspectorTreeConstants.SORT_MODE.EXECUTION_TIME: items.sort_custom(sort_items_by_execution_time) + # readding sorted childs for item in items: - parent.remove_child(item) parent.add_child(item) if item.get_child_count() > 0: - sort_tree_items(item) + _sort_tree_items(item, sort_mode) parent.visible = true - _tree.queue_redraw() -func sort_items_by_name(a: TreeItem, b: TreeItem, ascending: bool) -> bool: +static func sort_items_by_name(a: TreeItem, b: TreeItem, ascending: bool) -> bool: var type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE) var type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE) - # Compare types first - if type_a != type_b: - return type_a == GdUnitType.FOLDER - var name_a :String = a.get_meta(META_GDUNIT_NAME) - var name_b :String = b.get_meta(META_GDUNIT_NAME) - return name_a.naturalnocasecmp_to(name_b) < 0 if ascending else name_a.naturalnocasecmp_to(name_b) > 0 + + # Sort folders to the top + if type_a == GdUnitType.FOLDER and type_b != GdUnitType.FOLDER: + return true + if type_b == GdUnitType.FOLDER and type_a != GdUnitType.FOLDER: + return false + + # sort by name + var name_a: String = a.get_meta(META_GDUNIT_NAME) + var name_b: String = b.get_meta(META_GDUNIT_NAME) + var comparison := name_a.naturalnocasecmp_to(name_b) + + return comparison < 0 if ascending else comparison > 0 -func sort_items_by_execution_time(a: TreeItem, b: TreeItem) -> bool: +static func sort_items_by_execution_time(a: TreeItem, b: TreeItem) -> bool: var type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE) var type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE) - # Compare types first - if type_a != type_b: - return type_a == GdUnitType.FOLDER + + # Sort folders to the top + if type_a == GdUnitType.FOLDER and type_b != GdUnitType.FOLDER: + return true + if type_b == GdUnitType.FOLDER and type_a != GdUnitType.FOLDER: + return false + var execution_time_a :int = a.get_meta(META_GDUNIT_EXECUTION_TIME) var execution_time_b :int = b.get_meta(META_GDUNIT_EXECUTION_TIME) # if has same execution time sort by name @@ -317,13 +321,20 @@ func sort_items_by_execution_time(a: TreeItem, b: TreeItem) -> bool: return execution_time_a > execution_time_b -func sort_items_by_original_index(a: TreeItem, b: TreeItem) -> bool: +static func sort_items_by_original_index(a: TreeItem, b: TreeItem) -> bool: var type_a: GdUnitType = a.get_meta(META_GDUNIT_TYPE) var type_b: GdUnitType = b.get_meta(META_GDUNIT_TYPE) - if type_a != type_b: - return type_a == GdUnitType.FOLDER + + # Sort folders to the top + if type_a == GdUnitType.FOLDER and type_b != GdUnitType.FOLDER: + return true + if type_b == GdUnitType.FOLDER and type_a != GdUnitType.FOLDER: + return false + var index_a :int = a.get_meta(META_GDUNIT_ORIGINAL_INDEX) var index_b :int = b.get_meta(META_GDUNIT_ORIGINAL_INDEX) + + # Sorting by index return index_a < index_b @@ -518,23 +529,33 @@ func set_state_initial(item: TreeItem, type: GdUnitType) -> void: set_item_icon_by_state(item) -func set_state_running(item: TreeItem) -> void: +func set_state_running(item: TreeItem, is_running: bool) -> void: if is_state_running(item): return - item.set_custom_color(0, Color.DARK_GREEN) - item.set_custom_color(1, Color.DARK_GREEN) - item.set_icon(0, ICON_SPINNER) - item.set_meta(META_GDUNIT_STATE, STATE.RUNNING) - item.collapsed = false + if is_item_state(item, STATE.INITIAL): + item.set_custom_color(0, Color.DARK_GREEN) + item.set_custom_color(1, Color.DARK_GREEN) + item.set_meta(META_GDUNIT_STATE, STATE.RUNNING) + item.collapsed = false + + if is_running: + item.set_icon(0, ICON_SPINNER) + else: + set_item_icon_by_state(item) + for child in item.get_children(): + set_item_icon_by_state(child) + var parent := item.get_parent() if parent != _tree_root: - set_state_running(parent) - # force scrolling to current test case - @warning_ignore("return_value_discarded") - select_item(item) + set_state_running(parent, is_running) func set_state_succeded(item: TreeItem) -> void: + # Do not overwrite higher states + if is_state_error(item) or is_state_failed(item): + return + if item == _tree_root: + return item.set_custom_color(0, Color.GREEN) item.set_custom_color(1, Color.GREEN) item.set_meta(META_GDUNIT_STATE, STATE.SUCCESS) @@ -637,12 +658,12 @@ func update_state(item: TreeItem, event: GdUnitEvent, add_reports := true) -> vo if item == null: return - if event.is_success() and event.is_flaky(): + if event.is_skipped(): + set_state_skipped(item) + elif event.is_success() and event.is_flaky(): set_state_flaky(item, event) elif event.is_success(): set_state_succeded(item) - elif event.is_skipped(): - set_state_skipped(item) elif event.is_error(): set_state_error(item) elif event.is_failed(): @@ -654,6 +675,14 @@ func update_state(item: TreeItem, event: GdUnitEvent, add_reports := true) -> vo add_report(item, report) set_state_orphan(item, event) + var parent := item.get_parent() + if parent == null: + return + + var item_state: int = item.get_meta(META_GDUNIT_STATE) + var parent_state: int = parent.get_meta(META_GDUNIT_STATE) + if item_state <= parent_state: + return update_state(item.get_parent(), event, false) @@ -667,15 +696,11 @@ func add_report(item: TreeItem, report: GdUnitReport) -> void: func abort_running(items:=_tree_root.get_children()) -> void: for item in items: - if is_state_running(item): + if item.get_icon(0) == ICON_SPINNER: set_state_aborted(item) abort_running(item.get_children()) -func select_first_failure() -> TreeItem: - return select_item(_find_first_item_by_state(_tree_root, STATE.FAILED)) - - func _on_select_next_item_by_state(item_state: int) -> TreeItem: var current_selected := _tree.get_selected() # If nothing is selected, the first error is selected or the next one in the vicinity of the current selection is found @@ -721,31 +746,32 @@ func show_failed_report(selected_item: TreeItem) -> void: for report in get_item_reports(selected_item): var reportNode: RichTextLabel = _report_template.duplicate() _report_list.add_child(reportNode) + reportNode.push_color(Color.DARK_TURQUOISE) reportNode.append_text(report.to_string()) + reportNode.pop() reportNode.visible = true func update_test_suite(event: GdUnitEvent) -> void: - var item := _find_tree_item_by_path(extract_resource_path(event), event.suite_name()) + var item := _find_tree_item_by_test_suite(_tree_root, event.resource_path(), event.suite_name()) if not item: - push_error("[GdUnitInspector:731] Internal Error: Can't find tree item for\n %s" % event) - return - if event.type() == GdUnitEvent.TESTSUITE_BEFORE: - set_state_running(item) + push_error("[InspectorTreeMainPanel#update_test_suite] Internal Error: Can't find test suite item '{_suite_name}' for {_resource_path} ".format(event)) return if event.type() == GdUnitEvent.TESTSUITE_AFTER: update_item_elapsed_time_counter(item, event.elapsed_time()) update_state(item, event) - update_progress_counters(item, 23) + set_state_running(item, false) func update_test_case(event: GdUnitEvent) -> void: var item := _find_tree_item_by_id(_tree_root, event.guid()) if not item: - push_error("Internal Error: Can't find test id %s" % [event.guid()]) + #push_error("Internal Error: Can't find test id %s" % [event.guid()]) return if event.type() == GdUnitEvent.TESTCASE_BEFORE: - set_state_running(item) + set_state_running(item, true) + # force scrolling to current test case + _tree.scroll_to_item(item, true) return if event.type() == GdUnitEvent.TESTCASE_AFTER: @@ -753,7 +779,7 @@ func update_test_case(event: GdUnitEvent) -> void: if event.is_success() or event.is_warning(): update_item_processed_counter(item) update_state(item, event) - update_progress_counters(item, event.retry_count()) + update_progress_counters(item) func create_item(parent: TreeItem, test: GdUnitTestCase, item_name: String, type: GdUnitType) -> TreeItem: @@ -766,12 +792,10 @@ func create_item(parent: TreeItem, test: GdUnitTestCase, item_name: String, type item.set_meta(META_TEST_CASE, test) GdUnitType.TEST_GROUP: # We need to create a copy of the test record meta with a new uniqe guid - item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.source_file, test.line_number, test.test_name)) + item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.suite_resource_path, test.source_file, test.line_number, test.test_name)) GdUnitType.TEST_SUITE: # We need to create a copy of the test record meta with a new uniqe guid - item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.source_file, test.line_number, test.suite_name)) - # We need to add the suite item to the item cache by path because the guid is not provided - add_tree_item_to_cache(test.source_file, item_name, item) + item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.suite_resource_path, test.source_file, test.line_number, test.suite_name)) item.set_meta(META_GDUNIT_NAME, item_name) set_state_initial(item, type) @@ -799,12 +823,6 @@ func update_item_total_counter(item: TreeItem) -> void: item.set_meta(META_GDUNIT_PROGRESS_COUNT_MAX, child_count) item.set_text(0, "(0/%d) %s" % [child_count, item.get_meta(META_GDUNIT_NAME)]) - if item == _tree_root: - var index: int = item.get_meta(META_GDUNIT_PROGRESS_INDEX) - var total_test: int = item.get_meta(META_GDUNIT_PROGRESS_COUNT_MAX) - var state: STATE = item.get_meta(META_GDUNIT_STATE) - test_counters_changed.emit(index, total_test, state) - update_item_total_counter(item.get_parent()) @@ -827,13 +845,9 @@ func update_item_processed_counter(item: TreeItem, add_count := 1) -> void: update_item_processed_counter(item.get_parent(), add_count) -func update_progress_counters(item: TreeItem, rety_count: int) -> void: - var index: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_INDEX) +func update_progress_counters(item: TreeItem) -> void: + var index: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_INDEX) + 1 var total_test: int = _tree_root.get_meta(META_GDUNIT_PROGRESS_COUNT_MAX) - # We only increment the index counter once for a test - if rety_count <= 1: - index += 1 - var state: STATE = item.get_meta(META_GDUNIT_STATE) test_counters_changed.emit(index, total_test, state) _tree_root.set_meta(META_GDUNIT_PROGRESS_INDEX, index) @@ -882,12 +896,6 @@ func recalculate_counters(parent: TreeItem) -> void: # Update the display text parent.set_text(0, "(%d/%d) %s" % [success_count, total_count, parent.get_meta(META_GDUNIT_NAME)]) - # If this is the root, emit the counter change signal - if parent == _tree_root: - var state: STATE = parent.get_meta(META_GDUNIT_STATE) - test_counters_changed.emit(progress_index, total_count, state) - - func update_item_elapsed_time_counter(item: TreeItem, time: int) -> void: item.set_text(1, "%s" % LocalTime.elapsed(time)) @@ -952,7 +960,7 @@ func get_icon_by_file_type(path: String, state: STATE, orphans: bool) -> Texture func on_test_case_discover_added(test_case: GdUnitTestCase) -> void: var test_root_folder := GdUnitSettings.test_root_folder().replace("res://", "") - var fully_qualified_name := test_case.fully_qualified_name.trim_prefix(test_root_folder).trim_suffix(test_case.display_name) + var fully_qualified_name := test_case.fully_qualified_name.trim_suffix(test_case.display_name) var parts := fully_qualified_name.split(".", false) parts.append(test_case.display_name) # Skip tree structure until test root folder @@ -969,19 +977,22 @@ func on_test_case_discover_added(test_case: GdUnitTestCase) -> void: func create_items_tree_mode_tree(test_case: GdUnitTestCase, parts: PackedStringArray) -> void: var parent := _tree_root + var is_suite_assigned := false + var suite_name := test_case.suite_name.split(".")[-1] for item_name in parts: var next := _find_tree_item(parent, item_name) if next != null: parent = next continue - if item_name == test_case.display_name: + if not is_suite_assigned and suite_name == item_name: + next = create_item(parent, test_case, item_name, GdUnitType.TEST_SUITE) + is_suite_assigned = true + elif item_name == test_case.display_name: next = create_item(parent, test_case, item_name, GdUnitType.TEST_CASE) # On grouped tests (parameterized tests) elif item_name == test_case.test_name: next = create_item(parent, test_case, item_name, GdUnitType.TEST_GROUP) - elif test_case.suite_name.ends_with(item_name): - next = create_item(parent, test_case, item_name, GdUnitType.TEST_SUITE) else: next = create_item(parent, test_case, item_name, GdUnitType.FOLDER) parent = next @@ -1077,7 +1088,7 @@ func _dump_tree_as_json(dump_name: String) -> void: func _to_json(parent :TreeItem) -> Dictionary: var item_as_dict := GdObjects.obj2dict(parent) - item_as_dict["TreeItem"]["childs"] = parent.get_children().map(func(item: TreeItem) -> Dictionary: + item_as_dict["TreeItem"]["childrens"] = parent.get_children().map(func(item: TreeItem) -> Dictionary: return _to_json(item)) return item_as_dict @@ -1098,6 +1109,22 @@ func collect_test_cases(item: TreeItem, tests: Array[GdUnitTestCase] = []) -> Ar return tests +func test_session_start() -> void: + _context_menu.disable_items() + reset_tree_state(_tree_root) + clear_reports() + + +func test_session_stop() -> void: + _context_menu.enable_items() + abort_running() + sort_tree_items(_tree_root) + # wait until the tree redraw + await get_tree().process_frame + var failure_item := _find_first_item_by_state(_tree_root, STATE.FAILED) + select_item( failure_item if failure_item else _current_selected_item) + + ################################################################################ # Tree signal receiver ################################################################################ @@ -1107,23 +1134,10 @@ func _on_tree_item_mouse_selected(mouse_position: Vector2, mouse_button_index: i _context_menu.popup() -func _on_run_pressed(run_debug: bool) -> void: - _context_menu.hide() - var item: = _tree.get_selected() - if item == null: - print_rich("[color=GOLDENROD]Abort Testrun, no test suite selected![/color]") - return - - var test_to_execute := collect_test_cases(item) - GdUnitCommandHandler.instance().cmd_run_tests(test_to_execute, run_debug) - - func _on_Tree_item_selected() -> void: - # only show report checked manual item selection - # we need to check the run mode here otherwise it will be called every selection - if not _context_menu.is_item_disabled(CONTEXT_MENU_RUN_ID): - var selected_item: TreeItem = _tree.get_selected() - show_failed_report(selected_item) + _current_selected_item = _tree.get_selected() + show_failed_report(_current_selected_item) + tree_item_selected.emit(_current_selected_item) # Opens the test suite @@ -1152,23 +1166,6 @@ func _on_Tree_item_activated() -> void: ################################################################################ # external signal receiver ################################################################################ -func _on_gdunit_runner_start() -> void: - reset_tree_state(_tree_root) - _context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, true) - _context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, true) - clear_reports() - - -func _on_gdunit_runner_stop(_client_id: int) -> void: - _context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, false) - _context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, false) - abort_running() - sort_tree_items(_tree_root) - # wait until the tree redraw - await get_tree().process_frame - @warning_ignore("return_value_discarded") - select_first_failure() - func _on_gdunit_event(event: GdUnitEvent) -> void: match event.type(): @@ -1179,17 +1176,11 @@ func _on_gdunit_event(event: GdUnitEvent) -> void: GdUnitEvent.DISCOVER_END: sort_tree_items(_tree_root) + select_item(_tree_root.get_first_child()) _discover_hint.visible = false _tree_root.visible = true #_dump_tree_as_json("tree_example_discovered") - GdUnitEvent.INIT: - reset_tree_state(_tree_root) - - GdUnitEvent.STOP: - sort_tree_items(_tree_root) - #_dump_tree_as_json("tree_example") - GdUnitEvent.TESTCASE_BEFORE: update_test_case(event) @@ -1202,24 +1193,18 @@ func _on_gdunit_event(event: GdUnitEvent) -> void: GdUnitEvent.TESTSUITE_AFTER: update_test_suite(event) + GdUnitEvent.SESSION_START: + test_session_start() -func _on_context_m_index_pressed(index: int) -> void: - match index: - CONTEXT_MENU_DEBUG_ID: - _on_run_pressed(true) - CONTEXT_MENU_RUN_ID: - _on_run_pressed(false) - CONTEXT_MENU_EXPAND_ALL: - do_collapse_all(false) - CONTEXT_MENU_COLLAPSE_ALL: - do_collapse_all(true) + GdUnitEvent.SESSION_CLOSE: + await test_session_stop() func _on_settings_changed(property :GdUnitProperty) -> void: match property.name(): GdUnitSettings.INSPECTOR_TREE_SORT_MODE: sort_tree_items(_tree_root) - # _dump_tree_as_json("tree_sorted_by_%s" % GdUnitInspectorTreeConstants.SORT_MODE.keys()[property.value()]) + #_dump_tree_as_json("tree_sorted_by_%s" % GdUnitInspectorTreeConstants.SORT_MODE.keys()[property.value()]) GdUnitSettings.INSPECTOR_TREE_VIEW_MODE: restructure_tree(_tree_root, GdUnitSettings.get_inspector_tree_view_mode()) diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid index 2da8e436..11eac087 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid +++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid @@ -1 +1 @@ -uid://2io2vddkuoqf +uid://doyt5veei3nil diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn index 7d7af9b4..2754f749 100644 --- a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn +++ b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn @@ -1,172 +1,149 @@ -[gd_scene load_steps=27 format=3 uid="uid://bqfpidewtpeg0"] - -[ext_resource type="Script" uid="uid://dalle60ox3sat" path="res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd" id="1"] - -[sub_resource type="Image" id="Image_466oo"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 223, 224, 224, 224, 148, 228, 228, 228, 28, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 211, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 221, 229, 229, 229, 29, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 65, 229, 229, 229, 29, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 1, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 229, 229, 229, 39, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[gd_scene format=3 uid="uid://bqfpidewtpeg0"] + +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd" id="1"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/menu/GdUnitInspectorContextMenu.tscn" id="2_o6s0p"] + +[sub_resource type="DPITexture" id="DPITexture_miuuy"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_nwpuj"] -image = SubResource("Image_466oo") - -[sub_resource type="Image" id="Image_o6s0p"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 224, 224, 224, 48, 224, 224, 224, 217, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 228, 228, 228, 47, 224, 224, 224, 236, 224, 224, 224, 255, 225, 225, 225, 125, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 86, 224, 224, 224, 252, 224, 224, 224, 252, 224, 224, 224, 194, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 225, 225, 225, 25, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_ern2r"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_pdcj5"] -image = SubResource("Image_o6s0p") - -[sub_resource type="Image" id="Image_miuuy"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 89, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 200, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 233, 224, 224, 224, 255, 225, 225, 225, 124, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 211, 238, 238, 238, 15, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_qdci2"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_o41n3"] -image = SubResource("Image_miuuy") - -[sub_resource type="Image" id="Image_ern2r"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 101, 224, 224, 224, 49, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 238, 224, 224, 224, 49, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 209, 255, 255, 255, 6, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 112, 230, 230, 230, 10, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_hed0i"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_6oiqe"] -image = SubResource("Image_ern2r") - -[sub_resource type="Image" id="Image_qdci2"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 226, 226, 226, 52, 225, 225, 225, 101, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 53, 224, 224, 224, 239, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 7, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 232, 232, 232, 11, 224, 224, 224, 113, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_8v04w"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_l0amb"] -image = SubResource("Image_qdci2") +[sub_resource type="DPITexture" id="DPITexture_arwmg"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} -[sub_resource type="Image" id="Image_hed0i"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 1, 255, 255, 255, 1, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 202, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 85, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 231, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 224, 224, 224, 212, 229, 229, 229, 39, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 225, 225, 225, 25, 230, 230, 230, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_rqglq"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_nonnc"] -image = SubResource("Image_hed0i") +[sub_resource type="DPITexture" id="DPITexture_ltb1l"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} -[sub_resource type="Image" id="Image_8v04w"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 26, 224, 224, 224, 41, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 19, 224, 224, 224, 218, 227, 227, 227, 45, 255, 255, 255, 0, 227, 227, 227, 9, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 234, 227, 227, 227, 45, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 202, 224, 224, 224, 252, 224, 224, 224, 252, 224, 224, 224, 82, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 40, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_2lq8w"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_d2btj"] -image = SubResource("Image_8v04w") +[sub_resource type="DPITexture" id="DPITexture_kwwmp"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} -[sub_resource type="Image" id="Image_arwmg"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 230, 230, 230, 30, 225, 225, 225, 149, 224, 224, 224, 221, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 41, 226, 226, 226, 26, 192, 192, 192, 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, 7, 224, 224, 224, 214, 224, 224, 224, 255, 224, 224, 224, 253, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 231, 231, 231, 31, 224, 224, 224, 224, 224, 224, 224, 252, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 51, 223, 223, 223, 47, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 226, 226, 226, 43, 227, 227, 227, 9, 255, 255, 255, 0, 224, 224, 224, 32, 227, 227, 227, 63, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 17, 255, 255, 255, 8, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 192, 192, 192, 4, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 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, 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, 224, 224, 224, 41, 225, 225, 225, 51, 225, 225, 225, 51, 225, 225, 225, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 225, 225, 225, 51, 225, 225, 225, 51, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 51, 223, 223, 223, 47, 255, 255, 255, 8, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 9, 223, 223, 223, 47, 225, 225, 225, 51, 225, 225, 225, 25, 255, 255, 255, 0, 255, 255, 255, 0, 192, 192, 192, 4, 226, 226, 226, 43, 224, 224, 224, 8, 255, 255, 255, 0, 227, 227, 227, 9, 227, 227, 227, 18, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 18, 227, 227, 227, 9, 255, 255, 255, 0, 227, 227, 227, 9, 226, 226, 226, 43, 255, 255, 255, 3, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 0, 227, 227, 227, 9, 228, 228, 228, 47, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 223, 223, 223, 47, 227, 227, 227, 9, 255, 255, 255, 0, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 43, 225, 225, 225, 51, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 1, 225, 225, 225, 51, 225, 225, 225, 51, 226, 226, 226, 43, 255, 255, 255, 2, 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, 192, 192, 192, 4, 226, 226, 226, 26, 224, 224, 224, 40, 255, 255, 255, 0, 255, 255, 255, 1, 229, 229, 229, 39, 225, 225, 225, 25, 255, 255, 255, 3, 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, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 +[sub_resource type="DPITexture" id="DPITexture_t1rin"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) } -[sub_resource type="ImageTexture" id="ImageTexture_1bxo7"] -image = SubResource("Image_arwmg") +[sub_resource type="DPITexture" id="DPITexture_pi1kv"] +_source = " +" +saturation = 2.0 +color_map = { +Color(1, 0.37254903, 0.37254903, 1): Color(1, 0.47, 0.42, 1), +Color(0.37254903, 1, 0.5921569, 1): Color(0.45, 0.95, 0.5, 1), +Color(1, 0.8666667, 0.39607844, 1): Color(0.83, 0.78, 0.62, 1) +} -[sub_resource type="AnimatedTexture" id="AnimatedTexture_eylo1"] +[sub_resource type="AnimatedTexture" id="AnimatedTexture_eor1x"] frames = 8 speed_scale = 2.5 -frame_0/texture = SubResource("ImageTexture_nwpuj") +frame_0/texture = SubResource("DPITexture_8v04w") frame_0/duration = 0.2 -frame_1/texture = SubResource("ImageTexture_pdcj5") +frame_1/texture = SubResource("DPITexture_arwmg") frame_1/duration = 0.2 -frame_2/texture = SubResource("ImageTexture_o41n3") +frame_2/texture = SubResource("DPITexture_rqglq") frame_2/duration = 0.2 -frame_3/texture = SubResource("ImageTexture_6oiqe") +frame_3/texture = SubResource("DPITexture_ltb1l") frame_3/duration = 0.2 -frame_4/texture = SubResource("ImageTexture_l0amb") +frame_4/texture = SubResource("DPITexture_2lq8w") frame_4/duration = 0.2 -frame_5/texture = SubResource("ImageTexture_nonnc") +frame_5/texture = SubResource("DPITexture_kwwmp") frame_5/duration = 0.2 -frame_6/texture = SubResource("ImageTexture_d2btj") +frame_6/texture = SubResource("DPITexture_t1rin") frame_6/duration = 0.2 -frame_7/texture = SubResource("ImageTexture_1bxo7") +frame_7/texture = SubResource("DPITexture_pi1kv") frame_7/duration = 0.2 -[sub_resource type="Image" id="Image_rqglq"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 195, 224, 224, 224, 210, 224, 224, 224, 56, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 56, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 139, 224, 224, 224, 8, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 182, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 212, 226, 226, 226, 52, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 134, 255, 255, 255, 6, 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, 225, 225, 225, 191, 224, 224, 224, 206, 226, 226, 226, 52, 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, 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, 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, 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_dr7yj"] -image = SubResource("Image_rqglq") - -[sub_resource type="Image" id="Image_ltb1l"] -data = { -"data": PackedByteArray(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, 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, 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, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 202, 228, 228, 228, 37, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 239, 224, 224, 224, 74, 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, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 123, 255, 255, 255, 1, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 188, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 185, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 168, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 252, 225, 225, 225, 118, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 237, 226, 226, 226, 70, 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, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 188, 224, 224, 224, 201, 225, 225, 225, 34, 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, 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, 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_oh8cr"] -image = SubResource("Image_ltb1l") - -[sub_resource type="Image" id="Image_2lq8w"] -data = { -"data": PackedByteArray(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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 196, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 24, 226, 226, 226, 60, 226, 226, 226, 60, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 60, 226, 226, 226, 60, 233, 233, 233, 23, 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, 5, 225, 225, 225, 134, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 254, 225, 225, 225, 133, 255, 255, 255, 5, 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, 225, 225, 225, 67, 224, 224, 224, 231, 224, 224, 224, 230, 224, 224, 224, 66, 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, 231, 231, 231, 21, 231, 231, 231, 21, 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, 227, 227, 227, 71, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 224, 224, 224, 236, 225, 225, 225, 67, 226, 226, 226, 69, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 232, 224, 224, 224, 66, 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, 231, 231, 231, 21, 230, 230, 230, 20, 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, 224, 224, 224, 65, 224, 224, 224, 229, 224, 224, 224, 229, 224, 224, 224, 64, 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, 4, 224, 224, 224, 132, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 130, 255, 255, 255, 3, 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, 224, 224, 224, 24, 224, 224, 224, 64, 224, 224, 224, 64, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 64, 224, 224, 224, 64, 233, 233, 233, 23, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 200, 224, 224, 224, 200, 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, 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_x1ivs"] -image = SubResource("Image_2lq8w") - -[sub_resource type="Image" id="Image_kwwmp"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 237, 237, 14, 224, 224, 224, 165, 224, 224, 224, 165, 237, 237, 237, 14, 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, 225, 225, 225, 58, 225, 225, 225, 223, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 225, 225, 225, 58, 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, 233, 233, 233, 23, 225, 225, 225, 124, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 225, 225, 225, 124, 233, 233, 233, 23, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 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, 224, 224, 224, 255, 224, 224, 224, 255, 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, 233, 233, 233, 23, 225, 225, 225, 125, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 128, 225, 225, 225, 125, 233, 233, 233, 23, 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, 225, 225, 225, 59, 224, 224, 224, 224, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 225, 225, 225, 59, 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, 238, 238, 238, 15, 224, 224, 224, 165, 224, 224, 224, 165, 238, 238, 238, 15, 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_i13wr"] -image = SubResource("Image_kwwmp") - -[node name="MainPanel" type="VSplitContainer"] +[node name="MainPanel" type="VSplitContainer" unique_id=1044515559] use_parent_material = true clip_contents = true anchors_preset = 15 @@ -177,33 +154,44 @@ grow_horizontal = 2 grow_vertical = 2 size_flags_horizontal = 3 size_flags_vertical = 3 +split_offsets = PackedInt32Array(200) split_offset = 200 script = ExtResource("1") -[node name="Panel" type="PanelContainer" parent="."] +[node name="contextMenu" parent="." unique_id=73764904 instance=ExtResource("2_o6s0p")] +visible = false +item_0/icon = SubResource("DPITexture_miuuy") +item_1/icon = SubResource("DPITexture_ern2r") +item_2/text = "Run Tests Until Fail" +item_2/icon = SubResource("DPITexture_miuuy") +item_4/icon = SubResource("DPITexture_qdci2") +item_5/icon = SubResource("DPITexture_hed0i") + +[node name="Panel" type="PanelContainer" parent="." unique_id=641912870] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 -[node name="Tree" type="Tree" parent="Panel"] +[node name="Tree" type="Tree" parent="Panel" unique_id=741772884] use_parent_material = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 theme_override_constants/icon_max_width = 16 columns = 2 +allow_reselect = true allow_rmb_select = true hide_root = true select_mode = 1 -[node name="discover_hint" type="HBoxContainer" parent="Panel"] +[node name="discover_hint" type="HBoxContainer" parent="Panel" unique_id=555683311] unique_name_in_owner = true visible = false use_parent_material = true layout_mode = 2 alignment = 1 -[node name="spinner" type="Button" parent="Panel/discover_hint"] +[node name="spinner" type="Button" parent="Panel/discover_hint" unique_id=200472082] unique_name_in_owner = true clip_contents = true custom_minimum_size = Vector2(64, 64) @@ -212,62 +200,41 @@ size_flags_stretch_ratio = 1.94 disabled = true button_mask = 0 text = "Discover Tests" -icon = SubResource("AnimatedTexture_eylo1") +icon = SubResource("AnimatedTexture_eor1x") flat = true alignment = 2 -[node name="report" type="PanelContainer" parent="."] +[node name="report" type="PanelContainer" parent="." unique_id=503703488] clip_contents = true layout_mode = 2 size_flags_horizontal = 11 size_flags_vertical = 11 -[node name="report_template" type="RichTextLabel" parent="report"] +[node name="report_template" type="RichTextLabel" parent="report" unique_id=817912164] +auto_translate_mode = 2 use_parent_material = true clip_contents = false layout_mode = 2 size_flags_horizontal = 3 -auto_translate = false localize_numeral_system = false focus_mode = 2 bbcode_enabled = true fit_content = true selection_enabled = true -[node name="ScrollContainer" type="ScrollContainer" parent="report"] +[node name="ScrollContainer" type="ScrollContainer" parent="report" unique_id=1479075289] use_parent_material = true custom_minimum_size = Vector2(0, 80) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 11 -[node name="list" type="VBoxContainer" parent="report/ScrollContainer"] +[node name="list" type="VBoxContainer" parent="report/ScrollContainer" unique_id=1104634565] clip_contents = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 -[node name="contextMenu" type="PopupMenu" parent="."] -size = Vector2i(133, 120) -auto_translate = false -item_count = 5 -item_0/text = "Run" -item_0/icon = SubResource("ImageTexture_dr7yj") -item_0/id = 0 -item_1/text = "Debug" -item_1/icon = SubResource("ImageTexture_oh8cr") -item_1/id = 1 -item_2/text = "" -item_2/id = 2 -item_2/separator = true -item_3/text = "Collapse All" -item_3/icon = SubResource("ImageTexture_x1ivs") -item_3/id = 3 -item_4/text = "Expand All" -item_4/icon = SubResource("ImageTexture_i13wr") -item_4/id = 4 - [connection signal="item_activated" from="Panel/Tree" to="." method="_on_Tree_item_activated"] [connection signal="item_mouse_selected" from="Panel/Tree" to="." method="_on_tree_item_mouse_selected"] [connection signal="item_selected" from="Panel/Tree" to="." method="_on_Tree_item_selected"] -[connection signal="index_pressed" from="contextMenu" to="." method="_on_context_m_index_pressed"] diff --git a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid index 8b2dd786..1cbe1be9 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid +++ b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid @@ -1 +1 @@ -uid://djjujnonaiy34 +uid://8ts5sxccg0or diff --git a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn index 40bdb502..7e62c438 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn +++ b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://pmnkxrhglak5"] -[ext_resource type="Script" uid="uid://birwkunpkmqc4" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd" id="1_gki1u"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd" id="1_gki1u"] [node name="GdUnitInputMapper" type="Control"] modulate = Color(0.929099, 0.929099, 0.929099, 0.936189) diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd index 4a9a9465..14a8bd5b 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd @@ -10,15 +10,15 @@ const GdUnitUpdateClient = preload ("res://addons/gdUnit4/src/update/GdUnitUpdat @onready var _btn_install: Button = %btn_install_examples @onready var _progress_bar: ProgressBar = %ProgressBar @onready var _progress_text: Label = %progress_lbl -@onready var _properties_template: Node = $property_template -@onready var _properties_common: Node = % "common-content" -@onready var _properties_ui: Node = % "ui-content" -@onready var _properties_shortcuts: Node = % "shortcut-content" -@onready var _properties_report: Node = % "report-content" +@onready var _properties_template: Control = $property_template +@onready var _properties_common: Control = % "common-content" +@onready var _properties_ui: Control = % "ui-content" +@onready var _properties_shortcuts: Control = % "shortcut-content" +@onready var _properties_report: Control = % "report-content" @onready var _input_capture: GdUnitInputCapture = %GdUnitInputCapture @onready var _property_error: Window = % "propertyError" @onready var _tab_container: TabContainer = %Properties -@onready var _update_tab: = %Update +@onready var _update_tab: Control = %Update var _font_size: float @@ -41,7 +41,7 @@ func _sort_by_key(left: GdUnitProperty, right: GdUnitProperty) -> bool: return left.name() < right.name() -func setup_properties(properties_parent: Node, property_category: String) -> void: +func setup_properties(properties_parent: Control, property_category: String) -> void: # Do remove first potential previous added properties (could be happened when the dlg is opened at twice) for child in properties_parent.get_children(): properties_parent.remove_child(child) @@ -66,8 +66,9 @@ func setup_properties(properties_parent: Node, property_category: String) -> voi grid.columns = 4 grid.theme = theme_ - var sub_category: Node = _properties_template.get_child(3).duplicate() - sub_category.get_child(0).text = current_category.capitalize() + var sub_category: Control = _properties_template.get_child(3).duplicate() + var category_label: Label = sub_category.get_child(0) + category_label.text = current_category.capitalize() sub_category.custom_minimum_size.y = _font_size + 16 properties_parent.add_child(sub_category) properties_parent.add_child(grid) @@ -91,7 +92,7 @@ func setup_properties(properties_parent: Node, property_category: String) -> voi @warning_ignore("return_value_discarded") reset_btn.pressed.connect(_on_btn_property_reset_pressed.bind(property, input, reset_btn)) # property help text - var info: Node = _properties_template.get_child(2).duplicate() + var info: Label = _properties_template.get_child(2).duplicate() info.text = property.help() info_labels.append(info) grid.add_child(info) @@ -106,7 +107,6 @@ func setup_properties(properties_parent: Node, property_category: String) -> voi properties_parent.custom_minimum_size.x = min_size_overall -@warning_ignore("return_value_discarded") func _create_input_element(property: GdUnitProperty, reset_btn: Button) -> Node: if property.is_selectable_value(): var options := OptionButton.new() @@ -114,7 +114,7 @@ func _create_input_element(property: GdUnitProperty, reset_btn: Button) -> Node: for value in property.value_set(): options.add_item(value) options.item_selected.connect(_on_option_selected.bind(property, reset_btn)) - options.select(property.value()) + options.select(property.int_value()) return options if property.type() == TYPE_BOOL: var check_btn := CheckButton.new() @@ -131,7 +131,8 @@ func _create_input_element(property: GdUnitProperty, reset_btn: Button) -> Node: return input if property.type() == TYPE_PACKED_INT32_ARRAY: var key_input_button := Button.new() - key_input_button.text = to_shortcut(property.value()) + var value:PackedInt32Array = property.value() + key_input_button.text = to_shortcut(value) key_input_button.pressed.connect(_on_shortcut_change.bind(key_input_button, property, reset_btn)) return key_input_button return Control.new() @@ -150,7 +151,6 @@ func to_shortcut(keys: PackedInt32Array) -> String: return input_event.as_text() -@warning_ignore("return_value_discarded") func to_keys(input_event: InputEventKey) -> PackedInt32Array: var keys := PackedInt32Array() if input_event.ctrl_pressed: @@ -184,8 +184,8 @@ func _install_examples() -> void: var tmp_path := GdUnitFileAccess.create_temp_dir("download") var zip_file := tmp_path + "/examples.zip" var response: GdUnitUpdateClient.HttpResponse = await _update_client.request_zip_package(EAXAMPLE_URL, zip_file) - if response.code() != 200: - push_warning("Examples cannot be retrieved from GitHub! \n Error code: %d : %s" % [response.code(), response.response()]) + if response.status() != 200: + push_warning("Examples cannot be retrieved from GitHub! \n Error code: %d : %s" % [response.status(), response.response()]) update_progress("Install examples failed! Try it later again.") await get_tree().create_timer(3).timeout stop_progress() @@ -199,20 +199,28 @@ func _install_examples() -> void: stop_progress() return update_progress("Refresh project") - await rescan(true) + await rescan() + await reimport("res://gdUnit4-examples/") + update_progress("Examples successfully installed") await get_tree().create_timer(3).timeout stop_progress() -func rescan(update_scripts:=false) -> void: - await get_tree().idle_frame +func rescan() -> void: + await get_tree().process_frame var fs := EditorInterface.get_resource_filesystem() fs.scan_sources() while fs.is_scanning(): await get_tree().create_timer(1).timeout - if update_scripts: - EditorInterface.get_resource_filesystem().update_script_classes() + + +func reimport(path: String) -> void: + await get_tree().process_frame + var files := DirAccess.get_files_at(path) + EditorInterface.get_resource_filesystem().reimport_files(files) + for directory in DirAccess.get_directories_at(path): + reimport(directory) func check_for_update() -> void: @@ -252,17 +260,19 @@ func _on_btn_close_pressed() -> void: func _on_btn_property_reset_pressed(property: GdUnitProperty, input: Node, reset_btn: Button) -> void: if input is CheckButton: - input.button_pressed = property.default() + var is_default_pressed: bool = property.default() + (input as CheckButton).button_pressed = is_default_pressed elif input is LineEdit: - input.text = str(property.default()) + (input as LineEdit).text = str(property.default()) # we have to update manually for text input fields because of no change event is emited _on_property_text_changed(property.default(), property, reset_btn) elif input is OptionButton: - input.select(0) + (input as OptionButton).select(0) _on_option_selected(0, property, reset_btn) elif input is Button: - input.text = to_shortcut(property.default()) - _on_property_text_changed(property.default(), property, reset_btn) + var value: PackedInt32Array = property.default() + (input as Button).text = to_shortcut(value) + _on_property_text_changed(value, property, reset_btn) func _on_property_text_changed(new_value: Variant, property: GdUnitProperty, reset_btn: Button) -> void: @@ -271,7 +281,7 @@ func _on_property_text_changed(new_value: Variant, property: GdUnitProperty, res var error: Variant = GdUnitSettings.update_property(property) if error: var label: Label = _property_error.get_child(0) as Label - label.set_text(error) + label.set_text(str(error)) var control := gui_get_focus_owner() _property_error.show() if control != null: diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid index 11e3f9e3..01e0f858 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid @@ -1 +1 @@ -uid://bbmloatmyvou5 +uid://b3ri7v3r70h8t diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn index 96f1c0b0..08afee43 100644 --- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn @@ -1,11 +1,12 @@ -[gd_scene load_steps=8 format=3 uid="uid://dwgat6j2u77g4"] +[gd_scene load_steps=9 format=3] -[ext_resource type="Script" uid="uid://dgoxpc1orxs08" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd" id="2"] -[ext_resource type="Texture2D" uid="uid://bkh022wwqq7s3" path="res://addons/gdUnit4/src/ui/settings/logo.png" id="3_isfyl"] -[ext_resource type="PackedScene" uid="uid://dte0m2endcgtu" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn" id="4"] -[ext_resource type="PackedScene" uid="uid://0xyeci1tqebj" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn" id="5_n1jtv"] -[ext_resource type="PackedScene" uid="uid://pmnkxrhglak5" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn" id="5_xu3j8"] -[ext_resource type="Script" uid="uid://ogva4spkrtij" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="8_2ggr0"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd" id="2"] +[ext_resource type="Texture2D" path="res://addons/gdUnit4/src/ui/settings/logo.png" id="3_isfyl"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn" id="4"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn" id="4_nf72w"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn" id="5_n1jtv"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn" id="5_xu3j8"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="8_2ggr0"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hbbq5"] content_margin_left = 10.0 @@ -226,10 +227,14 @@ layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 +[node name="Hooks" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("4_nf72w")] +visible = false +layout_mode = 2 + [node name="UI" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 1 +metadata/_tab_index = 2 [node name="ui-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/UI"] unique_name_in_owner = true @@ -242,7 +247,7 @@ size_flags_vertical = 3 [node name="Shortcuts" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 2 +metadata/_tab_index = 3 [node name="shortcut-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Shortcuts"] unique_name_in_owner = true @@ -255,7 +260,7 @@ size_flags_vertical = 3 [node name="Report" type="ScrollContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties"] visible = false layout_mode = 2 -metadata/_tab_index = 3 +metadata/_tab_index = 4 [node name="report-content" type="VBoxContainer" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties/Report"] unique_name_in_owner = true @@ -268,13 +273,13 @@ size_flags_vertical = 3 [node name="Templates" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("4")] visible = false layout_mode = 2 -metadata/_tab_index = 4 +metadata/_tab_index = 5 [node name="Update" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("5_n1jtv")] unique_name_in_owner = true visible = false layout_mode = 2 -metadata/_tab_index = 5 +metadata/_tab_index = 6 [node name="GdUnitInputCapture" parent="Panel/PanelContainer/v/MarginContainer/GridContainer/Properties" instance=ExtResource("5_xu3j8")] unique_name_in_owner = true diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd new file mode 100644 index 00000000..3b01f340 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd @@ -0,0 +1,255 @@ +@tool +extends ScrollContainer + + +@onready var _hooks_tree: Tree = %hooks_tree +@onready var _hook_description: RichTextLabel = %hook_description +@onready var _btn_move_up: Button = %hook_actions/btn_move_up +@onready var _btn_move_down: Button = %hook_actions/btn_move_down +@onready var _btn_delete: Button = %hook_actions/btn_delete_hook +@onready var _select_hook_dlg: FileDialog = %select_hook_dlg +@onready var _error_msg_popup :AcceptDialog = %error_msg_popup + +var _selected_hook_item: TreeItem = null +var _root: TreeItem +var _system_box_style: StyleBoxFlat +var _priority_box_style: StyleBoxFlat + +func _ready() -> void: + _setup_styles() + _setup_buttons() + _setup_tree() + _load_registered_hooks() + + +func _setup_styles() -> void: + _system_box_style = StyleBoxFlat.new() + _system_box_style.bg_color = Color(1.0, 0.76, 0.03, 1) + _system_box_style.corner_radius_top_left = 6 + _system_box_style.corner_radius_top_right = 6 + _system_box_style.corner_radius_bottom_left = 6 + _system_box_style.corner_radius_bottom_right = 6 + _priority_box_style = _system_box_style.duplicate() + _priority_box_style.bg_color = Color(0.26, 0.54, 0.89, 1) + + +func _setup_buttons() -> void: + #if Engine.is_editor_hint(): + # _btn_move_up.icon = GdUnitUiTools.get_icon("MoveUp") + # _btn_move_down.icon = GdUnitUiTools.get_icon("MoveDown") + # _btn_add.icon = GdUnitUiTools.get_icon("Add") + # _btn_delete.icon = GdUnitUiTools.get_icon("Remove") + pass + + +func _setup_tree() -> void: + _hooks_tree.clear() + _root = _hooks_tree.create_item() + _hooks_tree.set_columns(2) + _hooks_tree.set_column_custom_minimum_width(1, 32) + _hooks_tree.set_column_expand(1, false) + _hooks_tree.set_hide_root(true) + _hooks_tree.set_hide_folding(true) + _hooks_tree.set_select_mode(Tree.SELECT_SINGLE) + _hooks_tree.item_selected.connect(_on_hook_selected) + _hooks_tree.item_edited.connect(_on_item_edited) + + +func _load_registered_hooks() -> void: + var hook_service := GdUnitTestSessionHookService.instance() + for hook: GdUnitTestSessionHook in hook_service.enigne_hooks: + _create_hook_tree_item(hook) + + # Select first item if any + if _root.get_child_count() > 0: + var first_item: TreeItem = _root.get_first_child() + first_item.select(0) + _on_hook_selected() + + +func _create_hook_tree_item(hook: GdUnitTestSessionHook) -> TreeItem: + var item: TreeItem = _hooks_tree.create_item(_root) + item.set_custom_minimum_height(26) + # Column 0: Hook info with custom drawing + item.set_cell_mode(0, TreeItem.CELL_MODE_CUSTOM) + item.set_custom_draw_callback(0, _draw_hook_item) + item.set_editable(0, false) + item.set_metadata(0, hook) + # Column 1: Checkbox for enable/disable + item.set_cell_mode(1, TreeItem.CELL_MODE_CHECK) + item.set_checked(1, GdUnitTestSessionHookService.is_enabled(hook)) + item.set_editable(1, true) + item.set_custom_bg_color(1, _hook_bg_color(hook)) + item.set_tooltip_text(1, "Enable/Disable the Hook") + item.propagate_check(1) + + if _is_system_hook(hook): + item.set_tooltip_text(0, "System hook - (Read-only)") + else: + item.set_tooltip_text(0, "User hook") + return item + + +func _hook_bg_color(hook: GdUnitTestSessionHook) -> Color: + if _is_system_hook(hook): + return Color(0.133, 0.118, 0.090, 1) # Brownish background for system hooks + return Color(0.176, 0.196, 0.235, 1) # Dark background #2d3142 + + +func _draw_hook_item(item: TreeItem, rect: Rect2) -> void: + var hook := _get_hook(item) + var is_system := _is_system_hook(hook) + var is_selected := item == _selected_hook_item + + # Draw background + var bg_color := _hook_bg_color(hook) # Dark background #2d3142 + if is_selected: + bg_color = bg_color.lerp(Color(0.2, 0.4, 0.6, 0.3), 0.5) # Blue tint for selection + _hooks_tree.draw_rect(rect, bg_color) + + # Draw left border for system hooks + if is_system: + var border_rect := Rect2(rect.position.x, rect.position.y, 3, rect.size.y) + _hooks_tree.draw_rect(border_rect, Color(1.0, 0.76, 0.03, 1)) # Yellow border + + var font := _hooks_tree.get_theme_default_font() + + # Draw hook name + var hook_name := hook.name + var text_pos := Vector2(rect.position.x + ( 15 if is_system else 12), rect.position.y + 18) + var text_color := Color(0.95, 0.95, 0.95, 1) + _hooks_tree.draw_string(font, text_pos, hook_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 14, text_color) + + # Draw system badge if needed + if is_system: + var badge_x := rect.position.x + rect.end.x - 100 + var badge_y := rect.position.y + 14 + var system_badge_rect := Rect2(badge_x, badge_y-8, 48, 16) + _hooks_tree.draw_style_box(_system_box_style, system_badge_rect) + + var system_text_pos := Vector2(badge_x + 4, badge_y + 4) + var system_font_size := 10 + _hooks_tree.draw_string(font, system_text_pos, "SYSTEM", HORIZONTAL_ALIGNMENT_CENTER, -1, system_font_size, Color(0.1, 0.1, 0.1, 1)) + + +func _create_hook_display_text(hook_name: String, priority: int, is_system: bool) -> String: + var text := hook_name + "\n" + text += "Priority: [color=#4299e1][bgcolor=#4299e1] " + str(priority) + " [/bgcolor][/color]" + + if is_system: + text += " [color=#1a202c][bgcolor=#ffc107] SYSTEM [/bgcolor][/color]" + + return text + + +func _update_hook_description() -> void: + if _selected_hook_item == null: + _hook_description.text = "[i]Select a hook to view its description[/i]" + return + _hook_description.text = _get_hook(_selected_hook_item).description + + +func _update_hook_buttons() -> void: + # Is nothing selected disable the move and delete buttons + if _selected_hook_item == null: + _btn_move_up.disabled = true + _btn_move_down.disabled = true + _btn_delete.disabled = true + return + + var hook := _get_hook(_selected_hook_item) + var is_system := _is_system_hook(hook) + + # Disable the move and delete buttons for system hooks by default + if is_system: + _btn_move_up.disabled = true + _btn_move_down.disabled = true + _btn_delete.disabled = true + return + + var prev_item: TreeItem = _selected_hook_item.get_prev() + var next_item: TreeItem = _selected_hook_item.get_next() + + if prev_item != null: + var prev_hook := _get_hook(prev_item) + _btn_move_up.disabled = _is_system_hook(prev_hook) + + _btn_move_down.disabled = next_item == null + _btn_delete.disabled = false + + +static func _get_hook(item: TreeItem) -> GdUnitTestSessionHook: + return item.get_metadata(0) + + +static func _is_system_hook(hook: GdUnitTestSessionHook) -> bool: + if hook == null: + return false + return hook.get_meta("SYSTEM_HOOK") + + +func _on_hook_selected() -> void: + _selected_hook_item = _hooks_tree.get_selected() + _update_hook_buttons() + _update_hook_description() + + +func _on_item_edited() -> void: + var selected_hook_item := _hooks_tree.get_selected() + if selected_hook_item != null: + var hook := _get_hook(selected_hook_item) + var is_enabled := selected_hook_item.is_checked(1) + GdUnitTestSessionHookService.instance().enable_hook(hook, is_enabled) + + +func _on_btn_add_hook_pressed() -> void: + _select_hook_dlg.show() + + +func _on_select_hook_dlg_file_selected(path: String) -> void: + _select_hook_dlg.set_current_path(path) + _on_select_hook_dlg_confirmed() + + +func _on_select_hook_dlg_confirmed() -> void: + _select_hook_dlg.hide() + var result := GdUnitTestSessionHookService.instance().load_hook(_select_hook_dlg.get_current_path()) + if result.is_error(): + _error_msg_popup.dialog_text = result.error_message() + _error_msg_popup.show() + return + + var hook: GdUnitTestSessionHook = result.value() + result = GdUnitTestSessionHookService.instance().register(hook) + if result.is_error(): + _error_msg_popup.dialog_text = result.error_message() + _error_msg_popup.show() + return + + var hook_added := _create_hook_tree_item(hook) + _hooks_tree.set_selected(hook_added, 0) + + +func _on_btn_delete_hook_pressed() -> void: + if _selected_hook_item != null: + _root.remove_child(_selected_hook_item) + GdUnitTestSessionHookService.instance()\ + .unregister(_get_hook(_selected_hook_item)) + _selected_hook_item = null + _update_hook_buttons() + + +func _on_btn_move_up_pressed() -> void: + var prev := _selected_hook_item.get_prev() + _selected_hook_item.move_before(prev) + GdUnitTestSessionHookService.instance()\ + .move_before(_get_hook(_selected_hook_item), _get_hook(prev)) + _update_hook_buttons() + + +func _on_btn_move_down_pressed() -> void: + var next := _selected_hook_item.get_next() + _selected_hook_item.move_after(next) + GdUnitTestSessionHookService.instance()\ + .move_after(_get_hook(_selected_hook_item), _get_hook(next)) + _update_hook_buttons() diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid new file mode 100644 index 00000000..0eae8aa9 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd.uid @@ -0,0 +1 @@ +uid://4fmcrhmge0lj diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn new file mode 100644 index 00000000..a9a850c0 --- /dev/null +++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.tscn @@ -0,0 +1,148 @@ +[gd_scene load_steps=10 format=3 uid="uid://41l7a46fol5m"] + +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/settings/GdUnitSettingsTabHooks.gd" id="1_8yffn"] + +[sub_resource type="Image" id="Image_h5sr5"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 255, 224, 224, 224, 255, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 225, 225, 225, 174, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 173, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 74, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 37, 224, 224, 224, 240, 224, 224, 224, 255, 224, 224, 224, 122, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 239, 228, 228, 228, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 200, 224, 224, 224, 255, 224, 224, 224, 172, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 1, 224, 224, 224, 173, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 193, 234, 234, 234, 12, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 193, 224, 224, 224, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_77fm0"] +image = SubResource("Image_h5sr5") + +[sub_resource type="Image" id="Image_77fm0"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 180, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 181, 224, 224, 224, 193, 234, 234, 234, 12, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 193, 224, 224, 224, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 200, 224, 224, 224, 255, 224, 224, 224, 173, 255, 255, 255, 1, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 1, 225, 225, 225, 174, 224, 224, 224, 255, 225, 225, 225, 199, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 228, 228, 37, 224, 224, 224, 239, 224, 224, 224, 255, 224, 224, 224, 122, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 239, 227, 227, 227, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 74, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 224, 224, 224, 253, 224, 224, 224, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 123, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 1, 224, 224, 224, 173, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 234, 234, 12, 224, 224, 224, 255, 224, 224, 224, 255, 234, 234, 234, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_rewru"] +image = SubResource("Image_77fm0") + +[sub_resource type="Image" id="Image_kppp6"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_manhx"] +image = SubResource("Image_kppp6") + +[sub_resource type="Image" id="Image_rewru"] +data = { +"data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 227, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 224, 224, 73, 224, 224, 224, 226, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 225, 226, 226, 226, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), +"format": "RGBA8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id="ImageTexture_4h4u1"] +image = SubResource("Image_rewru") + +[node name="Hooks" type="ScrollContainer"] +custom_minimum_size = Vector2(400, 300) +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_8yffn") +metadata/_tab_index = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="hooks_content" type="VBoxContainer" parent="HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="hooks_tree" type="Tree" parent="HBoxContainer/hooks_content"] +unique_name_in_owner = true +layout_direction = 2 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +hide_folding = true +hide_root = true + +[node name="hook_description" type="RichTextLabel" parent="HBoxContainer/hooks_content"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 120) +layout_mode = 2 +size_flags_vertical = 2 +bbcode_enabled = true +text = "The test result Html reporting hook." +scroll_active = false + +[node name="hook_actions" type="VBoxContainer" parent="HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +size_flags_horizontal = 0 +theme_override_constants/separation = 5 + +[node name="btn_move_up" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Move hook up in priority" +disabled = true +icon = SubResource("ImageTexture_77fm0") +icon_alignment = 1 + +[node name="btn_move_down" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Move hook down in priority" +disabled = true +icon = SubResource("ImageTexture_rewru") +icon_alignment = 1 + +[node name="btn_add_hook" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Add new hook" +icon = SubResource("ImageTexture_manhx") +icon_alignment = 1 + +[node name="btn_delete_hook" type="Button" parent="HBoxContainer/hook_actions"] +layout_mode = 2 +tooltip_text = "Delete selected hook" +disabled = true +icon = SubResource("ImageTexture_4h4u1") +icon_alignment = 1 + +[node name="select_hook_dlg" type="FileDialog" parent="."] +unique_name_in_owner = true +disable_3d = true +title = "Open a File" +initial_position = 3 +current_screen = 0 +ok_button_text = "Open" +file_mode = 0 +filters = PackedStringArray("*.gd") + +[node name="error_msg_popup" type="AcceptDialog" parent="."] +unique_name_in_owner = true +initial_position = 3 +current_screen = 0 + +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_move_up" to="." method="_on_btn_move_up_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_move_down" to="." method="_on_btn_move_down_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_add_hook" to="." method="_on_btn_add_hook_pressed"] +[connection signal="pressed" from="HBoxContainer/hook_actions/btn_delete_hook" to="." method="_on_btn_delete_hook_pressed"] +[connection signal="confirmed" from="select_hook_dlg" to="." method="_on_select_hook_dlg_confirmed"] +[connection signal="file_selected" from="select_hook_dlg" to="." method="_on_select_hook_dlg_file_selected"] diff --git a/addons/gdUnit4/src/ui/settings/logo.png.import b/addons/gdUnit4/src/ui/settings/logo.png.import index c2a071e8..6da467fd 100644 --- a/addons/gdUnit4/src/ui/settings/logo.png.import +++ b/addons/gdUnit4/src/ui/settings/logo.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://bkh022wwqq7s3" +uid="uid://bawie27f8x4bx" path="res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo.png-deda0e4ba02a0b9e4e4a830029a5817f.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid index 94cc662d..f9a4cf48 100644 --- a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid +++ b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid @@ -1 +1 @@ -uid://b8ypatydap4v6 +uid://c20py4hvwse1l diff --git a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn index f0667923..5ccc4430 100644 --- a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn +++ b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://dte0m2endcgtu"] -[ext_resource type="Script" uid="uid://d6ayx5x8dpnw" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd" id="1"] [node name="TestSuiteTemplate" type="MarginContainer"] anchors_preset = 15 diff --git a/addons/gdUnit4/src/update/GdMarkDownReader.gd b/addons/gdUnit4/src/update/GdMarkDownReader.gd index ce6885de..dab19e40 100644 --- a/addons/gdUnit4/src/update/GdMarkDownReader.gd +++ b/addons/gdUnit4/src/update/GdMarkDownReader.gd @@ -215,7 +215,7 @@ func process_tables(input: String) -> String: return "\n".join(bbcode) -class Table: +class GdUnitMDReaderTable: var _columns: int var _rows: Array[Row] = [] @@ -292,7 +292,7 @@ class Table: func parse_table(lines: Array) -> PackedStringArray: var line: String = lines[0] - var table := Table.new(line.count("|") + 1) + var table := GdUnitMDReaderTable.new(line.count("|") + 1) while not lines.is_empty(): line = lines.pop_front() if not table.parse_row(line): diff --git a/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid b/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid index f8964a21..c423762f 100644 --- a/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid +++ b/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid @@ -1 +1 @@ -uid://c0pbiv6wn5iiu +uid://ce1o5xmtawjsx diff --git a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid index 76bf4f50..190a46be 100644 --- a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid @@ -1 +1 @@ -uid://yfl8yfxw73e1 +uid://dcohg1ha6yt3s diff --git a/addons/gdUnit4/src/update/GdUnitPatcher.gd b/addons/gdUnit4/src/update/GdUnitPatcher.gd index 1adae828..73d25c92 100644 --- a/addons/gdUnit4/src/update/GdUnitPatcher.gd +++ b/addons/gdUnit4/src/update/GdUnitPatcher.gd @@ -22,6 +22,7 @@ func _scan(scan_path :String, current :GdUnit4Version) -> void: func patch_count() -> int: var count := 0 for key :String in _patches.keys(): + @warning_ignore("unsafe_method_access") count += _patches[key].size() return count @@ -29,7 +30,7 @@ func patch_count() -> int: func execute() -> void: for key :String in _patches.keys(): for path :String in _patches[key]: - var patch :GdUnitPatch = load(key + "/" + path).new() + var patch :GdUnitPatch = (load(key + "/" + path) as GDScript).new() if patch: prints("execute patch", patch.version(), patch.get_script().resource_path) if not patch.execute(): diff --git a/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid b/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid index c3dd5eac..7c49df9b 100644 --- a/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid @@ -1 +1 @@ -uid://cdcl6oy7t8r2a +uid://c6d0etwfrjx1k diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd b/addons/gdUnit4/src/update/GdUnitUpdate.gd index 1b492fe8..97b7fdd1 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.gd +++ b/addons/gdUnit4/src/update/GdUnitUpdate.gd @@ -18,7 +18,7 @@ var _download_url :String func _ready() -> void: - init_progress(5) + init_progress(6) func _process(_delta :float) -> void: @@ -56,6 +56,8 @@ func message_h4(message: String, color: Color, show_spinner := true) -> void: if show_spinner: _progress_content.add_image(_spinner_img) _progress_content.append_text(" [font_size=16]%s[/font_size]" % _colored(message, color)) + if _debug_mode: + prints(message) @warning_ignore("return_value_discarded") @@ -81,7 +83,7 @@ func run_update() -> void: await update_progress("Uninstall GdUnit4.") disable_gdUnit() if not _debug_mode: - delete_directory("res://addons/gdUnit4/") + GdUnitFileAccess.delete_directory("res://addons/gdUnit4/") # give editor time to react on deleted files await get_tree().create_timer(1).timeout @@ -91,14 +93,73 @@ func run_update() -> void: else: copy_directory(tmp_path, "res://") + await update_progress("Patch invalid UID's") + await patch_uids() + + await rebuild_project() + await update_progress("New GdUnit version successfully installed, Restarting Godot please wait.") await get_tree().create_timer(3).timeout enable_gdUnit() hide() - delete_directory("res://addons/.gdunit_update") + GdUnitFileAccess.delete_directory("res://addons/.gdunit_update") restart_godot() +func patch_uids(path := "res://addons/gdUnit4/src/") -> void: + var to_reimport: PackedStringArray + for file in DirAccess.get_files_at(path): + var file_path := path.path_join(file) + var ext := file.get_extension() + + if ext == "tscn" or ext == "scn" or ext == "tres" or ext == "res": + message_h4("Patch GdUnit4 scene: '%s'" % file, Color.WEB_GREEN) + remove_uids_from_file(file_path) + elif FileAccess.file_exists(file_path + ".import"): + to_reimport.append(file_path) + + if not to_reimport.is_empty(): + message_h4("Reimport resources '%s'" % ", ".join(to_reimport), Color.WEB_GREEN) + if Engine.is_editor_hint(): + EditorInterface.get_resource_filesystem().reimport_files(to_reimport) + + for dir in DirAccess.get_directories_at(path): + if not dir.begins_with("."): + patch_uids(path.path_join(dir)) + await get_tree().process_frame + + +func remove_uids_from_file(file_path: String) -> bool: + var file := FileAccess.open(file_path, FileAccess.READ) + if file == null: + print("Failed to open file: ", file_path) + return false + + var original_content := file.get_as_text() + file.close() + + # Remove UIDs using regex + var regex := RegEx.new() + regex.compile("(\\[ext_resource[^\\]]*?)\\s+uid=\"uid://[^\"]*\"") + + var modified_content := regex.sub(original_content, "$1", true) + + # Check if any changes were made + if original_content != modified_content: + prints("Patched invalid uid's out in '%s'" % file_path) + # Write the modified content back + file = FileAccess.open(file_path, FileAccess.WRITE) + if file == null: + print("Failed to write to file: ", file_path) + return false + + file.store_string(modified_content) + file.close() + return true + + return false + + func restart_godot() -> void: prints("Force restart Godot") EditorInterface.restart_editor(true) @@ -128,37 +189,13 @@ func temp_dir() -> String: func create_temp_dir(folder_name :String) -> String: var new_folder := temp_dir() + "/" + folder_name - delete_directory(new_folder) + GdUnitFileAccess.delete_directory(new_folder) if not DirAccess.dir_exists_absolute(new_folder): @warning_ignore("return_value_discarded") DirAccess.make_dir_recursive_absolute(new_folder) return new_folder -func delete_directory(path: String, only_content := false) -> void: - var dir := DirAccess.open(path) - if dir != null: - @warning_ignore("return_value_discarded") - dir.list_dir_begin() - var file_name := "." - while file_name != "": - file_name = dir.get_next() - if file_name.is_empty() or file_name == "." or file_name == "..": - continue - var next := path + "/" +file_name - if dir.current_is_dir(): - delete_directory(next) - else: - # delete file - var err := dir.remove(next) - if err: - printerr("Delete %s failed: %s" % [next, error_string(err)]) - if not only_content: - var err := dir.remove(path) - if err: - printerr("Delete %s failed: %s" % [path, error_string(err)]) - - func copy_directory(from_dir: String, to_dir: String) -> bool: if not DirAccess.dir_exists_absolute(from_dir): printerr("Source directory not found '%s'" % from_dir) @@ -236,6 +273,26 @@ func download_release() -> void: await get_tree().create_timer(3).timeout +func rebuild_project() -> void: + # Check if this is a Godot .NET runtime instance + if not ClassDB.class_exists("CSharpScript"): + return + + update_progress("Rebuild the project ...") + await get_tree().process_frame + + var output := [] + var exit_code := OS.execute("dotnet", ["build"], output) + if exit_code == -1: + message_h4("Rebuild the project failed, check your project dependencies.", Color.INDIAN_RED) + await get_tree().create_timer(3).timeout + return + + for out: String in output: + print_rich("[color=DEEP_SKY_BLUE] %s" % out.strip_edges()) + await get_tree().process_frame + + func _on_confirmed() -> void: await run_update() diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid index ebd02b68..e08c3a5a 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid @@ -1 +1 @@ -uid://yq8po6gm4kqs +uid://cj5v65eu7v0tg diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.tscn b/addons/gdUnit4/src/update/GdUnitUpdate.tscn index 3654f7d1..20d60b76 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdate.tscn +++ b/addons/gdUnit4/src/update/GdUnitUpdate.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://2eahgaw88y6q"] -[ext_resource type="Script" uid="uid://cyyxey236wyyr" path="res://addons/gdUnit4/src/update/GdUnitUpdate.gd" id="1"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdate.gd" id="1"] [sub_resource type="Gradient" id="Gradient_wilsr"] colors = PackedColorArray(0.151276, 0.151276, 0.151276, 1, 1, 1, 1, 1) diff --git a/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid index 498979e8..1e9a7af8 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid @@ -1 +1 @@ -uid://2lrcgo0tmnlo +uid://dc8sra4fnxlj6 diff --git a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid index 4b7e5900..6867d51d 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid +++ b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid @@ -1 +1 @@ -uid://cje1afd65l33e +uid://b73lujk72vws diff --git a/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn b/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn index 0878ad47..46119f14 100644 --- a/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn +++ b/addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=4 format=3 uid="uid://0xyeci1tqebj"] -[ext_resource type="Script" uid="uid://touotnvkoyo" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd" id="1_112wo"] -[ext_resource type="Script" uid="uid://ogva4spkrtij" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="2_18asx"] -[ext_resource type="PackedScene" uid="uid://2eahgaw88y6q" path="res://addons/gdUnit4/src/update/GdUnitUpdate.tscn" id="3_x87h6"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd" id="1_112wo"] +[ext_resource type="Script" path="res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd" id="2_18asx"] +[ext_resource type="PackedScene" path="res://addons/gdUnit4/src/update/GdUnitUpdate.tscn" id="3_x87h6"] [node name="Control" type="MarginContainer"] anchors_preset = 15 diff --git a/addons/gdUnit4/src/update/assets/border_bottom.png.import b/addons/gdUnit4/src/update/assets/border_bottom.png.import index 70e9a028..8b76dde0 100644 --- a/addons/gdUnit4/src/update/assets/border_bottom.png.import +++ b/addons/gdUnit4/src/update/assets/border_bottom.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dmv3ld2otx1e2" +uid="uid://1qvqbtjdv2ej" path="res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf16549d.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/border_bottom.png-30d66a4c67e3a03ad191e37cdf1 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/border_top.png.import b/addons/gdUnit4/src/update/assets/border_top.png.import index 814882aa..85245f0c 100644 --- a/addons/gdUnit4/src/update/assets/border_top.png.import +++ b/addons/gdUnit4/src/update/assets/border_top.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://b4sio0j5om50s" +uid="uid://7klfhxocgs6i" path="res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bbaa.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/border_top.png-c47cbebdb755144731c6ae309e18bb compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/dot1.png.import b/addons/gdUnit4/src/update/assets/dot1.png.import index f01f7098..e4d7df1d 100644 --- a/addons/gdUnit4/src/update/assets/dot1.png.import +++ b/addons/gdUnit4/src/update/assets/dot1.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://ce2eojg0pwpov" +uid="uid://bbhkuycnatxob" path="res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/dot1.png-380baf1b5247addda93bce3c799aa4e7.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/dot2.png.import b/addons/gdUnit4/src/update/assets/dot2.png.import index f1e10178..dd3741bb 100644 --- a/addons/gdUnit4/src/update/assets/dot2.png.import +++ b/addons/gdUnit4/src/update/assets/dot2.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://cvwa5lg3qj0e2" +uid="uid://bkh2mdiitvwtg" path="res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/dot2.png-86a9db80ef4413e353c4339ad8f68a5f.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/embedded.png.import b/addons/gdUnit4/src/update/assets/embedded.png.import index 26f37261..850e0516 100644 --- a/addons/gdUnit4/src/update/assets/embedded.png.import +++ b/addons/gdUnit4/src/update/assets/embedded.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://63wk5nib3r7q" +uid="uid://d2uoa5hlgruf0" path="res://.godot/imported/embedded.png-29390948772209a603567d24f8766495.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/embedded.png-29390948772209a603567d24f8766495 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/gdUnit4/src/update/assets/horizontal-line2.png.import b/addons/gdUnit4/src/update/assets/horizontal-line2.png.import index 949745cf..e74e345f 100644 --- a/addons/gdUnit4/src/update/assets/horizontal-line2.png.import +++ b/addons/gdUnit4/src/update/assets/horizontal-line2.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dgaa5faajesgv" +uid="uid://d2wor0u31mfaq" path="res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8c9deadbc.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/horizontal-line2.png-92618e6ee5cc9002847547a8 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/addons/monologue/assets/Node.svg.import b/addons/monologue/assets/Node.svg.import index b236e3d3..1feb6b00 100644 --- a/addons/monologue/assets/Node.svg.import +++ b/addons/monologue/assets/Node.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cgs1pju1mljky" -path="res://.godot/imported/Node.svg-e16dadd791df1928ceaf195a00daa668.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/Node.svg-e16dadd791df1928ceaf195a00daa668.dpitex" [deps] source_file="res://addons/monologue/assets/Node.svg" -dest_files=["res://.godot/imported/Node.svg-e16dadd791df1928ceaf195a00daa668.ctex"] +dest_files=["res://.godot/imported/Node.svg-e16dadd791df1928ceaf195a00daa668.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/addons/monologue/assets/RichTextLabel.svg.import b/addons/monologue/assets/RichTextLabel.svg.import index 41e94928..adb69fb1 100644 --- a/addons/monologue/assets/RichTextLabel.svg.import +++ b/addons/monologue/assets/RichTextLabel.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://tdn0wg47rwvc" -path="res://.godot/imported/RichTextLabel.svg-5ab481203362918ef68d076c470c15e7.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/RichTextLabel.svg-5ab481203362918ef68d076c470c15e7.dpitex" [deps] source_file="res://addons/monologue/assets/RichTextLabel.svg" -dest_files=["res://.godot/imported/RichTextLabel.svg-5ab481203362918ef68d076c470c15e7.ctex"] +dest_files=["res://.godot/imported/RichTextLabel.svg-5ab481203362918ef68d076c470c15e7.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/addons/monologue/core/process_logic/sentence.gd b/addons/monologue/core/process_logic/sentence.gd index 36347a56..e80ab0f2 100644 --- a/addons/monologue/core/process_logic/sentence.gd +++ b/addons/monologue/core/process_logic/sentence.gd @@ -18,7 +18,7 @@ func enter(ctx: MonologueContext, node: Dictionary, _data: Dictionary = {}) -> M elif not speaker_slot_name and ctx.settings.highlight_speaker: ctx.timeline.character_displayer.focus_slot("") - for language in languages: + for language: String in languages: if language == ctx.settings.language: sentence = languages[language] @@ -28,7 +28,7 @@ func enter(ctx: MonologueContext, node: Dictionary, _data: Dictionary = {}) -> M return MonologueProcessResult.interrupt_process(node.get("NextID")) -func update(ctx: MonologueContext, node: Dictionary, _delta: float) -> MonologueProcessResult: +func update(ctx: MonologueContext, _node: Dictionary, _delta: float) -> MonologueProcessResult: if Input.is_action_just_pressed("ui_continue") or \ (Input.is_action_just_pressed("ui_mouse_continue") and ctx.timeline.text_box_container_mouse_hovering): if ctx.timeline.text_box.visible_ratio < 1: diff --git a/autoloads/app.gd b/autoloads/app.gd index 6a3fb0dc..6e22cc5f 100644 --- a/autoloads/app.gd +++ b/autoloads/app.gd @@ -2,60 +2,60 @@ extends Node # Reference DPI for a 23.8" 1920x1080 display const BASE_SCALE_DPI: float = 92.56 - -# Application preferences config -var preferences: ConfigFile = ConfigFile.new() +const BASE_SCREEN_SIZE: Vector2i = Vector2i(1920, 1080) func _ready() -> void: - _load_preferences() - _update_window(true) - get_window().connect("size_changed", Callable(self, "_on_window_size_changed")) + _update_window(get_window(), true) + get_window().size_changed.connect(_on_window_size_changed) func _shortcut_input(event: InputEvent) -> void: - if event.is_action_pressed("Add node"): - GlobalSignal.emit("enable_picker_mode") + if event.is_action_pressed("mnl_add_node"): + EventBus.enable_picker_mode.emit() func _on_window_size_changed() -> void: - _update_window() + _update_window(get_window()) -func _update_window(update_size: bool = false) -> void: - var screen_size: Vector2i = DisplayServer.window_get_size() - var scale_factor: float = _get_auto_display_scale() - get_window().content_scale_factor = scale_factor +func _update_window(window: Window, update_size: bool = false) -> void: + var window_id: int = max(0, window.get_window_id()) + var scale_factor: float = _get_auto_display_scale(window_id) + window.content_scale_factor = scale_factor if update_size: - DisplayServer.window_set_size(screen_size * scale_factor) - get_window().move_to_center() - - -func _load_preferences() -> void: - var err := preferences.load(Constants.PREFERENCES_PATH) - if err != OK: - push_warning("Failed to load preferences from %s" % Constants.PREFERENCES_PATH) + var window_size: Vector2i = DisplayServer.window_get_size(window_id) + var screen_id: int = DisplayServer.window_get_current_screen(window_id) + var screen_size: Vector2 = DisplayServer.screen_get_size(screen_id) as Vector2 + var window_scale: Vector2 = screen_size / (BASE_SCREEN_SIZE as Vector2) + DisplayServer.window_set_size(Vector2i(window_size as Vector2 * window_scale)) + window.move_to_center() ## Returns the optimal window scale factor for the current screen. -## Logic adapted from Godot editor/editor_settings.cpp:1564. -func _get_auto_display_scale() -> float: - var os_name := OS.get_name() +## Logic adapted from Godot editor/editor_settings.cpp#L1974. +func _get_auto_display_scale(window_id: int = 0) -> float: + var os_name: String = OS.get_name() if os_name in ["Linux", "FreeBSD", "NetBSD", "OpenBSD", "BSD"]: if DisplayServer.get_name() == "Wayland": - var main_window_scale: float = DisplayServer.screen_get_scale( - DisplayServer.SCREEN_OF_MAIN_WINDOW - ) - if DisplayServer.get_screen_count() == 1: + var main_window_scale: float = DisplayServer.screen_get_scale(DisplayServer.SCREEN_OF_MAIN_WINDOW) + if DisplayServer.get_screen_count() == 1 or fmod(main_window_scale, 1.0) != 0.0: return main_window_scale return DisplayServer.screen_get_max_scale() + if os_name in ["macOS", "Android"]: return DisplayServer.screen_get_max_scale() - var screen: int = DisplayServer.window_get_current_screen() + + var screen: int = DisplayServer.window_get_current_screen(window_id) + if DisplayServer.screen_get_size(screen) == Vector2i.ZERO: + return 1.0 + + if os_name == "Windows": + return DisplayServer.screen_get_dpi(screen) / 96.0 + var screen_size: Vector2i = DisplayServer.screen_get_size(screen) - if screen_size == Vector2i(): - return 1.0 # Invalid screen size var smallest_dimension: int = min(screen_size.x, screen_size.y) + var dpi: float = DisplayServer.screen_get_dpi(screen) if dpi >= 192.0 and smallest_dimension >= 1440: return 2.0 # hiDPI display @@ -64,3 +64,15 @@ func _get_auto_display_scale() -> float: elif smallest_dimension <= 800: return 0.75 # small loDPI display return 1.0 + +func get_windows_scale() -> float: + var output: Array = [] + OS.execute("powershell", [ + "-Command", + "(Get-ItemProperty 'HKCU:\\Control Panel\\Desktop' -Name LogPixels -ErrorAction SilentlyContinue).LogPixels" + ], output) + + if output.size() > 0 and output[0].strip_edges() != "": + var logpixels: int = output[0].strip_edges().to_int() + return logpixels / 96.0 + return 1.0 diff --git a/autoloads/config_manager.gd b/autoloads/config_manager.gd new file mode 100644 index 00000000..93e4866b --- /dev/null +++ b/autoloads/config_manager.gd @@ -0,0 +1,67 @@ +extends Node + +signal theme_changed + +var current_configuration: ConfigurationDocument +var command_manager: CommandManager = CommandManager.new() + + +func _ready() -> void: + load_configuration() + + sync_theme() + sync_fonts() + + +func load_configuration() -> ConfigurationDocument: + _ensure_configuration() + + var file: FileAccess = FileAccess.open(Constants.PREFERENCES_PATH, FileAccess.READ) + var content: String = file.get_as_text() + file.close() + + var data: Dictionary = JSON.parse_string(content) + current_configuration = ConfigurationDocument.new(command_manager) + current_configuration._from_dict(data) + + return current_configuration + + +func save_configuration() -> void: + var file: FileAccess = FileAccess.open(Constants.PREFERENCES_PATH, FileAccess.WRITE) + var data: Dictionary = current_configuration._to_dict() + var content: String = JSON.stringify(data, "\t") + file.store_string(content) + file.close() + + +func _ensure_configuration() -> void: + if not FileAccess.file_exists(Constants.PREFERENCES_PATH): + current_configuration = ConfigurationDocument.new(command_manager) + save_configuration() + + +func get_config(property_name: String, default: Variant = null) -> Variant: + if not current_configuration: + Log.error("Attempting to retrieve a configuration value without a configuration document.") + return + + var property: Property = current_configuration.get_property(property_name) + return property.get_value() if property != null else default + + +func set_config(property_name: String, value: Variant) -> void: + if not current_configuration: + Log.error("Attempting to update a configuration property without a configuration document.") + return + + current_configuration.set_property_value(property_name, value) + + +func sync_theme() -> void: + ThemeLayout.generate_and_apply_theme() + theme_changed.emit() + +func sync_fonts() -> void: + #ThemeLayout.rebuild_fonts() + pass diff --git a/autoloads/config_manager.gd.uid b/autoloads/config_manager.gd.uid new file mode 100644 index 00000000..527d1ba6 --- /dev/null +++ b/autoloads/config_manager.gd.uid @@ -0,0 +1 @@ +uid://b5ifbbjetj5cm diff --git a/autoloads/constants.gd b/autoloads/constants.gd index 3ce56ef1..f174d68d 100644 --- a/autoloads/constants.gd +++ b/autoloads/constants.gd @@ -1,29 +1,7 @@ -extends Node +class_name Constants extends Node -## Dictionary of Monologue node types and their corresponding scenes. -const NODE_SCENES = { - "Root": preload("res://nodes/root_node/root_node.tscn"), - "Audio": preload("res://nodes/audio_node/audio_node.tscn"), - "Action": preload("res://nodes/action_node/action_node.tscn"), - "Background": preload("res://nodes/background_node/background_node.tscn"), - "Bridge": preload("res://nodes/bridge_in_node/bridge_in_node.tscn"), - "BridgeIn": preload("res://nodes/bridge_in_node/bridge_in_node.tscn"), - "BridgeOut": preload("res://nodes/bridge_out_node/bridge_out_node.tscn"), - "Choice": preload("res://nodes/choice_node/choice_node.tscn"), - "Character": preload("res://nodes/character_node/character_node.tscn"), - "Comment": preload("res://nodes/comment_node/comment_node.tscn"), - "Condition": preload("res://nodes/condition_node/condition_node.tscn"), - "Random": preload("res://nodes/random_node/random_node.tscn"), - "EndPath": preload("res://nodes/end_path_node/end_path_node.tscn"), - "Event": preload("res://nodes/event_node/event_node.tscn"), - "Sentence": preload("res://nodes/sentence_node/sentence_node.tscn"), - "Setter": preload("res://nodes/setter_node/setter_node.tscn"), - "Wait": preload("res://nodes/wait_node/wait_node.tscn"), - "Reroute": preload("res://nodes/reroute_node/reroute_node.tscn") -} -const HISTORY_PATH = "user://history.save" -const PREFERENCES_PATH = "user://preferences.cfg" -const PROPERTY_CLASSES = ["Property", "Localizable"] +const HISTORY_PATH: String = "user://history.save" +const PREFERENCES_PATH: String = "user://preferences.mnl" const UNSAVED_FILE_SUFFIX: String = "*" diff --git a/autoloads/cursor.gd b/autoloads/cursor.gd index e0a5c527..ddaa8b68 100644 --- a/autoloads/cursor.gd +++ b/autoloads/cursor.gd @@ -1,5 +1,5 @@ -extends Node +class_name Cursor -@onready var arrow = preload("res://ui/assets/cursors/cursor.svg") -@onready var hand = preload("res://ui/assets/cursors/hand.svg") -@onready var closed_hand = preload("res://ui/assets/cursors/closed_hand.svg") +const ARROW: Texture2D = preload("res://ui/assets/cursors/cursor.svg") +const HAND: Texture2D = preload("res://ui/assets/cursors/hand.svg") +const CLOSED_HAND: Texture2D = preload("res://ui/assets/cursors/closed_hand.svg") diff --git a/autoloads/event_bus.gd b/autoloads/event_bus.gd new file mode 100644 index 00000000..11612a7b --- /dev/null +++ b/autoloads/event_bus.gd @@ -0,0 +1,45 @@ +extends Node + +@warning_ignore_start("unused_signal") +signal load_project(path: String) +signal test_trigger + +signal refresh +signal refresh_graph +signal add_graph_node(descriptor_name: String, window: Window) +signal request_node_selection(node: InspectableNode, storyline_id: String, skip_history: bool) +signal request_object_inspection(object: InspectableObject) +signal request_storyline_inspection(storyline: StorylineDocument) +signal select_new_node +signal inspector_property_changed(object: InspectableObject, property_name: String, is_undo: bool) +signal storyline_deleted + +signal save_file_request(callable: Callable, filter_list: PackedStringArray, root_subdir: String, options: Array[Dictionary]) +signal open_file_request(callable: Callable, filter_list: PackedStringArray, root_subdir: String, options: Array[Dictionary]) +signal open_files_request(callable: Callable, filter_list: PackedStringArray, root_subdir: String, options: Array[Dictionary]) +signal open_dir_request(callable: Callable, root_subdir: String, options: Array[Dictionary]) + +signal load_languages(languages: Array, graph: MonologueGraphEdit) +signal show_languages(can_see: bool) +signal enable_language_switcher +signal disable_language_switcher +signal language_deleted + +signal show_welcome +signal hide_welcome +signal enable_picker_mode( + node: GraphNode, port: int, release: Vector2, graph_release: Vector2, center: Vector2 +) +signal show_inspector(visible: bool) +signal show_project_explorer(visible: bool) +signal show_console(visible: bool) +signal show_status_bar(visible: bool) +signal graph_snap(enabled: bool) +signal graph_show_grid(visible: bool) + +signal expand_text_edit(text_edit: TextEdit) +signal show_dimmer +signal hide_dimmer + +signal window_out +signal ask_dialog(callback: Callable, header: String, description: String, confirm_text: String, deny_text: String, cancel_text: String) diff --git a/autoloads/event_bus.gd.uid b/autoloads/event_bus.gd.uid new file mode 100644 index 00000000..eed6ffcb --- /dev/null +++ b/autoloads/event_bus.gd.uid @@ -0,0 +1 @@ +uid://dwbubkb7lgvnd diff --git a/autoloads/global_signal.gd b/autoloads/global_signal.gd deleted file mode 100644 index 7f2fa574..00000000 --- a/autoloads/global_signal.gd +++ /dev/null @@ -1,34 +0,0 @@ -extends Node - -# Keeps track of what listeners have been registered. -var _listeners = {} - - -# Adds a new global listener that is a method linked to signal_name. -func add_listener(signal_name: String, method: Callable) -> void: - if not _listeners.has(signal_name): - _listeners[signal_name] = [] - - _listeners[signal_name].append(method) - - -# Remove registered method linked to a signal_name. -func remove_listener(signal_name: String, method: Callable) -> void: - if not _listeners.has(signal_name): - return - if not _listeners[signal_name].has(method): - return - - _listeners[signal_name].erase(method) - - -# Call all the methods linked with a signal_name. -func emit(signal_name: String, args: Array = []): - if not _listeners.has(signal_name): - return - - for method in _listeners[signal_name]: - if not method.is_valid(): - _listeners[signal_name].erase(method) - continue - method.callv(args) diff --git a/autoloads/global_signal.gd.uid b/autoloads/global_signal.gd.uid deleted file mode 100644 index d16efcb9..00000000 --- a/autoloads/global_signal.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cddkml38qln28 diff --git a/autoloads/global_variables.gd b/autoloads/global_variables.gd deleted file mode 100644 index 24d0fc54..00000000 --- a/autoloads/global_variables.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends Node - -var language_switcher: LanguageSwitcher -var test_path: String diff --git a/autoloads/global_variables.gd.uid b/autoloads/global_variables.gd.uid deleted file mode 100644 index c533b8c2..00000000 --- a/autoloads/global_variables.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ch6hpkp7hoafi diff --git a/autoloads/id_gen.gd b/autoloads/id_gen.gd index 4e6052bc..9daa8c5d 100644 --- a/autoloads/id_gen.gd +++ b/autoloads/id_gen.gd @@ -1,20 +1,11 @@ -extends Node +class_name IDGen -var characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +const _CHARS: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +static var _rng: RandomNumberGenerator = RandomNumberGenerator.new() -func generate(length: int = 10, avoid_ids: Array = []) -> String: - for _i in range(100): - var id = "" - var random = RandomNumberGenerator.new() - random.randomize() - - for i in range(length): - var index = random.randi_range(0, characters.length() - 1) - id += characters[index] - - if id not in avoid_ids: - return id - - push_error("The program failed to generate a random number.") - return "" +static func generate(length: int = 10) -> String: + var id: String = "" + for i: int in range(length): + id += _CHARS[_rng.randi_range(0, _CHARS.length() - 1)] + return id diff --git a/autoloads/image_loader.gd b/autoloads/image_loader.gd index f5e9b375..2831cdfe 100644 --- a/autoloads/image_loader.gd +++ b/autoloads/image_loader.gd @@ -35,11 +35,13 @@ func _get_thumbnail(image_path: String) -> ImageTexture: var tx: ImageTexture = ImageTexture.new() if _cache.has(image_path): - tx = _cache[image_path].get("thumbnail_128", tx) + var cache_entry: Dictionary = _cache[image_path] + tx = cache_entry.get("thumbnail_128", tx) elif not image_path.is_empty(): _load_image_to_cache(image_path) if _cache.has(image_path): - tx = _cache[image_path].get("thumbnail_128", tx) + var cache_entry2: Dictionary = _cache[image_path] + tx = cache_entry2.get("thumbnail_128", tx) return tx @@ -48,10 +50,12 @@ func _get_image(image_path: String) -> ImageTexture: var tx: ImageTexture = ImageTexture.new() if _cache.has(image_path): - tx = _cache[image_path].get("raw", tx) + var cache_entry: Dictionary = _cache[image_path] + tx = cache_entry.get("raw", tx) elif not image_path.is_empty(): _load_image_to_cache(image_path) if _cache.has(image_path): - tx = _cache[image_path].get("raw", tx) + var cache_entry2: Dictionary = _cache[image_path] + tx = cache_entry2.get("raw", tx) return tx diff --git a/autoloads/logger.gd b/autoloads/logger.gd new file mode 100644 index 00000000..aa591941 --- /dev/null +++ b/autoloads/logger.gd @@ -0,0 +1,75 @@ +extends Node +# TODO save logs in files + +signal log_message(message: String, bbcode_message: String) + +enum Levels {DEBUG, INFO, WARN, ERROR, FATAL} + +var log_level: Levels = Levels.DEBUG +var _label := RichTextLabel.new() + + +func _ready() -> void: + _label.bbcode_enabled = true + + +func _log(level: Levels, levelname: String, colorname: String, args: Array, bold: bool = false) -> void: + if level < log_level: + return + + var message: String = " ".join(args) + var bbcode_args: Array = [] + bbcode_args.append("[color=%s]" % colorname) + bbcode_args.append("[b]%-5s\t [/b]" % levelname) + if bold: bbcode_args.append("[b]") + bbcode_args.append(message) + if bold: bbcode_args.append("[/b]") + bbcode_args.append("[/color]") + + print_rich.callv(bbcode_args) + var bbcode: String = "".join(bbcode_args) + var raw_log: String = _bbcode_to_plain(bbcode) + + log_message.emit(raw_log, bbcode) + + +func msg(...args: Array) -> void: + print_rich.callv(args) + + +func debug(...args: Array) -> void: + _log(Levels.DEBUG, "DEBUG", "#5f819d", args) + + +func info(...args: Array) -> void: + _log(Levels.INFO, "INFO", "#8c9440", args) + + +func warn(...args: Array) -> void: + _log(Levels.WARN, "WARN", "#de935f", args) + + +func error(...args: Array) -> void: + _log(Levels.ERROR, "ERROR", "#a54242", args) + + +func exception(...args: Array) -> void: + var stack: Array = get_stack() + stack.pop_front() + + var call_idx: int = 0 + for _call: Dictionary in stack: + var source: String = "%s:%s" % [_call.get("source", ""), _call.get("line", -1)] + args.append("[indent]%s - %s[/indent]" % [call_idx, source]) + call_idx += 1 + + error.callv(args) + + +func fatal(...args: Array) -> void: + _log(Levels.FATAL, "FATAL", "#a54242", args, true) + + +func _bbcode_to_plain(bbcode: String) -> String: + _label.text = bbcode + return _label.get_parsed_text() diff --git a/autoloads/logger.gd.uid b/autoloads/logger.gd.uid new file mode 100644 index 00000000..baa0f85f --- /dev/null +++ b/autoloads/logger.gd.uid @@ -0,0 +1 @@ +uid://bstbvfwoltk8n diff --git a/autoloads/path.gd b/autoloads/path.gd index 62c8d44a..c4acde1b 100644 --- a/autoloads/path.gd +++ b/autoloads/path.gd @@ -1,59 +1,61 @@ -extends Node +class_name PathUtil -func get_separator() -> String: +static func get_separator() -> String: return "\\" if OS.has_feature("windows") else "/" -func split_path(path: String) -> PackedStringArray: +static func split_path(path: String) -> PackedStringArray: var splt_path: String = path.replace(path.get_file(), "") splt_path = splt_path.replace("\\", "/") splt_path = splt_path.replace("//", "/") return splt_path.split("/", false) -func absolute_to_relative(path: String, root_file_path: String) -> String: +static func absolute_to_relative(path: String, root_file_path: String) -> String: var root_array: PackedStringArray = split_path(root_file_path) var path_array: PackedStringArray = split_path(path) if not path.is_absolute_path() or root_array.size() <= 0 or root_array[0] != path_array[0]: return path - var back = [] - var forward = [] - var max_path_size = max(root_array.size(), path_array.size()) - for i in max_path_size: - var root_index = root_array[i] if i < root_array.size() else "" - var path_index = path_array[i] if i < path_array.size() else "" + var back: Array[String] = [] + var forward: Array[String] = [] + var max_path_size: int = max(root_array.size(), path_array.size()) + for i: int in max_path_size: + var root_index: String = root_array[i] if i < root_array.size() else "" + var path_index: String = path_array[i] if i < path_array.size() else "" if root_index == path_index: continue if root_index: back.append("..") if path_index: forward.append(path_index) - var final_path = back + forward + var final_path: Array[String] = [] + final_path.append_array(back) + final_path.append_array(forward) final_path.append(path.get_file()) - var relative_path = "" - for step in final_path: + var relative_path: String = "" + for step: String in final_path: relative_path = relative_path.path_join(step) return relative_path -func relative_to_absolute(path: String, root_file_path: String) -> String: +static func relative_to_absolute(path: String, root_file_path: String) -> String: if path.is_absolute_path(): return path var root_array: PackedStringArray = split_path(root_file_path) var path_array: PackedStringArray = split_path(path) - var back_count = path.count("..") - var core_path = Array(root_array).slice(0, root_array.size() - back_count) - var to_file = Array(path_array).slice(back_count) - var final_path = Array(core_path) + to_file + var back_count: int = path.count("..") + var core_path: Array = Array(root_array).slice(0, root_array.size() - back_count) + var to_file: Array = Array(path_array).slice(back_count) + var final_path: Array = core_path + to_file final_path.append(path.get_file()) - var absolute_path = "" - for step in final_path: - absolute_path = absolute_path.path_join(step) + var absolute_path: String = "" + for step: Variant in final_path: + absolute_path = absolute_path.path_join(str(step)) # Prepend "/" for Linux root if not a Windows drive letter - var drive_matcher = RegEx.new() + var drive_matcher: RegEx = RegEx.new() drive_matcher.compile("[a-zA-Z]:") - var drive_result = root_array.size() > 0 and drive_matcher.search(root_array[0]) + var drive_result: RegExMatch = drive_matcher.search(root_array[0]) if root_array.size() > 0 else null if not drive_result: absolute_path = "/" + absolute_path return absolute_path diff --git a/autoloads/project_manager.gd b/autoloads/project_manager.gd new file mode 100644 index 00000000..662a05e3 --- /dev/null +++ b/autoloads/project_manager.gd @@ -0,0 +1,146 @@ +extends Node + +signal project_loaded +signal _file_prompt_done + +const SAVE_PROMPT: String = "%s has been modified." + +var current_project: MonologueProject +var _cancel_close: bool = false + + +func _ready() -> void: + EventBus.load_project.connect(load_project_from_path) + + +func close_current_project() -> bool: + if not current_project: + _update_window_title() + return true + + if current_project and current_project.is_dirty: + EventBus.ask_dialog.emit( + _on_close_project_dialog_response, + "Save change before closing?", + SAVE_PROMPT % current_project.project_path, + "Save", + "Don't Save", + "Cancel" + ) + await _file_prompt_done + + if _cancel_close: + return false + + _update_window_title() + return true + + +func load_project(project: MonologueProject) -> void: + if not await close_current_project(): + return + + Log.info("Project loaded!") + current_project = project + project_loaded.emit.call_deferred() + EventBus.hide_welcome.emit() + + if not project.project_path.is_empty(): + add_path_to_history(project.project_path) + + _update_window_title() + + +func save_project(project: MonologueProject) -> void: + project.save() + + +func load_project_from_path(path: String) -> void: + var new_project: MonologueProject + if FileAccess.file_exists(path) and path.ends_with(".%s" % MonologueProject.FILE_FORMAT): + new_project = await MonologueProject.from_file_path(path) + elif path.ends_with(".%s" % ManifestDocument.FILE_FORMAT): + new_project = await MonologueProject.from_dir_path(path.get_base_dir()) + + if not new_project: + Log.error("Can't load project from an invalid path.") + return + + load_project(new_project) + + +func add_path_to_history(path: String) -> void: + var paths: Array = get_history() as Array + paths.erase(path.simplify_path()) + paths.push_front(path) + + _ensure_history_file() + var file: FileAccess = FileAccess.open(Constants.HISTORY_PATH, FileAccess.WRITE) + var content: String = JSON.stringify(paths) + file.store_string(content) + file.close() + + +func get_history() -> PackedStringArray: + if not FileAccess.file_exists(Constants.HISTORY_PATH): + FileAccess.open(Constants.HISTORY_PATH, FileAccess.WRITE) + + var file: FileAccess = FileAccess.open(Constants.HISTORY_PATH, FileAccess.READ) + var content: String = file.get_as_text() + file.close() + + var paths: Array = [] + for path: String in _parse_history(content): + if path not in paths: + paths.append(path) + + for path: String in paths: + if not FileAccess.file_exists(path): + paths.erase(path) + + return paths as PackedStringArray + + +func _parse_history(text: String) -> Array: + var data: Variant = JSON.parse_string(text) + if data is Array: + return data.filter(func(p: Variant) -> bool: return FileAccess.file_exists(p)) + return [] + + +func _ensure_history_file() -> void: + if not FileAccess.file_exists(Constants.HISTORY_PATH): + FileAccess.open(Constants.HISTORY_PATH, FileAccess.WRITE) + + +func _on_close_project_dialog_response(response: int) -> void: + _cancel_close = false + if response == Prompt.DENIED: + _file_prompt_done.emit() + return + if response == Prompt.CONFIRMED: + await current_project.save() + _file_prompt_done.emit() + return + + _cancel_close = true + _file_prompt_done.emit() + return + + +func _update_window_title() -> void: + var base_title: String = "Monologue %s" % ProjectSettings.get_setting("application/config/version") + + if not current_project: + DisplayServer.window_set_title(base_title) + return + + var title: String = "" + if current_project.project_path: + title = current_project.project_path.get_file().get_basename() + + if current_project.is_dirty: + title = "* " + title + + DisplayServer.window_set_title("%s - %s" % [title, base_title]) + diff --git a/autoloads/project_manager.gd.uid b/autoloads/project_manager.gd.uid new file mode 100644 index 00000000..ed977378 --- /dev/null +++ b/autoloads/project_manager.gd.uid @@ -0,0 +1 @@ +uid://dabxmafwjrhkv diff --git a/autoloads/sfx_loader.gd b/autoloads/sfx_loader.gd index 1f363363..e7717199 100644 --- a/autoloads/sfx_loader.gd +++ b/autoloads/sfx_loader.gd @@ -4,7 +4,7 @@ var last_created_track: AudioStreamPlayer2D func clear() -> void: - for child in get_children(): + for child: Node in get_children(): child.queue_free() @@ -13,28 +13,31 @@ func get_track() -> AudioStreamPlayer2D: func load_track(path: String, loop: bool = false, volume: float = 0.0, pitch: float = 1.0) -> Error: - var file = FileAccess.open(path, FileAccess.READ) + var file: FileAccess = FileAccess.open(path, FileAccess.READ) if not file: return ERR_FILE_NOT_FOUND - var player = AudioStreamPlayer2D.new() + var player: AudioStreamPlayer2D = AudioStreamPlayer2D.new() player.pitch_scale = pitch player.volume_db = volume var stream: AudioStream = null - var buffer = file.get_buffer(file.get_length()) + var buffer: PackedByteArray = file.get_buffer(file.get_length()) match path.get_extension(): "mp3": - stream = AudioStreamMP3.new() - stream.data = buffer - stream.loop = loop + var mp3_stream: AudioStreamMP3 = AudioStreamMP3.new() + mp3_stream.data = buffer + mp3_stream.loop = loop + stream = mp3_stream "ogg": - stream = AudioStreamOggVorbis.load_from_buffer(buffer) - stream.loop = loop + var ogg_stream: AudioStreamOggVorbis = AudioStreamOggVorbis.load_from_buffer(buffer) + ogg_stream.loop = loop + stream = ogg_stream "wav": - stream = parse_wav_data(buffer) - if not stream: + var wav_stream: AudioStreamWAV = parse_wav_data(buffer) + if not wav_stream: return ERR_PARSE_ERROR - stream.loop_mode = int(loop) + wav_stream.loop_mode = int(loop) as AudioStreamWAV.LoopMode + stream = wav_stream _: return ERR_INVALID_PARAMETER file.close() @@ -52,26 +55,26 @@ func load_track(path: String, loop: bool = false, volume: float = 0.0, pitch: fl ## from [url]https://github.com/Gianclgar/GDScriptAudioImport[/url] which is ## MIT-licensed. 24-bit WAV is not converted, but rejected. func parse_wav_data(bytes: PackedByteArray) -> AudioStreamWAV: - var stream = AudioStreamWAV.new() - var bits_per_sample = 0 + var stream: AudioStreamWAV = AudioStreamWAV.new() + var bits_per_sample: int = 0 # read the .wav header in bytes, then assign values to the AudioStreamWAV - for i in range(0, 43): - var byte_chunk = str( + for i: int in range(0, 43): + var byte_chunk: String = str( char(bytes[i]) + char(bytes[i + 1]) + char(bytes[i + 2]) + char(bytes[i + 3]) ) if byte_chunk == "fmt ": - var start = i + 8 # audio format starts 8 bytes after "fmt " + var start: int = i + 8 # audio format starts 8 bytes after "fmt " # set format code (bytes 0-1) - var format_code = bytes[start] + (bytes[start + 1] << 8) - stream.format = format_code + var format_code: int = bytes[start] + (bytes[start + 1] << 8) + stream.format = format_code as AudioStreamWAV.Format # set channel number (bytes 2-3) - var channel_number = bytes[start + 2] + (bytes[start + 3] << 8) + var channel_number: int = bytes[start + 2] + (bytes[start + 3] << 8) stream.stereo = channel_number == 2 # set sample rate (bytes 4-7) - var sample_rate = ( + var sample_rate: int = ( bytes[start + 4] + (bytes[start + 5] << 8) + (bytes[start + 6] << 16) @@ -85,16 +88,16 @@ func parse_wav_data(bytes: PackedByteArray) -> AudioStreamWAV: return null # we only support 8-bit and 16-bit WAV elif byte_chunk == "data": - var start = i + 8 # starts 8 bytes after "data" - var size = ( + var start: int = i + 8 # starts 8 bytes after "data" + var size: int = ( bytes[i + 4] + (bytes[i + 5] << 8) + (bytes[i + 6] << 16) + (bytes[i + 7] << 24) ) stream.data = bytes.slice(start, start + size) break # calculate the size of each sample based on bits_per_sample - var sample_size = bits_per_sample / 8.0 + var sample_size: float = bits_per_sample / 8.0 # get samples and set loop end - var sample_number = stream.data.size() / sample_size - stream.loop_end = sample_number + var sample_number: float = stream.data.size() / sample_size + stream.loop_end = int(sample_number) return stream if stream.data else null diff --git a/autoloads/tooltip.gd b/autoloads/tooltip.gd new file mode 100644 index 00000000..bb0a81b7 --- /dev/null +++ b/autoloads/tooltip.gd @@ -0,0 +1,27 @@ +extends Node + +signal tooltip_update + +var text: String = "" + + +func _input(event: InputEvent) -> void: + if not event is InputEventMouseMotion: + return + + var control: Control = get_viewport().gui_get_hovered_control() + + text = "" + if control and (control.tooltip_text or control.has_meta("tooltip")): + text = control.tooltip_text if control.tooltip_text else control.get_meta("tooltip", "") + + tooltip_update.emit() + + +func _process(_delta: float) -> void: + var control: Control = get_viewport().gui_get_hovered_control() + if not control or not control.tooltip_text: + return + + control.set_meta("tooltip", control.tooltip_text) + control.tooltip_text = "" diff --git a/autoloads/tooltip.gd.uid b/autoloads/tooltip.gd.uid new file mode 100644 index 00000000..d7401465 --- /dev/null +++ b/autoloads/tooltip.gd.uid @@ -0,0 +1 @@ +uid://dv715qjt3315j diff --git a/autoloads/util.gd b/autoloads/util.gd index 3aa06a58..a8a4befe 100644 --- a/autoloads/util.gd +++ b/autoloads/util.gd @@ -1,30 +1,19 @@ -extends Node +class_name Util -const MAX_FILENAME_LENGTH = 48 +const MAX_FILENAME_LENGTH: int = 48 -func check_config_file(path: String) -> void: - assert(FileAccess.file_exists(path)) - var raw_text = FileAccess.open(path, FileAccess.READ).get_as_text() - var data: Dictionary = JSON.parse_string(raw_text) - assert(data.has("ProjectName")) - assert(data.has("VersionProject")) - assert(data.has("VersionEditor")) - assert(data.has("DefaultStart")) - assert(data.has("ListSpeakers")) - - -func is_equal(a: Variant, b: Variant) -> bool: - var type_a = typeof(a) - var type_b = typeof(b) +static func is_equal(a: Variant, b: Variant) -> bool: + var type_a: int = typeof(a) + var type_b: int = typeof(b) if type_a == type_b: match type_a: TYPE_DICTIONARY, TYPE_OBJECT: return a.hash() == b.hash() TYPE_ARRAY: if a.size() == b.size(): - var i = 0 - var premise = true + var i: int = 0 + var premise: bool = true while i < a.size() and premise: premise = is_equal(a[i], b[i]) i += 1 @@ -35,25 +24,30 @@ func is_equal(a: Variant, b: Variant) -> bool: ## Check if any element of array_a is inside array_b. -func is_any_inside(array_a: Array, array_b: Array) -> bool: - for element in array_a: +static func is_any_inside(array_a: Array, array_b: Array) -> bool: + for element: Variant in array_a: if array_b.has(element): return true return false ## Converts a snake_case name to JSON key format with capitalized "ID". -func to_key_name(snake_case_name: String, delimiter: String = "") -> String: - var words = snake_case_name.capitalize().split(" ") - var capitalized_list = PackedStringArray() - for word in words: +static func to_key_name(snake_case_name: String, delimiter: String = "") -> String: + var words: PackedStringArray = snake_case_name.capitalize().split(" ") + var capitalized_list: PackedStringArray = PackedStringArray() + for word: String in words: capitalized_list.append("ID" if word.to_lower() == "id" else word) return delimiter.join(capitalized_list) +## Converts a snake_case name to readable string with capitalized "ID". +static func to_readable_name(snake_case_name: String) -> String: + return to_key_name(snake_case_name, " ") + + ## Left-truncate a filename string based on MAX_FILENAME_LENGTH. -func truncate_filename(filename: String) -> String: - var truncated = filename +static func truncate_filename(filename: String) -> String: + var truncated: String = filename if filename.length() > MAX_FILENAME_LENGTH: truncated = "..." + filename.right(MAX_FILENAME_LENGTH - 3) return truncated diff --git a/bilUDX.png b/bilUDX.png deleted file mode 100644 index 8e52a85d..00000000 Binary files a/bilUDX.png and /dev/null differ diff --git a/bilUDX.png.import b/bilUDX.png.import deleted file mode 100644 index b45c9584..00000000 --- a/bilUDX.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://durc7t2rjdekn" -path="res://.godot/imported/bilUDX.png-4b799bae1c669628ee420ba80d3eba36.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://bilUDX.png" -dest_files=["res://.godot/imported/bilUDX.png-4b799bae1c669628ee420ba80d3eba36.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/build_profile.gdbuild b/build_profile.gdbuild new file mode 100644 index 00000000..f50e7166 --- /dev/null +++ b/build_profile.gdbuild @@ -0,0 +1,283 @@ +{ + "disabled_build_options": { + "brotli": false, + "disable_3d": true, + "disable_navigation_3d": true, + "disable_physics_2d": true, + "disable_physics_3d": true, + "disable_xr": true, + "graphite": false, + "module_godot_physics_2d_enabled": false, + "module_godot_physics_3d_enabled": false, + "module_jolt_physics_enabled": false, + "module_openxr_enabled": false + }, + "disabled_classes": [ + "AESContext", + "AStar2D", + "AStar3D", + "AStarGrid2D", + "AnimationTree", + "AudioEffect", + "AudioEffectInstance", + "AudioListener3D", + "AudioSample", + "AudioSamplePlayback", + "AudioStreamGenerator", + "AudioStreamInteractive", + "AudioStreamMicrophone", + "AudioStreamPlayback", + "AudioStreamPlayer3D", + "AudioStreamPlaylist", + "AudioStreamPolyphonic", + "AudioStreamRandomizer", + "AudioStreamSynchronized", + "BitMap", + "BoneAttachment3D", + "BoneMap", + "Camera3D", + "CameraAttributes", + "CameraFeed", + "CameraServer", + "CharFXTransform", + "CollisionObject3D", + "CollisionPolygon3D", + "CollisionShape3D", + "ColorPalette", + "Compositor", + "CompositorEffect", + "Crypto", + "CryptoKey", + "Curve3D", + "DTLSServer", + "ENetConnection", + "EncodedObjectAsID", + "EngineProfiler", + "FramebufferCacheRD", + "GDExtension", + "GDExtensionManager", + "GDScriptNativeClass", + "GLTFAccessor", + "GLTFAnimation", + "GLTFBufferView", + "GLTFCamera", + "GLTFDocument", + "GLTFDocumentExtension", + "GLTFLight", + "GLTFMesh", + "GLTFNode", + "GLTFObjectModelProperty", + "GLTFPhysicsBody", + "GLTFPhysicsShape", + "GLTFSkeleton", + "GLTFSkin", + "GLTFSpecGloss", + "GLTFState", + "GLTFTexture", + "GLTFTextureSampler", + "Geometry2D", + "Geometry3D", + "GodotInstance", + "GridMap", + "HMACContext", + "HashingContext", + "IP", + "ImageFormatLoader", + "ImporterMesh", + "ImporterMeshInstance3D", + "IntervalTweener", + "JNISingleton", + "JSONRPC", + "JavaClass", + "JavaClassWrapper", + "JavaObject", + "JavaScriptBridge", + "JavaScriptObject", + "Joint3D", + "JointLimitation3D", + "KinematicCollision2D", + "KinematicCollision3D", + "LightmapGIData", + "LightmapProbe", + "Lightmapper", + "Marker3D", + "Marshalls", + "Material", + "Mesh", + "MeshConvexDecompositionSettings", + "MeshDataTool", + "MeshLibrary", + "MethodTweener", + "MissingNode", + "MissingResource", + "MovieWriter", + "MultiMesh", + "MultiplayerAPI", + "MultiplayerSpawner", + "MultiplayerSynchronizer", + "NativeMenu", + "NavigationAgent2D", + "NavigationAgent3D", + "NavigationLink3D", + "NavigationMesh", + "NavigationMeshGenerator", + "NavigationMeshSourceGeometryData2D", + "NavigationMeshSourceGeometryData3D", + "NavigationObstacle3D", + "NavigationPathQueryParameters2D", + "NavigationPathQueryParameters3D", + "NavigationPathQueryResult2D", + "NavigationPathQueryResult3D", + "NavigationPolygon", + "NavigationRegion3D", + "NavigationServer2D", + "NavigationServer2DManager", + "NavigationServer3D", + "NavigationServer3DManager", + "Node3D", + "Node3DGizmo", + "Noise", + "Occluder3D", + "OccluderPolygon2D", + "OggPacketSequence", + "OggPacketSequencePlayback", + "OpenXRAPIExtension", + "OpenXRAction", + "OpenXRActionMap", + "OpenXRActionSet", + "OpenXRBindingModifier", + "OpenXRCompositionLayer", + "OpenXRExtensionWrapper", + "OpenXRFutureResult", + "OpenXRHand", + "OpenXRHapticBase", + "OpenXRIPBinding", + "OpenXRInteractionProfile", + "OpenXRInteractionProfileMetadata", + "OpenXRRenderModel", + "OpenXRRenderModelManager", + "OpenXRSpatialCapabilityConfigurationBaseHeader", + "OpenXRSpatialComponentData", + "OpenXRStructureBase", + "PCKPacker", + "PackedDataContainer", + "PackedDataContainerRef", + "PacketPeer", + "ParallaxLayer", + "Path3D", + "PathFollow3D", + "PhysicsDirectBodyState2D", + "PhysicsDirectBodyState3D", + "PhysicsDirectSpaceState2D", + "PhysicsDirectSpaceState3D", + "PhysicsMaterial", + "PhysicsPointQueryParameters2D", + "PhysicsPointQueryParameters3D", + "PhysicsRayQueryParameters2D", + "PhysicsRayQueryParameters3D", + "PhysicsServer2D", + "PhysicsServer2DManager", + "PhysicsServer3D", + "PhysicsServer3DManager", + "PhysicsServer3DRenderingServerHandler", + "PhysicsShapeQueryParameters2D", + "PhysicsShapeQueryParameters3D", + "PhysicsTestMotionParameters2D", + "PhysicsTestMotionParameters3D", + "PhysicsTestMotionResult2D", + "PhysicsTestMotionResult3D", + "PolygonPathFinder", + "RDAttachmentFormat", + "RDFramebufferPass", + "RDPipelineColorBlendState", + "RDPipelineColorBlendStateAttachment", + "RDPipelineDepthStencilState", + "RDPipelineMultisampleState", + "RDPipelineRasterizationState", + "RDPipelineSpecializationConstant", + "RDSamplerState", + "RDShaderFile", + "RDShaderSPIRV", + "RDShaderSource", + "RDTextureFormat", + "RDTextureView", + "RDUniform", + "RDVertexAttribute", + "RayCast3D", + "RemoteTransform3D", + "RenderData", + "RenderSceneBuffers", + "RenderSceneBuffersConfiguration", + "RenderSceneData", + "RenderingDevice", + "RenderingServer", + "ResourceFormatLoader", + "ResourceFormatSaver", + "ResourceImporter", + "ResourceUID", + "SceneReplicationConfig", + "SceneState", + "ScriptLanguage", + "Semaphore", + "Shape2D", + "Shape3D", + "ShapeCast3D", + "Skeleton3D", + "SkeletonModification2D", + "SkeletonModificationStack2D", + "SkeletonModifier3D", + "SkeletonProfile", + "Skin", + "SkinReference", + "SpringArm3D", + "SpringBoneCollision3D", + "StreamPeerExtension", + "StreamPeerGZIP", + "StreamPeerTLS", + "StreamPeerUDS", + "SubtweenTweener", + "SurfaceTool", + "TLSOptions", + "TextLine", + "TextParagraph", + "TextServerExtension", + "TextServerManager", + "Texture3D", + "TextureLayered", + "ThemeContext", + "TileData", + "TileMapPattern", + "TileSet", + "TileSetSource", + "TranslationDomain", + "TranslationServer", + "TriangleMesh", + "UDPServer", + "UDSServer", + "UPNP", + "UPNPDevice", + "UniformSetCacheRD", + "VehicleWheel3D", + "VisualInstance3D", + "VisualShader", + "VisualShaderNode", + "VoxelGIData", + "WeakRef", + "WebRTCPeerConnection", + "WorkerThreadPool", + "World2D", + "World3D", + "WorldEnvironment", + "X509Certificate", + "XMLParser", + "XRFaceModifier3D", + "XRInterface", + "XRNode3D", + "XROrigin3D", + "XRPose", + "XRServer", + "XRTracker", + "XRVRS" + ], + "type": "build_profile" +} \ No newline at end of file diff --git a/common/assets/logo/logo_256x256.png.import b/common/assets/logo/logo_256x256.png.import deleted file mode 100644 index 94ba4b54..00000000 --- a/common/assets/logo/logo_256x256.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dec1yarlm1myv" -path="res://.godot/imported/logo_256x256.png-c64139f54d35de8927bbaa3e9e2c151f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_256x256.png" -dest_files=["res://.godot/imported/logo_256x256.png-c64139f54d35de8927bbaa3e9e2c151f.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_256x256.svg.import b/common/assets/logo/logo_256x256.svg.import deleted file mode 100644 index 3cd5ed55..00000000 --- a/common/assets/logo/logo_256x256.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://exl7lwdbgxr2" -path="res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_256x256.svg" -dest_files=["res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/assets/logo/logo_32x32.png.import b/common/assets/logo/logo_32x32.png.import deleted file mode 100644 index ab1f6931..00000000 --- a/common/assets/logo/logo_32x32.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://t1y8o0t6yppw" -path="res://.godot/imported/logo_32x32.png-a9ec36e08c728751a55ec37b05469643.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_32x32.png" -dest_files=["res://.godot/imported/logo_32x32.png-a9ec36e08c728751a55ec37b05469643.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_32x32.svg.import b/common/assets/logo/logo_32x32.svg.import deleted file mode 100644 index 1ed2486e..00000000 --- a/common/assets/logo/logo_32x32.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b35w6abcbrbvw" -path="res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_32x32.svg" -dest_files=["res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/assets/logo/logo_512x512.png b/common/assets/logo/logo_512x512.png deleted file mode 100644 index ec117535..00000000 Binary files a/common/assets/logo/logo_512x512.png and /dev/null differ diff --git a/common/assets/logo/logo_512x512.png.import b/common/assets/logo/logo_512x512.png.import deleted file mode 100644 index 2700cb6e..00000000 --- a/common/assets/logo/logo_512x512.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c7mbfc2ueoceo" -path="res://.godot/imported/logo_512x512.png-dac42d53bf3ffaa08235f733c5844545.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_512x512.png" -dest_files=["res://.godot/imported/logo_512x512.png-dac42d53bf3ffaa08235f733c5844545.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_512x512.svg b/common/assets/logo/logo_512x512.svg deleted file mode 100644 index 758f1915..00000000 --- a/common/assets/logo/logo_512x512.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/common/assets/logo/logo_512x512.svg.import b/common/assets/logo/logo_512x512.svg.import deleted file mode 100644 index f3e3fe43..00000000 --- a/common/assets/logo/logo_512x512.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://d3icwpvi8vr6x" -path="res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_512x512.svg" -dest_files=["res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/assets/logo/logo_72x72.png b/common/assets/logo/logo_72x72.png deleted file mode 100644 index 7a855169..00000000 Binary files a/common/assets/logo/logo_72x72.png and /dev/null differ diff --git a/common/assets/logo/logo_72x72.png.import b/common/assets/logo/logo_72x72.png.import deleted file mode 100644 index 6ea0d2b0..00000000 --- a/common/assets/logo/logo_72x72.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://5ht0vqbdjteb" -path="res://.godot/imported/logo_72x72.png-053db1a5547f3fbe3dc44285da1ce4cc.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_72x72.png" -dest_files=["res://.godot/imported/logo_72x72.png-053db1a5547f3fbe3dc44285da1ce4cc.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_72x72.svg b/common/assets/logo/logo_72x72.svg deleted file mode 100644 index 429e5f8f..00000000 --- a/common/assets/logo/logo_72x72.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/common/assets/logo/logo_72x72.svg.import b/common/assets/logo/logo_72x72.svg.import deleted file mode 100644 index b9b55c67..00000000 --- a/common/assets/logo/logo_72x72.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bn75u1jdkiwu3" -path="res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/assets/logo/logo_72x72.svg" -dest_files=["res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/blur.gdshader b/common/blur.gdshader deleted file mode 100644 index ff41a1e1..00000000 --- a/common/blur.gdshader +++ /dev/null @@ -1,11 +0,0 @@ -shader_type canvas_item; - -uniform float lod: hint_range(0.0, 5.0) = 0.0; -uniform float darkness : hint_range(0.0, 1.0) = 0.5; -uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap; - -void fragment() { - vec4 color = texture(SCREEN_TEXTURE, SCREEN_UV, lod); - color.rgb *= (1.0 - darkness); - COLOR = vec4(color.rgb, 1.0); -} diff --git a/common/blur.gdshader.uid b/common/blur.gdshader.uid deleted file mode 100644 index b4d61024..00000000 --- a/common/blur.gdshader.uid +++ /dev/null @@ -1 +0,0 @@ -uid://de1ql8ahad2ts diff --git a/common/bucket.gd b/common/bucket.gd new file mode 100644 index 00000000..662260ee --- /dev/null +++ b/common/bucket.gd @@ -0,0 +1,96 @@ +## Abstract base class for all descriptor-registry autoload nodes +## (FieldBucket, NodeBucket, CollectionBucket). +## Handles registration, lazy initialization, listing, categorization, and sorting. +## Subclasses must implement _search_types() to populate _descriptors. +@abstract +class_name Bucket extends Node + +var _descriptors: Dictionary = {} +var _is_initialized: bool = false + + +## Register a descriptor. Subclasses may override to perform extra setup +## (e.g. assigning a sequential type_id) before calling super. +func register_descriptor(descriptor: BucketDescriptor) -> void: + if descriptor == null: + push_warning("Attempted to register a null descriptor.") + return + if descriptor.name.is_empty(): + push_warning("Descriptor missing required 'name'.") + return + if _descriptors.has(descriptor.name): + push_warning("Descriptor '%s' already registered." % descriptor.name) + return + _descriptors[descriptor.name] = descriptor + + +## Returns the descriptor for the given name, or null if not found. +func get_descriptor(descriptor_name: String) -> BucketDescriptor: + _ensure_initialized() + return _descriptors.get(descriptor_name) + + +## Returns all registered descriptors sorted alphabetically by display_name. +func list_descriptors() -> Array: + _ensure_initialized() + var values: Array = _descriptors.values() + values.sort_custom(_sort_descriptors) + return values + + +## Returns metadata dictionaries for all registered descriptors. +func list_metadata() -> Array[Dictionary]: + return list_descriptors().map(func(d: BucketDescriptor) -> Dictionary: return d.to_metadata()) + + +## Returns a sorted list of unique category names across all descriptors. +func get_categories(internal: bool = false) -> PackedStringArray: + _ensure_initialized() + var categories: PackedStringArray = [] + for descriptor: BucketDescriptor in _descriptors.values(): + if descriptor.category.begins_with("_") and internal == false: + continue + if descriptor.category in categories: + continue + categories.append(descriptor.category) + categories.sort() + return categories + + +## Returns descriptors belonging to the given category, sorted by display_name. +func get_descriptors_by_category(category: String) -> Array: + _ensure_initialized() + var result: Array = [] + for descriptor: BucketDescriptor in _descriptors.values(): + if descriptor.category == category: + result.append(descriptor) + result.sort_custom(_sort_descriptors) + return result + + +## Clears and re-discovers all descriptors. +func refresh_registry() -> void: + _is_initialized = false + _descriptors.clear() + _on_refresh() + _ensure_initialized() + + +## Called during refresh before re-initialization. Override for custom teardown. +func _on_refresh() -> void: + pass + + +func _ensure_initialized() -> void: + if _is_initialized: + return + _search_types() + _is_initialized = true + + +## Implement this to discover and register all descriptors from the file system. +@abstract func _search_types() -> void + + +static func _sort_descriptors(a: BucketDescriptor, b: BucketDescriptor) -> bool: + return a.display_name.naturalnocasecmp_to(b.display_name) < 0 diff --git a/common/bucket.gd.uid b/common/bucket.gd.uid new file mode 100644 index 00000000..2cb4e3f3 --- /dev/null +++ b/common/bucket.gd.uid @@ -0,0 +1 @@ +uid://bad3e3x1ahpvq diff --git a/common/bucket_descriptor.gd b/common/bucket_descriptor.gd new file mode 100644 index 00000000..966b42dc --- /dev/null +++ b/common/bucket_descriptor.gd @@ -0,0 +1,32 @@ +## Abstract base class for all descriptor objects +## (FieldDescriptor, GraphNodeDescriptor, CollectionDescriptor). +## Provides shared name, display_name, category, tags, and description. +@abstract +class_name BucketDescriptor extends RefCounted + +var name: String = "" +var display_name: String = "" +var category: String = "General" +var tags: Array[String] = [] +var description: String = "" + + +func _init(p_name: String = "", metadata: Dictionary = {}) -> void: + name = p_name + display_name = metadata.get("display_name", Util.to_readable_name(name)) + category = metadata.get("category", category) + description = metadata.get("description", "") + if metadata.has("tags") and metadata["tags"] is Array: + tags = metadata["tags"].duplicate(true) + + +## Returns a serializable Dictionary of common descriptor metadata. +## Subclasses should call super.to_metadata() and merge their own fields. +func to_metadata() -> Dictionary: + return { + "name": name, + "display_name": display_name, + "category": category, + "description": description, + "tags": tags.duplicate(true), + } diff --git a/common/bucket_descriptor.gd.uid b/common/bucket_descriptor.gd.uid new file mode 100644 index 00000000..6e7d2e2c --- /dev/null +++ b/common/bucket_descriptor.gd.uid @@ -0,0 +1 @@ +uid://b8x6awqymns0o diff --git a/common/collections/bezier/bezier_item.gd b/common/collections/bezier/bezier_item.gd new file mode 100644 index 00000000..379cac14 --- /dev/null +++ b/common/collections/bezier/bezier_item.gd @@ -0,0 +1,28 @@ +class_name BezierCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "new bezier", + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property("bezier", [0.25, 0.10, 0.25, 1.0], "bezier") + + +func get_type() -> String: + return "bezier" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name"] diff --git a/common/collections/bezier/bezier_item.gd.uid b/common/collections/bezier/bezier_item.gd.uid new file mode 100644 index 00000000..de764efa --- /dev/null +++ b/common/collections/bezier/bezier_item.gd.uid @@ -0,0 +1 @@ +uid://cyubx671gs3ng diff --git a/common/collections/bezier/index.gd b/common/collections/bezier/index.gd new file mode 100644 index 00000000..7343c849 --- /dev/null +++ b/common/collections/bezier/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "beziers", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://cyubx671gs3ng") diff --git a/common/collections/bezier/index.gd.uid b/common/collections/bezier/index.gd.uid new file mode 100644 index 00000000..c732d4dc --- /dev/null +++ b/common/collections/bezier/index.gd.uid @@ -0,0 +1 @@ +uid://c6g8l3sfhpqle diff --git a/common/collections/character/character_item.gd b/common/collections/character/character_item.gd new file mode 100644 index 00000000..d057bc2f --- /dev/null +++ b/common/collections/character/character_item.gd @@ -0,0 +1,49 @@ +class_name CharacterCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + var default_portrait: CollectionItem = CollectionBucket.create_item("portraits", history) + default_portrait.set_property_value("name", "default") + default_portrait.set_property_value("protected", true) + + define_property( + "name", + NameGenerator.generate, + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property("display_name", "", "text") + define_property("nicknames", "", "text") + define_property( + "default_portrait", + "", + "dropdown", + { "source": "self:portraits" }, + "Portraits" + ) + define_property( + "portraits", + [default_portrait._to_dict()], + "collection", + { "collection": "portraits" }, + "Portraits" + ) + define_property("description", "", "textarea", {}, "Extra") + define_property("protected", false, "bool", { "visible_in_inspector": false }, "Extra") + + +func get_type() -> String: + return "character" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name", "description"] diff --git a/common/collections/character/character_item.gd.uid b/common/collections/character/character_item.gd.uid new file mode 100644 index 00000000..d91b6e3f --- /dev/null +++ b/common/collections/character/character_item.gd.uid @@ -0,0 +1 @@ +uid://bqu0a4ofohwe4 diff --git a/common/collections/character/index.gd b/common/collections/character/index.gd new file mode 100644 index 00000000..539fbf54 --- /dev/null +++ b/common/collections/character/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "characters", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://bqu0a4ofohwe4") diff --git a/common/collections/character/index.gd.uid b/common/collections/character/index.gd.uid new file mode 100644 index 00000000..a1215566 --- /dev/null +++ b/common/collections/character/index.gd.uid @@ -0,0 +1 @@ +uid://k3xnygnj71if diff --git a/common/collections/collection_bucket.gd b/common/collections/collection_bucket.gd new file mode 100644 index 00000000..db633800 --- /dev/null +++ b/common/collections/collection_bucket.gd @@ -0,0 +1,53 @@ +# Autoload +extends Bucket + +const DEFAULT_COLLECTIONS_LOCATION: String = "res://common/collections/" + + +func register_descriptor(descriptor: BucketDescriptor) -> void: + if descriptor == null or descriptor is not CollectionDescriptor: + push_warning("Attempted to register a null CollectionDescriptor.") + return + if not descriptor.default_settings: + descriptor.default_settings = {} + super.register_descriptor(descriptor) + + +func create_item(descriptor_name: String, history: CommandManager) -> CollectionItem: + var descriptor: CollectionDescriptor = get_descriptor(descriptor_name) as CollectionDescriptor + if descriptor: + return descriptor.instantiate_item(history) + push_warning("No collection descriptor registered for '%s'." % descriptor_name) + return null + + +func _search_types() -> void: + var directories: Array = DirAccess.get_directories_at(DEFAULT_COLLECTIONS_LOCATION) + for dir_path: String in directories: + var index_path: String = DEFAULT_COLLECTIONS_LOCATION.path_join(dir_path).path_join("index.gd") + if not FileAccess.file_exists(index_path): + continue + var index_script: GDScript = load(index_path) + if index_script == null: + push_warning("Failed to load collection indexer at %s" % index_path) + continue + var indexer: MonologueIndexer = index_script.new() + var descriptor: CollectionDescriptor = _descriptor_from_indexer(indexer) + if descriptor: + register_descriptor(descriptor) + + +func _descriptor_from_indexer(indexer: MonologueIndexer) -> CollectionDescriptor: + var metadata: Dictionary = indexer.get_metadata() + if metadata.get("type") != MonologueIndexer.ObjectType.COLLECTION: + return null + var descriptor_name: String = metadata.get("name", "") + if descriptor_name.is_empty(): + return null + var script_resource: Script + if indexer.has_method("get_collection_item_script"): + script_resource = indexer.call("get_collection_item_script") + if script_resource == null: + push_warning("Indexer for '%s' missing script reference." % descriptor_name) + return null + return CollectionDescriptor.new(descriptor_name, script_resource, metadata) diff --git a/common/collections/collection_bucket.gd.uid b/common/collections/collection_bucket.gd.uid new file mode 100644 index 00000000..f7a5ece9 --- /dev/null +++ b/common/collections/collection_bucket.gd.uid @@ -0,0 +1 @@ +uid://dl72pka6rakjc diff --git a/common/collections/collection_descriptor.gd b/common/collections/collection_descriptor.gd new file mode 100644 index 00000000..985a2829 --- /dev/null +++ b/common/collections/collection_descriptor.gd @@ -0,0 +1,24 @@ +class_name CollectionDescriptor extends BucketDescriptor + +var collection_script: GDScript +var default_settings: Dictionary = {} + + +func _init(p_name: String = "", p_script: Script = null, metadata: Dictionary = {}) -> void: + super._init(p_name, metadata) + collection_script = p_script + + +func instantiate_item(history: CommandManager) -> CollectionItem: + if collection_script == null: + push_warning("Descriptor '%s' missing script for instantiation." % name) + return null + var instance: CollectionItem = collection_script.new(history) + if instance is CollectionItem: + return instance + push_error("Descriptor '%s' does not create an InspectableNode." % name) + return null + + +func to_metadata() -> Dictionary: + return super.to_metadata() diff --git a/common/collections/collection_descriptor.gd.uid b/common/collections/collection_descriptor.gd.uid new file mode 100644 index 00000000..65b9a158 --- /dev/null +++ b/common/collections/collection_descriptor.gd.uid @@ -0,0 +1 @@ +uid://dvq6scnchvq72 diff --git a/common/collections/item/index.gd b/common/collections/item/index.gd new file mode 100644 index 00000000..e319a8c3 --- /dev/null +++ b/common/collections/item/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "items", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://y8hn47i11ixd") diff --git a/common/collections/item/index.gd.uid b/common/collections/item/index.gd.uid new file mode 100644 index 00000000..b09569be --- /dev/null +++ b/common/collections/item/index.gd.uid @@ -0,0 +1 @@ +uid://bl0ow8mjo160d diff --git a/common/collections/item/item_item.gd b/common/collections/item/item_item.gd new file mode 100644 index 00000000..45bbf83f --- /dev/null +++ b/common/collections/item/item_item.gd @@ -0,0 +1,29 @@ +class_name ItemCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "new item", + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property("display_name", "", "text") + define_property("description", "", "textarea", {}, "Extra") + + +func get_type() -> String: + return "item" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name", "description"] diff --git a/common/collections/item/item_item.gd.uid b/common/collections/item/item_item.gd.uid new file mode 100644 index 00000000..536e2309 --- /dev/null +++ b/common/collections/item/item_item.gd.uid @@ -0,0 +1 @@ +uid://y8hn47i11ixd diff --git a/common/collections/language/index.gd b/common/collections/language/index.gd new file mode 100644 index 00000000..55c120ff --- /dev/null +++ b/common/collections/language/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "languages", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("res://common/collections/language/language_item.gd") diff --git a/common/collections/language/index.gd.uid b/common/collections/language/index.gd.uid new file mode 100644 index 00000000..1faa0d9d --- /dev/null +++ b/common/collections/language/index.gd.uid @@ -0,0 +1 @@ +uid://bc4tdx32bmdk7 diff --git a/common/collections/language/language_item.gd b/common/collections/language/language_item.gd new file mode 100644 index 00000000..64497ba6 --- /dev/null +++ b/common/collections/language/language_item.gd @@ -0,0 +1,33 @@ +class_name LanguageCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "New Language", + "text", + { + "unique": true, + "placeholder": "English", + "validation": {"min_length": 1}, + } + ) + define_property( + "code", + "nl", + "text", + { + "unique": true, + "placeholder": "en", + "validation": {"min_length": 1, "max_length": 4}, + } + ) + define_property("protected", false, "bool", {"visible_in_inspector": false}, "Extra") + + +func get_type() -> String: + return "language" + + +func get_preview_property_names() -> Array[String]: + return ["name", "code"] diff --git a/common/collections/language/language_item.gd.uid b/common/collections/language/language_item.gd.uid new file mode 100644 index 00000000..e227653a --- /dev/null +++ b/common/collections/language/language_item.gd.uid @@ -0,0 +1 @@ +uid://bn7fjbu7luvrr diff --git a/common/collections/location/index.gd b/common/collections/location/index.gd new file mode 100644 index 00000000..8224cb97 --- /dev/null +++ b/common/collections/location/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "locations", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://bcl51sy34f52y") diff --git a/common/collections/location/index.gd.uid b/common/collections/location/index.gd.uid new file mode 100644 index 00000000..29cbc1fd --- /dev/null +++ b/common/collections/location/index.gd.uid @@ -0,0 +1 @@ +uid://o4kanmp2a050 diff --git a/common/collections/location/location_item.gd b/common/collections/location/location_item.gd new file mode 100644 index 00000000..40c438b0 --- /dev/null +++ b/common/collections/location/location_item.gd @@ -0,0 +1,29 @@ +class_name LocationCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "new location", + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property("display_name", "", "text") + define_property("description", "", "textarea", {}, "Extra") + + +func get_type() -> String: + return "location" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name", "description"] diff --git a/common/collections/location/location_item.gd.uid b/common/collections/location/location_item.gd.uid new file mode 100644 index 00000000..17331135 --- /dev/null +++ b/common/collections/location/location_item.gd.uid @@ -0,0 +1 @@ +uid://bcl51sy34f52y diff --git a/common/collections/option/index.gd b/common/collections/option/index.gd new file mode 100644 index 00000000..92736e82 --- /dev/null +++ b/common/collections/option/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "option", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://dhodjihl1pyka") diff --git a/common/collections/option/index.gd.uid b/common/collections/option/index.gd.uid new file mode 100644 index 00000000..af8f59f7 --- /dev/null +++ b/common/collections/option/index.gd.uid @@ -0,0 +1 @@ +uid://onrikkq33lbq diff --git a/common/collections/option/option_item.gd b/common/collections/option/option_item.gd new file mode 100644 index 00000000..a758079e --- /dev/null +++ b/common/collections/option/option_item.gd @@ -0,0 +1,24 @@ +class_name OptionCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_main_property("option", "option") + define_property("text", {}, "translatable", {"multiline": true}) + define_property("correspondent", "", "dropdown", {"source": "characters"}) + define_property("enabled", true, "bool", {}, "Advanced") + define_property("one_shot", false, "bool", {}, "Advanced") + define_property("enable_condition", false, "bool", {}, "Advanced") + define_property("condition", {}, "condition", {}, "Advanced") + define_property("description", "", "textarea", {}, "Extra") + + +func get_type() -> String: + return "option" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["text"] diff --git a/common/collections/option/option_item.gd.uid b/common/collections/option/option_item.gd.uid new file mode 100644 index 00000000..e3f9eeb8 --- /dev/null +++ b/common/collections/option/option_item.gd.uid @@ -0,0 +1 @@ +uid://dhodjihl1pyka diff --git a/common/collections/portrait/index.gd b/common/collections/portrait/index.gd new file mode 100644 index 00000000..17e17dc7 --- /dev/null +++ b/common/collections/portrait/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "portraits", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://brhhri86u8h56") diff --git a/common/collections/portrait/index.gd.uid b/common/collections/portrait/index.gd.uid new file mode 100644 index 00000000..fdbf35e7 --- /dev/null +++ b/common/collections/portrait/index.gd.uid @@ -0,0 +1 @@ +uid://cvsqg8songyas diff --git a/common/collections/portrait/portrait_item.gd b/common/collections/portrait/portrait_item.gd new file mode 100644 index 00000000..9a0d8105 --- /dev/null +++ b/common/collections/portrait/portrait_item.gd @@ -0,0 +1,28 @@ +class_name PortraitCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "new portrait", + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property("protected", false, "bool", { "visible_in_inspector": false }, "Extra") + + +func get_type() -> String: + return "portrait" + + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name"] diff --git a/common/collections/portrait/portrait_item.gd.uid b/common/collections/portrait/portrait_item.gd.uid new file mode 100644 index 00000000..aec0255b --- /dev/null +++ b/common/collections/portrait/portrait_item.gd.uid @@ -0,0 +1 @@ +uid://brhhri86u8h56 diff --git a/common/collections/variable/index.gd b/common/collections/variable/index.gd new file mode 100644 index 00000000..d5d7a5a1 --- /dev/null +++ b/common/collections/variable/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return {"name": "variables", "type": ObjectType.COLLECTION} + + +func get_collection_item_script() -> Script: + return preload("uid://b8gmqtdncql4s") diff --git a/common/collections/variable/index.gd.uid b/common/collections/variable/index.gd.uid new file mode 100644 index 00000000..00e9acad --- /dev/null +++ b/common/collections/variable/index.gd.uid @@ -0,0 +1 @@ +uid://dlrc8huv7sw1t diff --git a/common/collections/variable/variable_item.gd b/common/collections/variable/variable_item.gd new file mode 100644 index 00000000..fb8c655e --- /dev/null +++ b/common/collections/variable/variable_item.gd @@ -0,0 +1,50 @@ +class_name VariableCollectionItem extends CollectionItem + + +func initialize_properties() -> void: + define_property( + "name", + "new variable", + "text", + { + "required": true, + "unique": true, + "protect": true, + "validation": {"min_length": 1}, + }, + ) + define_property( + "type", + "string", + "dropdown", + { + "required": true, + "options": ["bool", "string", "int", "float"] + } + ) + define_property( + "value", + "", + "dynamic", + { + PropertySettings.KEY_CASE_PROPERTY: "type", + PropertySettings.KEY_CASES: { + "bool": {"type": "bool", "default": false}, + "string": {"type": "text", "default": "", "coerce": "string"}, + "int": {"type": "int", "default": 0, "coerce": "int"}, + "float": {"type": "float", "default": 0.0, "coerce": "float"} + } + } + ) + define_property("description", "", "textarea", {}, "Extra") + + +func get_type() -> String: + return "variable" + +func _on_property_changed(_pname: String) -> void: + pass + + +func get_preview_property_names() -> Array[String]: + return ["name", "type", "value"] diff --git a/common/collections/variable/variable_item.gd.uid b/common/collections/variable/variable_item.gd.uid new file mode 100644 index 00000000..06acd6d3 --- /dev/null +++ b/common/collections/variable/variable_item.gd.uid @@ -0,0 +1 @@ +uid://b8gmqtdncql4s diff --git a/common/connection_manager.gd b/common/connection_manager.gd new file mode 100644 index 00000000..d7d73072 --- /dev/null +++ b/common/connection_manager.gd @@ -0,0 +1,386 @@ +class_name ConnectionManager extends RefCounted + +## Manages connections between node properties for easy access and validation + +var _storyline: StorylineDocument +var _suspended_connections: Dictionary = {} + + +func _init(storyline: StorylineDocument) -> void: + _storyline = storyline + + +## Splits a composite property name like "choices:item_id" into [base_name, item_id]. +## Returns [property_name, ""] for simple names. +static func parse_composite_name(property_name: String) -> Array: + if ":" in property_name: + var parts: PackedStringArray = property_name.split(":", true, 1) + return [parts[0], parts[1]] + return [property_name, ""] + + +## Updates property connection tracking when a connection is made (using property names) +## All parameters are node_ids, not scene node names. +## Supports composite property names like "choices:item_id" for sub-ports. +func register_connection_by_property( + from_node_id: String, from_property_name: String, to_node_id: String, to_property_name: String +) -> void: + var from_node: InspectableNode = _get_node_view_by_id(from_node_id) + var to_node: InspectableNode = _get_node_view_by_id(to_node_id) + + if not from_node or not to_node: + push_warning("Cannot register connection: node not found") + return + + # Resolve composite sub-port names + var from_parsed: Array = parse_composite_name(from_property_name) + var from_base_name: String = from_parsed[0] + var from_item_id: String = from_parsed[1] + + var to_parsed: Array = parse_composite_name(to_property_name) + var to_base_name: String = to_parsed[0] + var to_item_id: String = to_parsed[1] + + var from_prop: Property = from_node.get_property(from_base_name) + var to_prop: Property = to_node.get_property(to_base_name) + + if not from_prop or not to_prop: + push_warning("Cannot register connection: property not found") + return + + # Build connection dict, including item_id if it's a sub-port + var to_conn: Dictionary = {"node_id": to_node_id, "property_name": to_property_name} + if not from_item_id.is_empty(): + to_conn["item_id"] = from_item_id + var from_conn: Dictionary = {"node_id": from_node_id, "property_name": from_property_name} + if not to_item_id.is_empty(): + from_conn["item_id"] = to_item_id + + if to_conn not in from_prop.connected_to: + from_prop.connected_to.append(to_conn) + from_prop.connection_changed.emit() + if from_conn not in to_prop.connected_from: + to_prop.connected_from.append(from_conn) + to_prop.connection_changed.emit() + + +## Updates property connection tracking when a connection is made (using port indices - legacy) +## Parameters are node_ids for compatibility with GraphEdit using ids for names. +func register_connection( + from_node_id: String, from_port: int, to_node_id: String, to_port: int +) -> void: + var from_node: InspectableNode = _get_node_view_by_id(from_node_id) + var to_node: InspectableNode = _get_node_view_by_id(to_node_id) + + if not from_node or not to_node: + push_warning("Cannot register connection: node not found") + return + + var from_prop: Property = _get_property_at_port(from_node, from_port) + var to_prop: Property = _get_property_at_port(to_node, to_port) + + if not from_prop or not to_prop: + push_warning("Cannot register connection: property not found") + return + + # Track the connection using property names + register_connection_by_property(from_node_id, from_prop.name, to_node_id, to_prop.name) + + +## Updates property connection tracking when a connection is removed (using property names) +## All parameters are node_ids, not scene node names. +## Supports composite property names like "choices:item_id" for sub-ports. +func unregister_connection_by_property( + from_node_id: String, from_property_name: String, to_node_id: String, to_property_name: String +) -> void: + var from_node: InspectableNode = _get_node_view_by_id(from_node_id) + var to_node: InspectableNode = _get_node_view_by_id(to_node_id) + + if not from_node or not to_node: + return + + # Resolve composite sub-port names + var from_base_name: String = parse_composite_name(from_property_name)[0] + var to_base_name: String = parse_composite_name(to_property_name)[0] + + var from_prop: Property = from_node.get_property(from_base_name) + var to_prop: Property = to_node.get_property(to_base_name) + + if not from_prop or not to_prop: + return + + # Remove the connection from both properties, matching full property_name (with sub-port) + var old_to_size: int = from_prop.connected_to.size() + from_prop.connected_to.assign(from_prop.connected_to.filter( + func(c: Dictionary) -> bool: return not (c["node_id"] == to_node_id and c["property_name"] == to_property_name) + )) + if from_prop.connected_to.size() != old_to_size: + from_prop.connection_changed.emit() + + var old_from_size: int = to_prop.connected_from.size() + to_prop.connected_from.assign(to_prop.connected_from.filter( + func(c: Dictionary) -> bool: + return not (c["node_id"] == from_node_id and c["property_name"] == from_property_name) + )) + if to_prop.connected_from.size() != old_from_size: + to_prop.connection_changed.emit() + + +## Updates property connection tracking when a connection is removed (using port indices - legacy) +## Parameters are node_ids for compatibility. +func unregister_connection( + from_node_id: String, from_port: int, to_node_id: String, to_port: int +) -> void: + var from_node: InspectableNode = _get_node_view_by_id(from_node_id) + var to_node: InspectableNode = _get_node_view_by_id(to_node_id) + + if not from_node or not to_node: + return + + var from_prop: Property = _get_property_at_port(from_node, from_port) + var to_prop: Property = _get_property_at_port(to_node, to_port) + + if not from_prop or not to_prop: + return + + # Remove the connection using property names + unregister_connection_by_property(from_node_id, from_prop.name, to_node_id, to_prop.name) + + +## Gets all connections tracked by properties +## Returns array of {from_node_id, from_property, to_node_id, to_property} +func get_all_connections() -> Array[Dictionary]: + var connections: Array[Dictionary] = [] + var processed_connections: Dictionary = {} # To avoid duplicates + + for node: InspectableNode in _storyline.nodes: + var node_id: String = node.get_property("id").get_value() + for prop: Property in node.get_properties(): + # Only process outgoing connections to avoid duplicates + for conn: Dictionary in prop.connected_to: + # Reconstruct composite from_property for sub-port connections + var from_property: String = prop.name + var item_id: String = conn.get("item_id", "") + if not item_id.is_empty(): + from_property = "%s:%s" % [prop.name, item_id] + var key: String = ( + "%s.%s->%s.%s" % [node_id, from_property, conn["node_id"], conn["property_name"]] + ) + if key not in processed_connections: + connections.append( + { + "from_node_id": node_id, + "from_property": from_property, + "to_node_id": conn["node_id"], + "to_property": conn["property_name"] + } + ) + processed_connections[key] = true + + return connections + + +## Gets all properties that are connected (have at least one connection) +func get_connected_properties() -> Array[Property]: + var connected: Array[Property] = [] + for node: InspectableNode in _storyline.nodes: + for prop: Property in node.get_properties(): + if prop.is_port_connected(): + connected.append(prop) + return connected + + +## Gets the node a property is connected to (for single connection) +func get_connected_node(node: InspectableNode, property_name: String) -> InspectableNode: + var prop: Property = node.get_property(property_name) + if not prop or not prop.is_port_connected(): + return null + + # Get first connection (assuming single connection for simplicity) + var connection: Variant = null + if prop.connected_to.size() > 0: + connection = prop.connected_to[0] + elif prop.connected_from.size() > 0: + connection = prop.connected_from[0] + + if connection: + return _get_node_view_by_id(str(connection["node_id"])) + + return null + + +## Validates all connections are still valid (nodes and properties exist) +func validate_connections() -> bool: + var all_valid: bool = true + for node: InspectableNode in _storyline.nodes: + for prop: Property in node.get_properties(): + # Check outgoing connections + for conn: Dictionary in prop.connected_to: + var target_node: InspectableNode = _get_node_view_by_id(str(conn["node_id"])) + if not target_node or not target_node.get_property(str(conn["property_name"])): + push_warning( + ( + "Invalid connection found from %s.%s" + % [node.get_property_value("id"), prop.name] + ) + ) + all_valid = false + + # Check incoming connections + for conn: Dictionary in prop.connected_from: + var source_node: InspectableNode = _get_node_view_by_id(str(conn["node_id"])) + if not source_node or not source_node.get_property(str(conn["property_name"])): + push_warning( + ( + "Invalid connection found to %s.%s" + % [node.get_property_value("id"), prop.name] + ) + ) + all_valid = false + + return all_valid + + +## Rename a node id across all connection references and suspended snapshots +func rename_node_id(old_id: String, new_id: String) -> void: + if old_id == new_id or old_id.is_empty() or new_id.is_empty(): + return + + # Update connections on all properties + for node: InspectableNode in _storyline.nodes: + for prop: Property in node.get_properties(): + _update_connection_node_id(prop.connected_to, old_id, new_id) + _update_connection_node_id(prop.connected_from, old_id, new_id) + + # Update suspended connection snapshots + var keys: Array = _suspended_connections.keys() + for k: String in keys: + var parts: Array = k.split("::") + if parts.size() != 2: + continue + if parts[0] != old_id: + continue + var new_key: String = "%s::%s" % [new_id, parts[1]] + var snapshot: Dictionary = _suspended_connections[k] + for dir: String in ["incoming", "outgoing"]: + if snapshot.has(dir): + var dir_val: Variant = snapshot[dir] + if dir_val is Array: + var dir_conns: Array = dir_val + _update_connection_node_id(dir_conns, old_id, new_id) + _suspended_connections.erase(k) + _suspended_connections[new_key] = snapshot + + +## Replaces old_id with new_id in an array of connection dictionaries. +static func _update_connection_node_id( + connections: Array, old_id: String, new_id: String +) -> void: + for i: int in range(connections.size()): + var conn: Dictionary = connections[i] + if conn.get("node_id", "") == old_id: + conn["node_id"] = new_id + connections[i] = conn + + +func suspend_incoming_property_connections(node_id: String, property_name: String) -> void: + _suspend_property_connections(node_id, property_name, "incoming", "connected_from", true) + + +func suspend_outgoing_property_connections(node_id: String, property_name: String) -> void: + _suspend_property_connections(node_id, property_name, "outgoing", "connected_to", false) + + +func restore_incoming_property_connections(node_id: String, property_name: String) -> void: + _restore_property_connections(node_id, property_name, "incoming", true) + + +func restore_outgoing_property_connections(node_id: String, property_name: String) -> void: + _restore_property_connections(node_id, property_name, "outgoing", false) + + +func _suspend_property_connections( + node_id: String, + property_name: String, + direction: String, + connection_array_name: String, + is_incoming: bool +) -> void: + var key: String = _connection_key(node_id, property_name) + if not _suspended_connections.has(key): + _suspended_connections[key] = {} + + var node: InspectableNode = _get_node_view_by_id(node_id) + if not node: + return + + var prop: Property = node.get_property(property_name) + if not prop: + return + + var connections: Array = [] + var prop_val: Variant = prop.get(connection_array_name) + if prop_val is Array: + var prop_arr: Array = prop_val + connections = prop_arr.duplicate(true) + if connections.is_empty(): + return + + _suspended_connections[key][direction] = connections + + # Unregister each connection + for conn_dict: Dictionary in connections: + var other_node: String = conn_dict.get("node_id", "") + var other_property: String = conn_dict.get("property_name", "") + if other_node.is_empty() or other_property.is_empty(): + continue + + if is_incoming: + unregister_connection_by_property(other_node, other_property, node_id, property_name) + else: + unregister_connection_by_property(node_id, property_name, other_node, other_property) + + +func _restore_property_connections( + node_id: String, property_name: String, direction: String, is_incoming: bool +) -> void: + var key: String = _connection_key(node_id, property_name) + if not _suspended_connections.has(key): + return + + var snapshot: Dictionary = _suspended_connections[key] + _suspended_connections.erase(key) + + # Re-register each connection + var connections: Array = snapshot.get(direction, []) + for conn_dict: Dictionary in connections: + var other_node: String = conn_dict.get("node_id", "") + var other_property: String = conn_dict.get("property_name", "") + if other_node.is_empty() or other_property.is_empty(): + continue + + if is_incoming: + register_connection_by_property(other_node, other_property, node_id, property_name) + else: + register_connection_by_property(node_id, property_name, other_node, other_property) + + +func _connection_key(node_id: String, property_name: String) -> String: + return "%s::%s" % [node_id, property_name] + + +func _get_node_view_by_id(node_id: String) -> InspectableNode: + for node: InspectableNode in _storyline.nodes: + if node.get_property("id").get_value() == node_id: + return node + return null + + +## Helper: Get property at a specific port index in the graph view +func _get_property_at_port(node: InspectableNode, port: int) -> Property: + var visible_props: Array[Property] = node.get_visible_properties() + + if port >= 0 and port < visible_props.size(): + return visible_props[port] + + return null diff --git a/common/connection_manager.gd.uid b/common/connection_manager.gd.uid new file mode 100644 index 00000000..2bff7438 --- /dev/null +++ b/common/connection_manager.gd.uid @@ -0,0 +1 @@ +uid://cve25hgfr76tx diff --git a/common/documents/collection_document.gd b/common/documents/collection_document.gd new file mode 100644 index 00000000..47348f6a --- /dev/null +++ b/common/documents/collection_document.gd @@ -0,0 +1,21 @@ +class_name CollectionDocument extends InspectableDocument + +var name: String = "" +var _default_value: Array = [] + +func _init(collection_name: String, default_value: Array = [], command_manager: CommandManager = null) -> void: + name = collection_name + _default_value = default_value + super._init(command_manager) + + +func initialize_properties() -> void: + define_property(name, _default_value, "collection", { "collection": name }) + + +func get_value() -> Array: + return get_property_value(name) + + +func get_type() -> String: + return "collection" diff --git a/common/documents/collection_document.gd.uid b/common/documents/collection_document.gd.uid new file mode 100644 index 00000000..4a61d71a --- /dev/null +++ b/common/documents/collection_document.gd.uid @@ -0,0 +1 @@ +uid://cobs75fxb7taw diff --git a/common/documents/config_document.gd b/common/documents/config_document.gd new file mode 100644 index 00000000..ec4395f5 --- /dev/null +++ b/common/documents/config_document.gd @@ -0,0 +1,17 @@ +class_name ConfigurationDocument extends InspectableDocument + +func initialize_properties() -> void: + define_property("show_project_explorer", true, "bool", {}, "Interface") + define_property("show_inspector", true, "bool", {}, "Interface") + define_property("show_console", false, "bool", {}, "Interface") + define_property("show_status_bar", true, "bool", {}, "Interface") + + define_property("snap", false, "bool", {}, "Graph") + define_property("show_grid", true, "bool", {}, "Graph") + +func set_property_value(pname: String, pvalue: Variant) -> void: + super.set_property_value(pname, pvalue) + ConfigManager.save_configuration() + +func get_type() -> String: + return "preferences" diff --git a/common/documents/config_document.gd.uid b/common/documents/config_document.gd.uid new file mode 100644 index 00000000..aefc0d13 --- /dev/null +++ b/common/documents/config_document.gd.uid @@ -0,0 +1 @@ +uid://c7q5sondh3ww3 diff --git a/common/documents/inspectable_document.gd b/common/documents/inspectable_document.gd new file mode 100644 index 00000000..be8e77d1 --- /dev/null +++ b/common/documents/inspectable_document.gd @@ -0,0 +1,40 @@ +@abstract +class_name InspectableDocument extends InspectableObject + +const FILE_FORMAT: String = "mnlf" + +signal content_changed +signal undo_redo_changed + +var is_dirty: bool = false + + +func _init(command_manager: CommandManager) -> void: + property_changed.connect(_on_property_changed) + command_manager.command_executed.connect(_on_command_executed) + command_manager.undone.connect(_on_undo) + command_manager.redone.connect(_on_redo) + + super._init(command_manager) + + +func _on_property_changed(_pname: String) -> void: + is_dirty = true + content_changed.emit() + + +func _on_command_executed() -> void: + is_dirty = true + content_changed.emit() + undo_redo_changed.emit() + + +func _on_undo() -> void: + content_changed.emit() + undo_redo_changed.emit() + + +func _on_redo() -> void: + is_dirty = true + content_changed.emit() + undo_redo_changed.emit() diff --git a/common/documents/inspectable_document.gd.uid b/common/documents/inspectable_document.gd.uid new file mode 100644 index 00000000..6015b309 --- /dev/null +++ b/common/documents/inspectable_document.gd.uid @@ -0,0 +1 @@ +uid://c4pgnm3s0fk7m diff --git a/common/documents/manifest_document.gd b/common/documents/manifest_document.gd new file mode 100644 index 00000000..7f3938f7 --- /dev/null +++ b/common/documents/manifest_document.gd @@ -0,0 +1,15 @@ +class_name ManifestDocument extends InspectableDocument + + +func initialize_properties() -> void: + var editor_version: Array = ProjectSettings.get_setting("application/config/version").split(".") + define_property("editor_version", editor_version, "list", { + "show_in_inspector": false + }) + define_property("entry_point", "", "dropdown") # Storyline entry point + define_property("author", "", "text") + define_property("description", "", "textarea") + + +func get_type() -> String: + return "manifest" diff --git a/common/documents/manifest_document.gd.uid b/common/documents/manifest_document.gd.uid new file mode 100644 index 00000000..691a2c18 --- /dev/null +++ b/common/documents/manifest_document.gd.uid @@ -0,0 +1 @@ +uid://4b4x4kcbsk71 diff --git a/common/documents/monologue_project.gd b/common/documents/monologue_project.gd new file mode 100644 index 00000000..f48c3823 --- /dev/null +++ b/common/documents/monologue_project.gd @@ -0,0 +1,316 @@ +class_name MonologueProject extends Resource + +const FILE_FORMAT: String = "mnlp" +const FORMAT_FILTER: Array = ["*.mnlp;Monologue Project", "*.%s;Monologue File" % InspectableDocument.FILE_FORMAT] + +signal ready +signal content_changed +signal undo_redo_changed +signal _project_path_changed + +var manifest: ManifestDocument +var settings: ProjectSettingsDocument +var collections: Array[CollectionDocument] +var storylines: Array[StorylineDocument] +var compact: bool = true + +var command_manager: CommandManager = CommandManager.new() +var name: String = "unsaved" +var project_path: String = "" +var is_dirty: bool = false +var active_language_code: String = "en" + + +func _init() -> void: + _init_documents.call_deferred() + + command_manager.command_executed.connect(_on_command_executed) + command_manager.undone.connect(_on_undo) + command_manager.redone.connect(_on_redo) + + +func _init_documents() -> void: + manifest = ManifestDocument.new(command_manager) + settings = ProjectSettingsDocument.new(command_manager) + storylines.append(StorylineDocument.new("main", command_manager)) + _init_collections() + + ready.emit() + + +func get_all_documents() -> Array[InspectableDocument]: + var documents: Array[InspectableDocument] = [] + documents.append(manifest) + documents.append(settings) + documents.append_array(collections) + documents.append_array(storylines) + return documents + + +func get_documents() -> Array[InspectableDocument]: + var documents: Array[InspectableDocument] = [] + documents.append(manifest) + documents.append_array(collections) + documents.append_array(storylines) + return documents + + +func get_project_structure() -> Dictionary[String, Variant]: + var structure: Dictionary[String, Variant] = {} + structure["manifest.json"] = manifest + structure["settings.json"] = settings + + var collections_map: Dictionary = {} + for collection: CollectionDocument in collections: + collections_map["%s.json" % collection.name] = collection + structure["collections"] = collections_map + + var storylines_map: Dictionary = {} + for storyline: StorylineDocument in storylines: + storylines_map["%s.json" % storyline.name] = storyline + structure["storylines"] = storylines_map + + return structure + + +func _init_collections() -> void: + var default_narrator: CollectionItem = CollectionBucket.create_item("characters", command_manager) + default_narrator.set_property_value("name", "Narrator") + default_narrator.set_property_value("protected", true) + + var default_language: CollectionItem = CollectionBucket.create_item("languages", command_manager) + default_language.set_property_value("name", "English") + default_language.set_property_value("code", "en") + default_language.set_property_value("protected", true) + + var default_beziers: Dictionary = { + "Ease": [0.25, 0.10, 0.25, 1.0], + "Linear": [0.0, 0.0, 1.0, 1.0], + "Ease-In": [0.42, 0.0, 1.0, 1.0], + "Ease-Out": [0.0, 0.0, 0.58, 1.0], + "Ease-In-Out": [0.42, 0.0, 0.58, 1.0] + } + var beziers_data: Array = [] + for bezier_name: String in default_beziers: + var bezier_item: CollectionItem = CollectionBucket.create_item("beziers", command_manager) + bezier_item.set_property_value("name", bezier_name) + bezier_item.set_property_value("bezier", default_beziers.get(bezier_name)) + beziers_data.append(bezier_item._to_dict()) + + collections.append(CollectionDocument.new("characters", [default_narrator._to_dict()], command_manager)) + collections.append(CollectionDocument.new("variables", [], command_manager)) + collections.append(CollectionDocument.new("items", [], command_manager)) + collections.append(CollectionDocument.new("locations", [], command_manager)) + collections.append(CollectionDocument.new("languages", [default_language._to_dict()], command_manager)) + collections.append(CollectionDocument.new("beziers", beziers_data, command_manager)) + + +func get_collection(collection_name: String) -> CollectionDocument: + for collection: CollectionDocument in collections: + if collection.name == collection_name: + return collection + return null + + +func get_collection_value(collection_name: String) -> Array: + var collection: CollectionDocument = get_collection(collection_name) + if collection: + return collection.get_value() + + Log.error("Can't find collection '%s'" % collection_name) + return [] + + +func get_storyline(storyline_id: String) -> StorylineDocument: + for storyline: StorylineDocument in storylines: + if storyline.id == storyline_id: + return storyline + return null + + +func delete_storyline(storyline: StorylineDocument) -> void: + if storylines.size() <= 1: + Log.warn("The storyline couldn't be removed, it's the only one left.") + return + + storylines.erase(storyline) + EventBus.storyline_deleted.emit() + + +func add_new_storyline() -> void: + var base_name: String = "new_storyline" + var attempt: int = 1 + var storyline_name: String = base_name + var existing_names: Array[String] = _get_all_document_names(storylines) + + while storyline_name in existing_names: + storyline_name = "%s_%d" % [base_name, attempt] + attempt += 1 + + storylines.append(StorylineDocument.new(storyline_name, command_manager)) + + +func is_valid_storyline_name(storyline_name: String) -> bool: + for doc_name: String in _get_all_document_names(storylines): + if doc_name == storyline_name: + return true + + return false + + +func _get_all_document_names(documents: Array) -> Array[String]: + var result: Array[String] = [] + for doc: InspectableDocument in documents: + result.append(doc.name) + return result + + +func _on_command_executed() -> void: + is_dirty = true + content_changed.emit() + undo_redo_changed.emit() + ProjectManager._update_window_title() + + +func _on_undo() -> void: + is_dirty = true + content_changed.emit() + undo_redo_changed.emit() + ProjectManager._update_window_title() + + +func _on_redo() -> void: + is_dirty = true + content_changed.emit() + undo_redo_changed.emit() + ProjectManager._update_window_title() + + +func save() -> void: + if project_path.is_empty(): + EventBus.save_file_request.emit( + _open_file_request_callback, + FORMAT_FILTER, + "", + [{"name": "Compact", "values": [], "default_value_index": 1}] + ) + await _project_path_changed + + var writer: ZIPPacker = ZIPPacker.new() + writer.compression_level = 0 + var err: Error = writer.open(project_path, ZIPPacker.APPEND_CREATE) + if err != OK: + Log.error(err) + return + + pack_document(writer, manifest, "manifest.json") + pack_document(writer, settings, "settings.json") + for collection: CollectionDocument in collections: + pack_document(writer, collection, "collections/%s.json" % collection.name) + for storyline: StorylineDocument in storylines: + pack_document(writer, storyline, "storylines/%s.json" % storyline.name) + + writer.close() + is_dirty = false + Log.info("Project saved at path '%s'" % project_path) + ProjectManager._update_window_title() + + +func pack_document(writer: ZIPPacker, document: InspectableDocument, path: String) -> void: + var data: Dictionary = document._to_dict() + var s_data: String = JSON.stringify(data, "\t") + writer.start_file(path) + writer.write_file(s_data.to_utf8_buffer()) + writer.close_file() + + +func _open_file_request_callback(path: String) -> void: + project_path = path + _project_path_changed.emit() + + +static func from_file_path(path: String) -> MonologueProject: + if not path.ends_with(".%s" % FILE_FORMAT) or not FileAccess.file_exists(path): + Log.error("Can't load project from an invalid path.") + return null + + var reader: ZIPReader = ZIPReader.new() + reader.open(path) + + var project: MonologueProject = await _from_path_core(reader, path) + project.compact = true + return project + + +static func from_dir_path(path: String) -> MonologueProject: + if not DirAccess.dir_exists_absolute(path): + Log.error("Can't load project from an invalid path.") + return null + + var reader: DirAccess = DirAccess.open(path) + + var project: MonologueProject = await _from_path_core(reader, path) + project.compact = false + return project + + +## 'reader' can be either a 'ZIPReader' or a 'DirAccess'. +static func _from_path_core(reader: Variant, path: String) -> MonologueProject: + var files: PackedStringArray = reader.get_files() + + var project: MonologueProject = MonologueProject.new() + await project.ready + + project.project_path = path + project.name = path.get_file() + project.is_dirty = false + + if reader.file_exists("manifest.json"): + var manifest_data: Dictionary = JSON.parse_string( + reader.read_file("manifest.json").get_string_from_utf8() + ) + project.manifest._from_dict(manifest_data) + + if reader.file_exists("settings.json"): # FIX: settings n'était pas chargé + var settings_data: Dictionary = JSON.parse_string( + reader.read_file("settings.json").get_string_from_utf8() + ) + project.settings._from_dict(settings_data) + + project.storylines.clear() + + for file: String in files: + var paths: Array = file.split("/") as Array + if paths.size() <= 1: + continue + + var file_name: String = paths.back().get_basename() + var extension: String = paths.back().get_extension() + if extension != "json": + Log.error("Attempt to load a non-JSON file: '%s'" % file) + continue + + var file_content: String = reader.read_file(file).get_string_from_utf8() + + match paths[0]: + "collections": + _load_collection_from_file(project, file_name, file_content) + "storylines": + _load_storyline_from_file(project, file_name, file_content) + + return project + + +static func _load_collection_from_file(project: MonologueProject, collection_name: String, file_content: String) -> void: + var collection: CollectionDocument = project.get_collection(collection_name) + if not collection: + Log.error("Can't find the collection '%s' inside the project." % collection_name) + return + + collection._from_dict(JSON.parse_string(file_content)) + + +static func _load_storyline_from_file(project: MonologueProject, storyline_name: String, file_content: String) -> void: + var storyline: StorylineDocument = StorylineDocument.new(storyline_name, project.command_manager) + storyline._from_dict(JSON.parse_string(file_content)) + project.storylines.append(storyline) diff --git a/common/documents/monologue_project.gd.uid b/common/documents/monologue_project.gd.uid new file mode 100644 index 00000000..a533d9bf --- /dev/null +++ b/common/documents/monologue_project.gd.uid @@ -0,0 +1 @@ +uid://co3rhgyy316t0 diff --git a/common/documents/project_settings_document.gd b/common/documents/project_settings_document.gd new file mode 100644 index 00000000..e08a3a41 --- /dev/null +++ b/common/documents/project_settings_document.gd @@ -0,0 +1,9 @@ +class_name ProjectSettingsDocument extends InspectableDocument + + +func initialize_properties() -> void: + define_property("display_response_in_the_flow", false, "bool") + + +func get_type() -> String: + return "manifest" diff --git a/common/documents/project_settings_document.gd.uid b/common/documents/project_settings_document.gd.uid new file mode 100644 index 00000000..de5fbc91 --- /dev/null +++ b/common/documents/project_settings_document.gd.uid @@ -0,0 +1 @@ +uid://b0p06vip4jo7t diff --git a/common/documents/storyline_document.gd b/common/documents/storyline_document.gd new file mode 100644 index 00000000..087dec71 --- /dev/null +++ b/common/documents/storyline_document.gd @@ -0,0 +1,150 @@ +class_name StorylineDocument extends InspectableDocument + +signal node_added +signal node_removed + +var id: String : get = _get_id +var name: String = "" +var nodes: Array[InspectableNode] = [] +var _node_index: Dictionary = {} + + +func _init(sname: String, command_manager: CommandManager) -> void: + name = sname + super._init(command_manager) + + _create_default_nodes() + + +func add_node(node: InspectableNode) -> void: + _register_node(node) + + +func remove_node(node: InspectableNode) -> void: + if not node in nodes: + push_warning("Can't remove node %s " % str(node.get_property_value("id"))) + return + + var node_id: String = node.get_property_value("id") + _node_index.erase(node_id) + nodes.erase(node) + node_removed.emit() + + +func create_node(node_type: String) -> InspectableNode: + var node: InspectableNode = NodeBucket.create_node(node_type, history) + _register_node(node) + return node + + +func get_node(node_id: String) -> InspectableNode: + return _node_index.get(node_id) + +func initialize_properties() -> void: + pass + + +func get_type() -> String: + return "storyline" + + +func get_settings() -> Dictionary: + return {} + + +func build_graph_preview() -> Array[Control]: + return [] + + +func _get_id() -> String: + return get_property_value("id") + + +func _create_default_nodes() -> void: + var root_node: InspectableNode = NodeBucket.create_node("root", history) + var root_mp: Property = root_node.get_main_property() + + var sent_node: InspectableNode = NodeBucket.create_node("sentence", history) + var sent_mp: Property = sent_node.get_main_property() + + var option_node: InspectableNode = NodeBucket.create_node("option", history) + var option_mp: Property = option_node.get_main_property() + + var choice_node: InspectableNode = NodeBucket.create_node("choice", history) + var choice_mp: Property = choice_node.get_main_property() + var choice_opt: Array[Dictionary] = [] + for _i: int in range(2): + choice_opt.append(CollectionBucket.create_item("option", history)._to_dict()) + + sent_node.get_property("position").set_value([240.0, 0]) + option_node.get_property("position").set_value([240.0, 120.0]) + choice_node.get_property("position").set_value([480.0, 0]) + choice_node.get_property("choices").set_value(choice_opt) + choice_node.get_property("choices").set_settings_value("exposed", true) + + _register_node(root_node) + _register_node(sent_node) + _register_node(option_node) + _register_node(choice_node) + + root_mp.add_connection_to(sent_node.get_id(), sent_mp.name) + sent_mp.add_connection_from(root_node.get_id(), root_mp.name) + + sent_mp.add_connection_to(choice_node.get_id(), choice_mp.name) + choice_mp.add_connection_from(sent_node.get_id(), sent_mp.name) + + option_mp.add_connection_to(choice_node.get_id(), "choices") + choice_node.get_property("choices").add_connection_from(option_node.get_id(), option_mp.name) + + +func _register_node(node: InspectableNode) -> void: + if not node: + return + + if not node in nodes: + nodes.append(node) + + node.storyline_id = id + var node_id: String = node.get_property_value("id") + if not node_id.is_empty(): + _node_index[node_id] = node + + node_added.emit() + + +func _to_dict() -> Dictionary: + var dict: Dictionary = super._to_dict() + dict["nodes"] = [] + var root_node_id: String = "" + for node: InspectableNode in nodes: + if node is RootNode: + root_node_id = node.get_property("id").get_value() + var nodes_arr: Array = dict["nodes"] + nodes_arr.append(node._to_dict()) + + dict["root_node_id"] = root_node_id + + return dict + + +func _from_dict(dict: Dictionary) -> void: + if not dict or dict.is_empty(): + return + + nodes.clear() + _node_index.clear() + + super._from_dict(dict) + + # Reconstruct graph nodes + var node_list: Array = dict.get("nodes", []) + for node_data: Dictionary in node_list: + var node_type: String = node_data.get("$type", "") + if node_type.is_empty(): + continue + var node: InspectableNode = NodeBucket.create_node(node_type, history) + if not node: + push_warning("Could not create node of type '%s' from dict." % node_type) + continue + node._from_dict(node_data) + _register_node(node) diff --git a/common/documents/storyline_document.gd.uid b/common/documents/storyline_document.gd.uid new file mode 100644 index 00000000..33257a8d --- /dev/null +++ b/common/documents/storyline_document.gd.uid @@ -0,0 +1 @@ +uid://cv5yppp8eqjja diff --git a/common/fields/any/index.gd b/common/fields/any/index.gd new file mode 100644 index 00000000..01f585b4 --- /dev/null +++ b/common/fields/any/index.gd @@ -0,0 +1,14 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return PackedScene.new() + + +func get_metadata() -> Dictionary: + return { + "name": "any", + "type": ObjectType.FIELD, + "color": Color("87b26cff"), + "compatible_types": ["*"], + } diff --git a/common/fields/any/index.gd.uid b/common/fields/any/index.gd.uid new file mode 100644 index 00000000..bdfd59d9 --- /dev/null +++ b/common/fields/any/index.gd.uid @@ -0,0 +1 @@ +uid://cwj23cf477hbg diff --git a/common/fields/bezier/background_panel.gd b/common/fields/bezier/background_panel.gd new file mode 100644 index 00000000..5fddb6da --- /dev/null +++ b/common/fields/bezier/background_panel.gd @@ -0,0 +1,50 @@ +extends PanelContainer + +@export_range(0, 20) var line_count: int = 3 +@export var ball_speed: float = 1.0 + +var _ball_progress: float = 0.0 +var _ball_direction: int = 1 + +@onready var path: Path2D = %Path2D +@onready var cp1: BezierControlPoint = %CP1 +@onready var cp2: BezierControlPoint = %CP2 + + +func _ready() -> void: + item_rect_changed.connect(_on_item_rect_changed) + + +func _process(delta: float) -> void: + _ball_progress += ball_speed * delta * _ball_direction + if _ball_progress > 1.0 or _ball_progress < 0.0: + _ball_direction*=-1 + _ball_progress = clamp(_ball_progress, 0.0, 1.0) + queue_redraw() + + +func _draw() -> void: + for i: int in range(line_count): + var line_x: float = (size.x/(line_count+1)) * (i+1) + draw_line(Vector2(line_x, 0), Vector2(line_x, size.y), Color("ffffff3f"), 1.0) + var line_y: float = (size.y/(line_count+1)) * (i+1) + draw_line(Vector2(0, line_y), Vector2(size.x, line_y), Color("ffffff3f"), 1.0) + + var curve: Curve2D = path.curve + var points: PackedVector2Array = curve.tessellate() + draw_polyline(points, Color.WHITE, 1.0, true) + + draw_dashed_line(cp1.position + cp1.size/2, Vector2(0, size.y), Color("ffffff3f"), 1.0, 5.0) + draw_dashed_line(cp2.position + cp2.size/2, Vector2(size.x, 0), Color("ffffff3f"), 1.0, 5.0) + draw_dashed_line(Vector2(0, size.y), Vector2(size.x, 0), Color("ffffff3f"), 1.0, 5.0) + + # Preview + var curve_position: Vector2 = curve.sample_baked(_ball_progress * curve.get_baked_length()) + var ball_position: Vector2 = Vector2(curve_position.x, size.y/2) + draw_circle(ball_position, 12.0, Color("ffffff3f")) + + +func _on_item_rect_changed() -> void: + path.position = Vector2.ZERO + path.curve.set_point_position(0, Vector2(0, size.y)) + path.curve.set_point_position(1, Vector2(size.x, 0)) diff --git a/common/fields/bezier/background_panel.gd.uid b/common/fields/bezier/background_panel.gd.uid new file mode 100644 index 00000000..908c9294 --- /dev/null +++ b/common/fields/bezier/background_panel.gd.uid @@ -0,0 +1 @@ +uid://c1a44vlfq5k64 diff --git a/common/fields/bezier/bezier_field.gd b/common/fields/bezier/bezier_field.gd new file mode 100644 index 00000000..87aaf19f --- /dev/null +++ b/common/fields/bezier/bezier_field.gd @@ -0,0 +1,112 @@ +class_name BezierField extends Field + +@onready var spin_box_x1: SpinBox = %SpinBoxX1 +@onready var spin_box_y1: SpinBox = %SpinBoxY1 +@onready var spin_box_x2: SpinBox = %SpinBoxX2 +@onready var spin_box_y2: SpinBox = %SpinBoxY2 +@onready var background_panel: PanelContainer = %BackgroundPanel +@onready var ratio: AspectRatioContainer = %AspectRatioContainer +@onready var path: Path2D = %Path2D +@onready var cp1: BezierControlPoint = %CP1 +@onready var cp2: BezierControlPoint = %CP2 + +var _is_updating: bool = false +var _is_moving_cp: bool = false + + +func _ready() -> void: + for sb: SpinBox in [spin_box_x1, spin_box_y1, spin_box_x2, spin_box_y2]: + sb.value_changed.connect(_on_spin_box_value_changed) + item_rect_changed.connect(_on_item_rect_changed) + cp1.moved.connect(_on_cp_moved) + cp1.button_up.connect(_on_cp_up) + cp2.moved.connect(_on_cp_moved) + cp2.button_up.connect(_on_cp_up) + + +func set_value(value: Variant) -> void: + if not value is Array or value.size() < 4: + return + + _is_updating = true + spin_box_x1.value = value[0] + spin_box_y1.value = value[1] + spin_box_x2.value = value[2] + spin_box_y2.value = value[3] + _is_updating = false + _update_ui.call_deferred() + + +func get_value() -> Variant: + return [ + spin_box_x1.value, + spin_box_y1.value, + spin_box_x2.value, + spin_box_y2.value + ] + + +func set_editable(is_editable: bool) -> void: + cp1.movable = is_editable + cp2.movable = is_editable + for spin_box: SpinBox in [spin_box_x1, spin_box_x2, spin_box_y1, spin_box_y2]: + spin_box.editable = is_editable + + +func prefers_vertical_layout(_settings: Dictionary) -> bool: + return true + + +func _on_spin_box_value_changed(_value: float) -> void: + if _is_updating: + return + + _update_ui() + emit_value_committed(get_value()) + +func _update_values() -> void: + var canvas_size: float = background_panel.size.x + cp1.position.x = clamp(cp1.position.x, -cp1.size.x/2, canvas_size - cp1.size.x/2) + cp2.position.x = clamp(cp2.position.x, -cp2.size.x/2, canvas_size - cp2.size.x/2) + + var cp1_pos: Vector2 = cp1.position + cp1.size/2 + var cp2_pos: Vector2 = cp2.position + cp2.size/2 + var p1: Vector2 = (Vector2(cp1_pos.x, -cp1_pos.y) / canvas_size) + Vector2(0.0, 1.0) + var p2: Vector2 = (Vector2(cp2_pos.x, -cp2_pos.y) / canvas_size) + Vector2(0.0, 1.0) + + spin_box_x1.value = p1.x + spin_box_y1.value = p1.y + spin_box_x2.value = p2.x + spin_box_y2.value = p2.y + + +func _update_ui() -> void: + var canvas_size: float = background_panel.size.x + if canvas_size <= 0.0: + return + var bezier: Array = get_value() + var curve: Curve2D = path.curve + var p1: Vector2 = Vector2(bezier[0], -bezier[1]) * canvas_size + var p2: Vector2 = Vector2(bezier[2] - 1.0, 1.0 - bezier[3]) * canvas_size + curve.set_point_out(0, p1) + curve.set_point_in(1, p2) + if not _is_moving_cp: + var cp1_pos: Vector2 = Vector2(bezier[0], 1.0 - bezier[1]) * canvas_size + var cp2_pos: Vector2 = Vector2(bezier[2], -bezier[3] + 1.0) * canvas_size + cp1.position = cp1_pos - cp1.size / 2 + cp2.position = cp2_pos - cp2.size / 2 + background_panel.queue_redraw() + + +func _on_item_rect_changed() -> void: + _update_ui() + +func _on_cp_moved() -> void: + _is_moving_cp = true + _is_updating = true + _update_values() + _update_ui() + _is_updating = false + +func _on_cp_up() -> void: + emit_value_committed(get_value()) diff --git a/common/fields/bezier/bezier_field.gd.uid b/common/fields/bezier/bezier_field.gd.uid new file mode 100644 index 00000000..f1bffd9d --- /dev/null +++ b/common/fields/bezier/bezier_field.gd.uid @@ -0,0 +1 @@ +uid://bdhmssf7fdean diff --git a/common/fields/bezier/bezier_field.tscn b/common/fields/bezier/bezier_field.tscn new file mode 100644 index 00000000..2633b075 --- /dev/null +++ b/common/fields/bezier/bezier_field.tscn @@ -0,0 +1,116 @@ +[gd_scene format=3 uid="uid://dlmxs2fcef1a1"] + +[ext_resource type="Script" uid="uid://bdhmssf7fdean" path="res://common/fields/bezier/bezier_field.gd" id="1_msnhu"] +[ext_resource type="Script" uid="uid://c1a44vlfq5k64" path="res://common/fields/bezier/background_panel.gd" id="2_jp2y7"] +[ext_resource type="Script" uid="uid://broer240g44aw" path="res://common/fields/bezier/control_point.gd" id="3_jp2y7"] + +[sub_resource type="Curve2D" id="Curve2D_lvtej"] +_data = { +"points": PackedVector2Array(-125, 0, 0, 0, 0, 200, 0, 0, 123.60647, -0.14568329, 200, 0) +} +point_count = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hashh"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 100 +corner_radius_top_right = 100 +corner_radius_bottom_right = 100 +corner_radius_bottom_left = 100 + +[node name="BezierField" type="VBoxContainer" unique_id=754138938] +script = ExtResource("1_msnhu") + +[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=687121200] +layout_mode = 2 + +[node name="AspectRatioContainer" type="AspectRatioContainer" parent="HBoxContainer" unique_id=1708558315] +unique_name_in_owner = true +z_index = 10 +custom_minimum_size = Vector2(200, 200) +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="BackgroundPanel" type="PanelContainer" parent="HBoxContainer/AspectRatioContainer" unique_id=206201097] +unique_name_in_owner = true +clip_children = 2 +layout_mode = 2 +script = ExtResource("2_jp2y7") + +[node name="Path2D" type="Path2D" parent="HBoxContainer/AspectRatioContainer" unique_id=744452601] +unique_name_in_owner = true +curve = SubResource("Curve2D_lvtej") + +[node name="ControlPoints" type="Control" parent="HBoxContainer/AspectRatioContainer" unique_id=1501788487] +z_index = 10 +layout_mode = 2 +mouse_filter = 2 + +[node name="CP1" type="Button" parent="HBoxContainer/AspectRatioContainer/ControlPoints" unique_id=378577047] +unique_name_in_owner = true +custom_minimum_size = Vector2(10, 10) +layout_mode = 0 +offset_top = 125.0 +offset_right = 10.0 +offset_bottom = 135.0 +theme_override_styles/normal = SubResource("StyleBoxFlat_hashh") +theme_override_styles/pressed = SubResource("StyleBoxFlat_hashh") +theme_override_styles/hover = SubResource("StyleBoxFlat_hashh") +script = ExtResource("3_jp2y7") + +[node name="CP2" type="Button" parent="HBoxContainer/AspectRatioContainer/ControlPoints" unique_id=783942087] +unique_name_in_owner = true +custom_minimum_size = Vector2(10, 10) +layout_mode = 1 +offset_left = 125.0 +offset_right = 135.0 +offset_bottom = 10.0 +theme_override_styles/normal = SubResource("StyleBoxFlat_hashh") +theme_override_styles/pressed = SubResource("StyleBoxFlat_hashh") +theme_override_styles/hover = SubResource("StyleBoxFlat_hashh") +script = ExtResource("3_jp2y7") + +[node name="CoordinatesHBox1" type="HBoxContainer" parent="." unique_id=562686494] +layout_mode = 2 + +[node name="SpinBoxX1" type="SpinBox" parent="CoordinatesHBox1" unique_id=1902653887] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +alignment = 3 +suffix = "x1" + +[node name="SpinBoxY1" type="SpinBox" parent="CoordinatesHBox1" unique_id=1587721798] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +allow_greater = true +allow_lesser = true +alignment = 3 +suffix = "y1" + +[node name="CoordinatesHBox2" type="HBoxContainer" parent="." unique_id=1353984226] +layout_mode = 2 + +[node name="SpinBoxX2" type="SpinBox" parent="CoordinatesHBox2" unique_id=1401866127] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +alignment = 3 +suffix = "x2" + +[node name="SpinBoxY2" type="SpinBox" parent="CoordinatesHBox2" unique_id=1652230862] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +allow_greater = true +allow_lesser = true +alignment = 3 +suffix = "y2" diff --git a/common/fields/bezier/control_point.gd b/common/fields/bezier/control_point.gd new file mode 100644 index 00000000..35dacdab --- /dev/null +++ b/common/fields/bezier/control_point.gd @@ -0,0 +1,13 @@ +class_name BezierControlPoint extends Button + +signal moved + +var movable: bool = true + + +func _input(event: InputEvent) -> void: + if not movable or not button_pressed or not event is InputEventMouseMotion: + return + + global_position = get_global_mouse_position() - size/2 + moved.emit() diff --git a/common/fields/bezier/control_point.gd.uid b/common/fields/bezier/control_point.gd.uid new file mode 100644 index 00000000..5c5735e6 --- /dev/null +++ b/common/fields/bezier/control_point.gd.uid @@ -0,0 +1 @@ +uid://broer240g44aw diff --git a/common/fields/bezier/index.gd b/common/fields/bezier/index.gd new file mode 100644 index 00000000..c2d750b4 --- /dev/null +++ b/common/fields/bezier/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://dlmxs2fcef1a1") + + +func get_metadata() -> Dictionary: + return {"name": "bezier", "type": ObjectType.FIELD, "color": Color("9df27eff"), "default_value": [0.25, 0.10, 0.25, 1.0]} diff --git a/common/fields/bezier/index.gd.uid b/common/fields/bezier/index.gd.uid new file mode 100644 index 00000000..817fe388 --- /dev/null +++ b/common/fields/bezier/index.gd.uid @@ -0,0 +1 @@ +uid://bde4dy1e6nswr diff --git a/common/fields/bool/bool_field.gd b/common/fields/bool/bool_field.gd new file mode 100644 index 00000000..6e9a00e0 --- /dev/null +++ b/common/fields/bool/bool_field.gd @@ -0,0 +1,27 @@ +class_name BoolField extends Field + +@onready var check_box: CheckBox = %CheckBox + + +func _ready() -> void: + check_box.pressed.connect(_on_check_box_pressed) + + var check_box_label: Variant = settings.get("label", " ") + check_box.text = check_box_label + + +func set_value(value: Variant) -> void: + if value is bool: + check_box.button_pressed = value + + +func get_value() -> Variant: + return check_box.button_pressed + + +func set_editable(is_editable: bool) -> void: + check_box.disabled = not is_editable + + +func _on_check_box_pressed() -> void: + emit_value_committed(get_value()) diff --git a/common/fields/bool/bool_field.gd.uid b/common/fields/bool/bool_field.gd.uid new file mode 100644 index 00000000..e84e40aa --- /dev/null +++ b/common/fields/bool/bool_field.gd.uid @@ -0,0 +1 @@ +uid://r0ajxbf3ci1n diff --git a/common/fields/bool/bool_field.tscn b/common/fields/bool/bool_field.tscn new file mode 100644 index 00000000..c7495a1b --- /dev/null +++ b/common/fields/bool/bool_field.tscn @@ -0,0 +1,13 @@ +[gd_scene format=3 uid="uid://c6dvfrwaes07a"] + +[ext_resource type="Script" uid="uid://r0ajxbf3ci1n" path="res://common/fields/bool/bool_field.gd" id="1_b0ct0"] + +[node name="BoolField" type="VBoxContainer" unique_id=1052952054] +script = ExtResource("1_b0ct0") + +[node name="HBox" type="HBoxContainer" parent="." unique_id=1227476302] +layout_mode = 2 + +[node name="CheckBox" type="CheckBox" parent="HBox" unique_id=2045888175] +unique_name_in_owner = true +layout_mode = 2 diff --git a/common/fields/bool/index.gd b/common/fields/bool/index.gd new file mode 100644 index 00000000..811148db --- /dev/null +++ b/common/fields/bool/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://c6dvfrwaes07a") + + +func get_metadata() -> Dictionary: + return {"name": "bool", "type": ObjectType.FIELD, "color": Color("f2997eff"), "default_value": false} diff --git a/common/fields/bool/index.gd.uid b/common/fields/bool/index.gd.uid new file mode 100644 index 00000000..9e19e9a0 --- /dev/null +++ b/common/fields/bool/index.gd.uid @@ -0,0 +1 @@ +uid://dea6qu3apycfi diff --git a/common/fields/collection/collection_field.gd b/common/fields/collection/collection_field.gd new file mode 100644 index 00000000..34542116 --- /dev/null +++ b/common/fields/collection/collection_field.gd @@ -0,0 +1,197 @@ +class_name CollectionField extends BaseListField + +var _collection_name: String = "" + + +func _on_initialize() -> void: + super._on_initialize() + var property: Property = _binding.property if _binding else null + if not property: + Log.warning("CollectionField is not binded.") + + var collection: Variant = property.get_settings_value("collection") + if not collection or not CollectionBucket.get_descriptor(str(collection)): + Log.error("Can't find collection %s." % str(collection)) + return + _collection_name = collection + + if not property.connection_changed.is_connected(_on_connection_changed): + property.connection_changed.connect(_on_connection_changed) + + +func set_value(value: Variant) -> void: + var need_rebuild: bool = true + + if value is not Array: + value = [] + + var outdated_childrens: Array = get_items().duplicate() + for item_data: Dictionary in value: + if item_data is not Dictionary: + continue + + var child_found: bool = false + for child: CollectionItem in outdated_childrens: + var dict_match := false + var item_id: Variant = item_data.get("id", {}).get("value") if item_data.has("id") else null + var child_id: Variant = child.get_property_value("id") + + if item_id != null and child_id != null: + dict_match = (item_id == child_id) + else: + dict_match = (item_data == child._to_dict()) + + if dict_match: + child._from_dict(item_data) + outdated_childrens.erase(child) + child_found = true + break + + if not child_found: + var item: CollectionItem = _create_new_list_item(item_data) + _binding.owner.add_property_children(_binding.property.name, item) + + + for child: CollectionItem in outdated_childrens: + _binding.owner.remove_property_children(_binding.property.name, child) + + for child: CollectionItem in get_items(): + if child.property_changed.is_connected(_on_item_property_changed): + continue + + child.property_changed.connect(_on_item_property_changed.bind(child)) + + for i in range(value.size()): + var item_data: Dictionary = value[i] + for child: CollectionItem in get_items(): + if child._to_dict() == item_data: + _binding.owner.move_property_child(_binding.property.name, child, i) + break + + if need_rebuild: + _rebuild_ui() + + +func get_value() -> Variant: + var result: Array = [] + for item: InspectableObject in get_items(): + result.append(item._to_dict()) + return result + + +func _create_new_list_item(from_data: Dictionary = {}) -> CollectionItem: + var item: CollectionItem = CollectionBucket.create_item(_collection_name, _binding.owner.history) + item._from_dict(from_data) + + return item + + +func _on_item_property_changed(_property_name: String, _item: CollectionItem) -> void: + # The undo/redo is handle by the CollectionItem it self. + # We're just updating the value of the list property. + _binding.property.value = get_value() + + +func _on_add_button() -> void: + add_item() + + +func _on_edit_item(index: int) -> void: + var item: CollectionItem = get_items()[index] + EventBus.request_object_inspection.emit(item) + + +func _on_duplicate_item(index: int) -> void: + var children: Array = get_items() + if not _is_valid_index(index): + return + + var item_data: Dictionary = children[index]._to_dict() + var new_item: CollectionItem = CollectionBucket.create_item(_collection_name, _binding.owner.history) + new_item._from_dict(item_data) + _make_item_unique(new_item) + + _binding.owner.add_property_children(_binding.property.name, new_item) + emit_value_committed(get_value()) + + +func _make_item_unique(item: CollectionItem) -> void: + for prop: Property in item.get_properties(): + if not prop.get_settings_value(PropertySettings.KEY_UNIQUE, false): + continue + + if prop.name == "id": + prop.value = IDGen.generate(InspectableObject.ID_LENGTH) + + var base_val: String = str(prop.value) + var regex := RegEx.new() + regex.compile(r"^(.*?)(\d+)$") + var result := regex.search(base_val) + var name_base: String = base_val + " " + var attempt: int = 1 + + if result: + name_base = result.get_string(1) + attempt = int(result.get_string(2)) + 1 + + while _value_exists_in_list(prop.name, prop.value): + if prop.name == "id": + prop.value = IDGen.generate(InspectableObject.ID_LENGTH) + else: + prop.value = "%s%d" % [name_base, attempt] + attempt += 1 + + +func _on_delete_item(index: int) -> void: + var item: CollectionItem = get_items()[index] + _binding.owner.remove_property_children(_binding.property.name, item) + + emit_value_committed(get_value()) + _rebuild_ui() + + +func reorder_item(from_index: int, to_index: int) -> void: + var children: Array = get_items() + if from_index < 0 or from_index >= children.size(): + return + if to_index < 0 or to_index >= children.size(): + return + + _binding.owner.move_property_child(_binding.property.name, children[from_index], to_index) + emit_value_committed(get_value()) + _rebuild_ui() + + +func _is_valid_index(index: int) -> bool: + return index >= 0 and index < get_items().size() + + +func add_item() -> void: + var new_item: CollectionItem = _create_new_list_item() + _make_item_unique(new_item) + _binding.owner.add_property_children(_binding.property.name, new_item) + + emit_value_committed(get_value()) + _rebuild_ui() + + +func _value_exists_in_list(pname: String, pvalue: Variant) -> bool: + for item: InspectableObject in get_items(): + var prop: Property = item.get_property(pname) + if prop and prop.value == pvalue: + return true + return false + + +func _on_connection_changed() -> void: + _rebuild_ui() + + +func _populate_external_items() -> void: + var externals: Array[Dictionary] = _binding.owner.get_external_list_items(_binding.property.name) + + for ext_data: Dictionary in externals: + var item_idx: int = items_container.get_child_count() + var container: PanelContainer = _create_item_container(not item_idx % 2) + CollectionItemHelper.populate_external_item_view(container, ext_data.get("name", "")) + items_container.add_child(container) diff --git a/common/fields/collection/collection_field.gd.uid b/common/fields/collection/collection_field.gd.uid new file mode 100644 index 00000000..559def06 --- /dev/null +++ b/common/fields/collection/collection_field.gd.uid @@ -0,0 +1 @@ +uid://bqosgu0pi38hf diff --git a/common/fields/collection/collection_field.tscn b/common/fields/collection/collection_field.tscn new file mode 100644 index 00000000..87b94026 --- /dev/null +++ b/common/fields/collection/collection_field.tscn @@ -0,0 +1,27 @@ +[gd_scene format=3 uid="uid://collectionfield0001"] + +[ext_resource type="Script" path="res://common/fields/collection/collection_field.gd" id="1_collection"] + +[node name="CollectionField" type="VBoxContainer" unique_id=1282527206] +offset_right = 300.0 +offset_bottom = 35.0 +size_flags_horizontal = 3 +size_flags_vertical = 0 +script = ExtResource("1_collection") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1918604036] +layout_mode = 2 +theme_type_variation = &"ListFieldPanel" + +[node name="ItemsContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1665726569] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"VBoxContainerCompact" + +[node name="AddButton" type="Button" parent="." unique_id=1510650974] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"PlainButton" +text = "Add Item" +text_overrun_behavior = 5 \ No newline at end of file diff --git a/common/fields/collection/index.gd b/common/fields/collection/index.gd new file mode 100644 index 00000000..7bb247e9 --- /dev/null +++ b/common/fields/collection/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("res://common/fields/collection/collection_field.tscn") + + +func get_metadata() -> Dictionary: + return {"name": "collection", "type": ObjectType.FIELD, "color": Color("b48eadff"), "default_value": []} \ No newline at end of file diff --git a/common/fields/collection/index.gd.uid b/common/fields/collection/index.gd.uid new file mode 100644 index 00000000..13f82cb2 --- /dev/null +++ b/common/fields/collection/index.gd.uid @@ -0,0 +1 @@ +uid://p61abvep10h diff --git a/common/fields/color/color.gd b/common/fields/color/color.gd new file mode 100644 index 00000000..39aee496 --- /dev/null +++ b/common/fields/color/color.gd @@ -0,0 +1,82 @@ +extends Field + +# Sweetie 16 Palette by GrafxKid +const COLORS: Array[String] = [ + "#000000", + "#b13e53", + "#ef7d57", + "#ffcd75", + "#a7f070", + "#38b764", + "#257179", + "#3b5dc9", + "#41a6f6", + "#73eff7", + "#94b0c2", + "#566c86" +] + +@onready var color_picker_button: Button = %ColorPickerButton +@onready var color_preview: ColorRect = %ColorPreview +@onready var color_popup: PopupPanel = %ColorPopup +@onready var color_selector: GridContainer = %ColorSelector + +@onready var Swatch: PackedScene = preload("uid://psbrljdmbtg6") + +var color: Color = Color(COLORS[0]) + + +func _ready() -> void: + for color_string: String in COLORS: + var swatch: ColorSwatch = Swatch.instantiate() + swatch.color = color_string + color_selector.add_child(swatch) + swatch.pressed.connect(_on_color_swatch_pressed.bind(color_string)) + _update_preview() + + +func set_value(value: Variant) -> void: + if value is not String: + value = "ffffff" + color = Color(str(value)) + _update_preview() + + +func get_value() -> Variant: + return color.to_html() + + +func set_editable(is_editable: bool) -> void: + color_picker_button.disabled = not is_editable + if not is_editable: + color_popup.hide() + + +func _on_color_picker_button_pressed() -> void: + color_popup.position = global_position + Vector2(0, size.y) + color_popup.popup() + + +func _on_color_swatch_pressed(color_string: String) -> void: + color = Color(color_string) + color_popup.hide() + _update_preview() + + emit_value_committed(color_string) + + +func _update_preview() -> void: + color_preview.color = color + + custom_minimum_size.x = size.y + + +func _on_resized() -> void: + if not is_node_ready(): + return + + _update_preview() + + +func _on_color_popup_popup_hide() -> void: + color_picker_button.button_pressed = false diff --git a/common/fields/color/color.gd.uid b/common/fields/color/color.gd.uid new file mode 100644 index 00000000..4611c73b --- /dev/null +++ b/common/fields/color/color.gd.uid @@ -0,0 +1 @@ +uid://cwkwnc22br2nn diff --git a/common/fields/color/color.tscn b/common/fields/color/color.tscn new file mode 100644 index 00000000..38c121e8 --- /dev/null +++ b/common/fields/color/color.tscn @@ -0,0 +1,59 @@ +[gd_scene format=3 uid="uid://dywxnyncdf55t"] + +[ext_resource type="Script" uid="uid://cwkwnc22br2nn" path="res://common/fields/color/color.gd" id="1_fkepi"] +[ext_resource type="Texture2D" uid="uid://cweh5vxhmobip" path="res://ui/assets/icons/arrow_down.svg" id="2_7aqwf"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_fkepi"] + +[node name="Color" type="VBoxContainer" unique_id=164474158] +anchors_preset = -1 +anchor_right = 0.029000001 +anchor_bottom = 0.025 +offset_right = 3.3999977 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_fkepi") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1010819063] +clip_contents = true +layout_mode = 2 +size_flags_horizontal = 0 +theme_type_variation = &"ColorPanel" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer" unique_id=1579367488] +layout_mode = 2 +theme_type_variation = &"HBoxContainerCompact" + +[node name="ColorPreview" type="ColorRect" parent="PanelContainer/HBoxContainer" unique_id=831859225] +unique_name_in_owner = true +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="ColorPickerButton" type="Button" parent="PanelContainer/HBoxContainer" unique_id=1033632256] +unique_name_in_owner = true +clip_children = 2 +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"ToggleIconButton" +toggle_mode = true +icon = ExtResource("2_7aqwf") + +[node name="ColorPopup" type="PopupPanel" parent="PanelContainer/HBoxContainer/ColorPickerButton" unique_id=1318626126] +unique_name_in_owner = true +oversampling_override = 1.0 +size = Vector2i(8, 8) +theme_override_styles/panel = SubResource("StyleBoxEmpty_fkepi") + +[node name="PanelContainer" type="PanelContainer" parent="PanelContainer/HBoxContainer/ColorPickerButton/ColorPopup" unique_id=1474133410] +offset_right = 8.0 +offset_bottom = 8.0 + +[node name="ColorSelector" type="GridContainer" parent="PanelContainer/HBoxContainer/ColorPickerButton/ColorPopup/PanelContainer" unique_id=492361297] +unique_name_in_owner = true +layout_mode = 2 +columns = 4 + +[connection signal="resized" from="." to="." method="_on_resized"] +[connection signal="pressed" from="PanelContainer/HBoxContainer/ColorPickerButton" to="." method="_on_color_picker_button_pressed"] +[connection signal="popup_hide" from="PanelContainer/HBoxContainer/ColorPickerButton/ColorPopup" to="." method="_on_color_popup_popup_hide"] diff --git a/common/fields/color/color_swatch.gd b/common/fields/color/color_swatch.gd new file mode 100644 index 00000000..766e2792 --- /dev/null +++ b/common/fields/color/color_swatch.gd @@ -0,0 +1,19 @@ +@tool +extends Button +class_name ColorSwatch + +@onready var color_rect: ColorRect = %ColorRect + +@export var color: Color = Color("ffffff"): + set = _set_color + + +func _set_color(value: Color) -> void: + color = value + if not color_rect: + return + color_rect.color = value + + +func _ready() -> void: + self.color = color diff --git a/common/fields/color/color_swatch.gd.uid b/common/fields/color/color_swatch.gd.uid new file mode 100644 index 00000000..46c1e9f6 --- /dev/null +++ b/common/fields/color/color_swatch.gd.uid @@ -0,0 +1 @@ +uid://c5p813dybhgwt diff --git a/common/fields/color/color_swatch.tscn b/common/fields/color/color_swatch.tscn new file mode 100644 index 00000000..694f6551 --- /dev/null +++ b/common/fields/color/color_swatch.tscn @@ -0,0 +1,18 @@ +[gd_scene format=3 uid="uid://psbrljdmbtg6"] + +[ext_resource type="Script" uid="uid://c5p813dybhgwt" path="res://common/fields/color/color_swatch.gd" id="1_x2h8f"] + +[node name="ColorSwatch" type="Button"] +clip_children = 1 +custom_minimum_size = Vector2(25, 25) +script = ExtResource("1_x2h8f") + +[node name="ColorRect" type="ColorRect" parent="."] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/common/fields/color/index.gd b/common/fields/color/index.gd new file mode 100644 index 00000000..ef1a0d14 --- /dev/null +++ b/common/fields/color/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://dywxnyncdf55t") + + +func get_metadata() -> Dictionary: + return {"name": "color", "type": ObjectType.FIELD, "color": Color("d1b37bff"), "default_value": "#000000"} diff --git a/common/fields/color/index.gd.uid b/common/fields/color/index.gd.uid new file mode 100644 index 00000000..f214138a --- /dev/null +++ b/common/fields/color/index.gd.uid @@ -0,0 +1 @@ +uid://7bxfucw58wy0 diff --git a/common/fields/condition/condition_field.gd b/common/fields/condition/condition_field.gd new file mode 100644 index 00000000..78fac379 --- /dev/null +++ b/common/fields/condition/condition_field.gd @@ -0,0 +1,168 @@ +class_name ConditionField extends Field + +const VARIABLES_COLLECTION: String = "variables" +const OPERATORS_BY_TYPE: Dictionary = { + "bool": ["==", "!="], + "int": ["==", "!=", ">", ">=", "<", "<="], + "float": ["==", "!=", ">", ">=", "<", "<="], + "string": ["==", "!=", "contains", "fuzzy"], +} +const FIELD_TYPE_BY_VARIABLE_TYPE: Dictionary = { + "bool": "bool", + "int": "int", + "float": "float", + "string": "text", +} + +@onready var variable_dropdown: OptionButton = %VariableDropdown +@onready var operator_dropdown: OptionButton = %OperatorDropdown +@onready var value_container: Container = %OperatorContainer + +var _variables: Dictionary = {} +var _value_field: Field + + +func _on_initialize() -> void: + _load_variables() + + if not variable_dropdown.item_selected.is_connected(_on_variable_item_selected): + variable_dropdown.item_selected.connect(_on_variable_item_selected) + if not operator_dropdown.item_selected.is_connected(_on_operator_item_selected): + operator_dropdown.item_selected.connect(_on_operator_item_selected) + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + + var data: Dictionary = value if value is Dictionary else {} + var variable: String = str(data.get("variable", "")) + + _load_variables() + _select_variable(variable) + _refresh_operator_options(variable) + _set_operator(data.get("operator", "")) + _rebuild_value_field(variable, data.get("value")) + + +func get_value() -> Variant: + return { + "variable": variable_dropdown.get_item_metadata(variable_dropdown.selected) if variable_dropdown.selected >= 0 else "", + "operator": operator_dropdown.get_item_text(operator_dropdown.selected), + "value": _value_field.get_value() if _value_field else null, + } + + +func set_editable(is_editable: bool) -> void: + variable_dropdown.disabled = !is_editable + operator_dropdown.disabled = !is_editable + if _value_field: + _value_field.set_editable(is_editable) + + +func _load_variables() -> void: + _variables.clear() + variable_dropdown.clear() + + var project: Variant = ProjectManager.current_project + if project == null: + return + + var entries: Array = project.get_collection_value(VARIABLES_COLLECTION) + for entry: Variant in entries: + if not entry is Dictionary: + continue + var entry_dict: Dictionary = entry + var entry_id: String = str(entry_dict.get("id").get("value")) + var entry_name: String = str(entry_dict.get("name", {}).get("value", "")) + var entry_type: String = str(entry_dict.get("type", {}).get("value", "string")) + if not entry_name.is_empty(): + _variables[entry_id] = {"name": entry_name, "type": entry_type} + + for id: String in _variables.keys(): + var variable_name: String = _variables[id].get("name", "") + variable_dropdown.add_item(variable_name) + variable_dropdown.set_item_metadata(variable_dropdown.item_count-1, id) + + +func _select_variable(variable_id: String) -> void: + for item_idx: int in variable_dropdown.item_count: + if variable_dropdown.get_item_metadata(item_idx) == variable_id: + variable_dropdown.selected = item_idx + return + + +func _on_variable_item_selected(_new_variable: Variant) -> void: + var variable_id: String = variable_dropdown.get_item_metadata(variable_dropdown.selected) + _refresh_operator_options(variable_id) + _rebuild_value_field(variable_id, null) + emit_value_committed(get_value()) + + +func _on_operator_item_selected(_new_operator: Variant) -> void: + emit_value_committed(get_value()) + + +func _on_value_field_committed(_new_value: Variant) -> void: + emit_value_committed(get_value()) + + +func _refresh_operator_options(variable_id: String) -> void: + var variable_type: String = _variables.get(variable_id, {}).get("type", "string") + var operators: Array = OPERATORS_BY_TYPE.get(variable_type, OPERATORS_BY_TYPE["string"]) + var current_operator: String = operator_dropdown.get_item_text(operator_dropdown.selected) if operator_dropdown.selected >= 0 else "" + + operator_dropdown.clear() + for operator: String in operators: + operator_dropdown.add_item(operator) + + if operators.has(current_operator): + _set_operator(current_operator) + elif operators.size() > 0: + operator_dropdown.selected = 0 + + +func _set_operator(operator_string: String) -> void: + for item_idx: int in operator_dropdown.item_count: + if operator_dropdown.get_item_text(item_idx) == operator_string: + operator_dropdown.selected = item_idx + return + + +func _rebuild_value_field(variable_id: String, value: Variant) -> void: + var variable_type: String = _variables.get(variable_id, {}).get("type", "string") + var field_type: String = FIELD_TYPE_BY_VARIABLE_TYPE.get(variable_type, "string") + + if _value_field: + if _value_field.value_committed.is_connected(_on_value_field_committed): + _value_field.value_committed.disconnect(_on_value_field_committed) + _value_field.queue_free() + _value_field = null + + var new_field: Control = FieldBucket.safe_create_field(field_type) + if new_field == null: + return + + value_container.add_child(new_field) + new_field.size_flags_horizontal = SIZE_EXPAND_FILL + + if new_field is Field: + _value_field = new_field + _value_field._binding = _binding + _value_field.initialize() + _value_field.value_committed.connect(_on_value_field_committed) + var initial_value: Variant = value if value != null else _default_value_for_type(variable_type) + _value_field.set_value(initial_value) + + +func _default_value_for_type(variable_type: String) -> Variant: + match variable_type: + "bool": + return false + "int": + return 0 + "float": + return 0.0 + "string": + return "" + return null diff --git a/common/fields/condition/condition_field.gd.uid b/common/fields/condition/condition_field.gd.uid new file mode 100644 index 00000000..ba498520 --- /dev/null +++ b/common/fields/condition/condition_field.gd.uid @@ -0,0 +1 @@ +uid://laytxf3j7qlf diff --git a/common/fields/condition/condition_field.tscn b/common/fields/condition/condition_field.tscn new file mode 100644 index 00000000..2da83e26 --- /dev/null +++ b/common/fields/condition/condition_field.tscn @@ -0,0 +1,32 @@ +[gd_scene format=3 uid="uid://b6bfebqeejpsg"] + +[ext_resource type="Script" uid="uid://laytxf3j7qlf" path="res://common/fields/condition/condition_field.gd" id="1_eppmo"] + +[node name="Condition" type="VBoxContainer" unique_id=2114152170] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_eppmo") +metadata/_custom_type_script = "uid://r0ajxbf3ci1n" + +[node name="SourceContainer" type="HBoxContainer" parent="." unique_id=109487292] +layout_mode = 2 + +[node name="IfLabel" type="Label" parent="SourceContainer" unique_id=617804244] +layout_mode = 2 +size_flags_horizontal = 0 +text = "If" + +[node name="VariableDropdown" type="OptionButton" parent="SourceContainer" unique_id=2010088278] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="OperatorContainer" type="HBoxContainer" parent="." unique_id=1279684541] +unique_name_in_owner = true +layout_mode = 2 + +[node name="OperatorDropdown" type="OptionButton" parent="OperatorContainer" unique_id=124404139] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 0 +fit_to_longest_item = false diff --git a/common/fields/condition/index.gd b/common/fields/condition/index.gd new file mode 100644 index 00000000..3dc35922 --- /dev/null +++ b/common/fields/condition/index.gd @@ -0,0 +1,13 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://b6bfebqeejpsg") + + +func get_metadata() -> Dictionary: + return {"name": "condition", "type": ObjectType.FIELD, "color": Color("d1b37bff"), "default_value": { + "variable": "", + "operator": ">=", + "value": true + }} diff --git a/common/fields/condition/index.gd.uid b/common/fields/condition/index.gd.uid new file mode 100644 index 00000000..3b3a299c --- /dev/null +++ b/common/fields/condition/index.gd.uid @@ -0,0 +1 @@ +uid://dngh8os3rcu08 diff --git a/common/fields/context/index.gd b/common/fields/context/index.gd new file mode 100644 index 00000000..c3d26b26 --- /dev/null +++ b/common/fields/context/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return PackedScene.new() + + +func get_metadata() -> Dictionary: + return {"name": "context", "type": ObjectType.FIELD, "color": Color("ffffff")} diff --git a/common/fields/context/index.gd.uid b/common/fields/context/index.gd.uid new file mode 100644 index 00000000..4e460bcb --- /dev/null +++ b/common/fields/context/index.gd.uid @@ -0,0 +1 @@ +uid://cg62bw4hys8fb diff --git a/common/fields/dropdown/dropdown_field.gd b/common/fields/dropdown/dropdown_field.gd new file mode 100644 index 00000000..dad2140b --- /dev/null +++ b/common/fields/dropdown/dropdown_field.gd @@ -0,0 +1,232 @@ +extends Field + +var _is_listening: bool = false +var _static_options: Array = [] + +@onready var option_button: OptionButton = %OptionButton + + +func _ready() -> void: + super._ready() + option_button.item_selected.connect(_on_item_selected) + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + + # If value is a string, find it in the list + if value is String: + for i: int in range(option_button.item_count): + if option_button.get_item_text(i) == value: + option_button.selected = i + return + if option_button.item_count > 0: + option_button.selected = 0 + return + # If value is an int, use it as index + elif value is int and value >= 0 and value < option_button.item_count: + option_button.selected = value + + +func get_value() -> Variant: + var selected_idx: int = option_button.selected + if selected_idx >= 0: + return option_button.get_item_text(selected_idx) + return "" + + +func set_editable(is_editable: bool) -> void: + option_button.disabled = not is_editable + + +func _on_initialize() -> void: + super._on_initialize() + await _populate_options() + _setup_source_listener() + + +func _setup_source_listener() -> void: + if not _binding or not _binding.property: + return + + var property: Property = _binding.property + var source: String = property.get_settings_value("source", "") + + if not source.is_empty(): + # Listen for changes to the source property + var storyline: InspectableObject = _get_storyline() + if storyline: + storyline.property_changed.connect(_on_source_changed) + if not _is_listening and storyline.history: + storyline.history.command_executed.connect(_on_storyline_command_executed) + storyline.history.redone.connect(_on_storyline_command_executed) + storyline.history.undone.connect(_on_storyline_command_executed) + _is_listening = true + + +func _on_source_changed(property_name: String) -> void: + if not _binding or not _binding.property: + return + + var source: String = _binding.property.get_settings_value("source", "") + if property_name == source: + # Source data changed, repopulate options + await _populate_options() + await _set_value_to_existing() + + +func _on_storyline_command_executed() -> void: + await _populate_options() + await _set_value_to_existing() + +func _populate_options() -> void: + option_button.clear() + + var options: Array = [] + var property: Property = _binding.property if _binding else null + if property: + var source: String = property.get_settings_value("source", "") + if not source.is_empty() and _binding.owner: + options = _get_options_from_source(source) + else: + options = _normalize_options_array(property.get_settings_value("options", [])) + + if options.is_empty(): + options = _static_options.duplicate() + + for option: Variant in options: + option_button.add_item(str(option)) + + await _set_value_to_existing() + + +func _set_value_to_existing() -> void: + if not _binding or not _binding.property: + if _static_options.is_empty(): + return + var default_value: Variant = _static_options[0] if _static_options.size() > 0 else "" + await set_value(default_value) + return + + var current_value: Variant = _binding.property.get_value() + if current_value == null: + return + await set_value(current_value) + + +func set_static_options(options: Variant) -> void: + _static_options = _normalize_options_array(options) + if is_node_ready(): + await _populate_options() + else: + call_deferred("_populate_options") + + +func _normalize_options_array(source: Variant) -> Array: + if source is Array: + var source_arr: Array = source + return source_arr.duplicate() + if source is PackedStringArray: + var source_psa: PackedStringArray = source + return Array(source_psa) + if source is PackedInt32Array: + var arr: Array = [] + var source_pia: PackedInt32Array = source + for value: int in source_pia: + arr.append(value) + return arr + if source is String: + return [source] + if source == null: + return [] + return [str(source)] + + +func _get_options_from_source(source: String) -> Array: + if not source.begins_with("self:"): + var values: Array = ProjectManager.current_project.get_collection_value(source) + return extract_list_values(values) + + # TODO: Better source path + var source_owner: InspectableObject + if not _binding or not _binding.owner: + return [] + + source_owner = _binding.owner + source = source.trim_prefix("self:") + + if not source_owner: + return [] + + # Get the list property from source_owner + var list_property: Property = source_owner.get_property(source) + if not list_property: + return [] + + var list_value: Variant = list_property.get_value() + if not list_value is Array: + return [] + + var list_arr: Array = list_value + return extract_list_values(list_arr) + + +func extract_list_values(values: Array) -> Array: + var result: Array = [] + + for item: Variant in values: + if item is Dictionary: + var item_dict: Dictionary = item + var entry_name: Variant = item_dict.get("name", item_dict.get("id", "")) + if entry_name is Dictionary: + var entry_dict: Dictionary = entry_name + entry_name = entry_dict.get("value", "") + result.append(entry_name) + else: + result.append(str(item)) + + return result + + +func _get_storyline() -> InspectableObject: + if not _binding or not _binding.owner: + return null + + var story_owner: InspectableObject = _binding.owner + + # If owner is a node, get its storyline + if story_owner is InspectableNode: + var node: InspectableNode = story_owner + var storyline_id: String = node.storyline_id + if storyline_id: + return ProjectManager.current_project.get_storyline(storyline_id) + + # If owner is already a storyline, return it + if story_owner is StorylineDocument: + return story_owner + + return null + + +func _on_item_selected(index: int) -> void: + var text: String = option_button.get_item_text(index) + emit_value_committed(text) + + +func _exit_tree() -> void: + if not _binding or not _binding.property: + return + var storyline: InspectableObject = _get_storyline() + if storyline: + if storyline.property_changed.is_connected(_on_source_changed): + storyline.property_changed.disconnect(_on_source_changed) + + if _is_listening and storyline.history: + if storyline.history.command_executed.is_connected(_on_storyline_command_executed): + storyline.history.command_executed.disconnect(_on_storyline_command_executed) + if storyline.history.redone.is_connected(_on_storyline_command_executed): + storyline.history.redone.disconnect(_on_storyline_command_executed) + if storyline.history.undone.is_connected(_on_storyline_command_executed): + storyline.history.undone.disconnect(_on_storyline_command_executed) + _is_listening = false diff --git a/common/fields/dropdown/dropdown_field.gd.uid b/common/fields/dropdown/dropdown_field.gd.uid new file mode 100644 index 00000000..3793e12f --- /dev/null +++ b/common/fields/dropdown/dropdown_field.gd.uid @@ -0,0 +1 @@ +uid://c4n8o9p0q1r2t diff --git a/common/fields/dropdown/dropdown_field.tscn b/common/fields/dropdown/dropdown_field.tscn new file mode 100644 index 00000000..644301f6 --- /dev/null +++ b/common/fields/dropdown/dropdown_field.tscn @@ -0,0 +1,18 @@ +[gd_scene format=3 uid="uid://cqhk5p4a8jx0b"] + +[ext_resource type="Script" uid="uid://c4n8pap0q1r2t" path="res://common/fields/dropdown/dropdown_field.gd" id="1_dropdown"] + +[node name="Dropdown" type="VBoxContainer" unique_id=330433791] +offset_right = 76.0 +offset_bottom = 29.0 +size_flags_horizontal = 3 +script = ExtResource("1_dropdown") + +[node name="OptionButton" type="OptionButton" parent="." unique_id=1631779124] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text_overrun_behavior = 5 +clip_text = true +search_bar_enabled = true +search_bar_min_item_count = 5 diff --git a/common/fields/dropdown/index.gd b/common/fields/dropdown/index.gd new file mode 100644 index 00000000..4ba92afd --- /dev/null +++ b/common/fields/dropdown/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://cqhk5p4a8jx0b") + + +func get_metadata() -> Dictionary: + return {"name": "dropdown", "type": ObjectType.FIELD, "color": Color("88c0d0ff"), "default_value": ""} diff --git a/common/fields/dropdown/index.gd.uid b/common/fields/dropdown/index.gd.uid new file mode 100644 index 00000000..f59e2aef --- /dev/null +++ b/common/fields/dropdown/index.gd.uid @@ -0,0 +1 @@ +uid://b3m7n8p9q0r1s diff --git a/common/fields/dynamic/dynamic_field.gd b/common/fields/dynamic/dynamic_field.gd new file mode 100644 index 00000000..f5b7e16a --- /dev/null +++ b/common/fields/dynamic/dynamic_field.gd @@ -0,0 +1,87 @@ +class_name DynamicField extends Field + +var _case_property: String = "" +var _cases: Dictionary = {} + +@onready var field_container: HBoxContainer = %FieldContainer +var _field: Field + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + + if _field: + _field.set_value(value) + + +func get_value() -> Variant: + if _field: + return _field.get_value() + return null + + +func set_editable(is_editable: bool) -> void: + if _field: + _field.set_editable(is_editable) + + +func _on_initialize() -> void: + super._on_initialize() + + _case_property = settings.get("case_property", "") + _cases = settings.get("cases", {}) + + _setup_property_case_listener() + _update_case_field() + + +func _setup_property_case_listener() -> void: + if not _binding or not _binding.property: + return + + var field_owner: InspectableObject = _binding.owner + var case_property: Property = field_owner.get_property(_case_property) + if case_property == null: + return + + case_property.value_changed.connect(_on_property_case_changed) + + +func _update_case_field() -> void: + for child: Node in field_container.get_children(): + child.queue_free() + + var field_owner: InspectableObject = _binding.owner + var case_property: Property = field_owner.get_property(_case_property) + if case_property == null: + return + + var actual_case: String = case_property.get_value() + if not _cases.has(actual_case): + push_warning("Unknown case '%s' for dynamic field" % actual_case) + return + var case_data: Dictionary = _cases[actual_case] + var case_type: String = case_data.get("type") + var _case_default: Variant = case_data.get("default") # TODO: Support default value + var _case_coerce: Variant = case_data.get("coerce") # TODO: Coerce value + + var new_field: Control = FieldBucket.safe_create_field(case_type) + field_container.add_child(new_field) + _field = null + if new_field is Field: + _field = new_field + _binding.property.bind_field.call_deferred(new_field, _binding.owner) + + +func _on_property_case_changed(_old_value: Variant, _new_value: Variant) -> void: + _update_case_field() + + +func _exit_tree() -> void: + if not _binding or not _binding.owner: + return + var field_owner: InspectableObject = _binding.owner + var case_property: Property = field_owner.get_property(_case_property) + if case_property and case_property.value_changed.is_connected(_on_property_case_changed): + case_property.value_changed.disconnect(_on_property_case_changed) diff --git a/common/fields/dynamic/dynamic_field.gd.uid b/common/fields/dynamic/dynamic_field.gd.uid new file mode 100644 index 00000000..d7f16ac5 --- /dev/null +++ b/common/fields/dynamic/dynamic_field.gd.uid @@ -0,0 +1 @@ +uid://6vwlwawmd6qw diff --git a/common/fields/dynamic/dynamic_field.tscn b/common/fields/dynamic/dynamic_field.tscn new file mode 100644 index 00000000..1b69ca0c --- /dev/null +++ b/common/fields/dynamic/dynamic_field.tscn @@ -0,0 +1,12 @@ +[gd_scene format=3 uid="uid://bmpb6yh0x0sup"] + +[ext_resource type="Script" uid="uid://6vwlwawmd6qw" path="res://common/fields/dynamic/dynamic_field.gd" id="1_g3o7u"] + +[node name="DynamicField" type="VBoxContainer" unique_id=1740638762] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_g3o7u") + +[node name="FieldContainer" type="HBoxContainer" parent="." unique_id=1639750187] +unique_name_in_owner = true +layout_mode = 2 diff --git a/common/fields/dynamic/index.gd b/common/fields/dynamic/index.gd new file mode 100644 index 00000000..2460de43 --- /dev/null +++ b/common/fields/dynamic/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://bmpb6yh0x0sup") + + +func get_metadata() -> Dictionary: + return {"name": "dynamic", "type": ObjectType.FIELD, "color": Color("af88d1ff")} diff --git a/common/fields/dynamic/index.gd.uid b/common/fields/dynamic/index.gd.uid new file mode 100644 index 00000000..80131674 --- /dev/null +++ b/common/fields/dynamic/index.gd.uid @@ -0,0 +1 @@ +uid://b6rmavumgsbkj diff --git a/common/fields/field.gd b/common/fields/field.gd new file mode 100644 index 00000000..2cdc74d9 --- /dev/null +++ b/common/fields/field.gd @@ -0,0 +1,67 @@ +@abstract +class_name Field extends VBoxContainer + +## Emitted on every real-time input change (keystroke, drag, slider move). +## FieldBinding uses this for live validation and error feedback only. +## The value is NOT persisted and does NOT create an undo/redo entry. +signal value_changed(new_value: Variant) + +## Emitted when the user confirms the value (Enter, focus lost, item selected, drag end). +## FieldBinding persists the value into the Property, creating an undo/redo entry. +## This is the only signal that writes data to the model. +signal value_committed(new_value: Variant) + +var _binding: FieldBinding +var _default_modulate: Color = Color(1, 1, 1, 1) + +var settings: Dictionary = {} + + +func _ready() -> void: + _default_modulate = modulate + + +func initialize(binding: FieldBinding = null) -> void: + if binding: + _binding = binding + + if _binding and _binding.property: + var property: Property = _binding.property + settings = property.get_settings() + _on_initialize() + + +func _on_initialize() -> void: + pass + + +func emit_value_changed(value: Variant) -> void: + Log.debug("Field value of property '%s' has changed." % _binding.property.name) + value_changed.emit(value) + + +func emit_value_committed(value: Variant) -> void: + Log.info("Field value of property '%s' has been committed." % _binding.property.name) + value_committed.emit(value) + + +func display_error(message: String) -> void: + tooltip_text = message + modulate = _default_modulate if message.is_empty() else Color(1, 0.85, 0.85, 1) + + +func clear_error() -> void: + display_error("") + + +func set_preview() -> void: + pass + + +func prefers_vertical_layout(_settings: Dictionary) -> bool: + return false + + +@abstract func set_value(value: Variant) -> void +@abstract func get_value() -> Variant +@abstract func set_editable(is_editable: bool) -> void diff --git a/common/fields/field.gd.uid b/common/fields/field.gd.uid new file mode 100644 index 00000000..85df71f3 --- /dev/null +++ b/common/fields/field.gd.uid @@ -0,0 +1 @@ +uid://7su6leuvyoaa diff --git a/common/fields/field_binding.gd b/common/fields/field_binding.gd new file mode 100644 index 00000000..138d3d1a --- /dev/null +++ b/common/fields/field_binding.gd @@ -0,0 +1,225 @@ +class_name FieldBinding extends RefCounted + +var property: Property +var field: Field +var descriptor: FieldDescriptor +var owner: InspectableObject + +var _is_syncing: bool = false +var _is_released: bool = false + + +func _init( + p_property: Property, + p_field: Field, + p_descriptor: FieldDescriptor, + p_owner: InspectableObject = null +) -> void: + property = p_property + field = p_field + descriptor = p_descriptor + owner = p_owner + + +func initialize() -> void: + if _is_released: + return + if not is_instance_valid(field): + push_warning("Attempted to initialize a binding with an invalid field instance.") + return + if descriptor == null: + push_warning("Field binding missing descriptor instance.") + return + field.initialize(self) + field.value_changed.connect(_on_field_value_changed) + field.value_committed.connect(_on_field_value_committed) + if property: + property.value_changed.connect(_on_property_value_changed) + field.tree_exiting.connect(_on_field_tree_exiting) + _sync_from_property() + _update_editable_state() + field.clear_error() + + +func release() -> void: + if _is_released: + return + _is_released = true + if is_instance_valid(field): + if field.value_changed.is_connected(_on_field_value_changed): + field.value_changed.disconnect(_on_field_value_changed) + if field.value_committed.is_connected(_on_field_value_committed): + field.value_committed.disconnect(_on_field_value_committed) + if field.tree_exiting.is_connected(_on_field_tree_exiting): + field.tree_exiting.disconnect(_on_field_tree_exiting) + if property and property.value_changed.is_connected(_on_property_value_changed): + property.value_changed.disconnect(_on_property_value_changed) + + +func refresh() -> void: + _sync_from_property() + _update_editable_state() + + +func is_active() -> bool: + return not _is_released + + +func _sync_from_property() -> void: + if not property or not is_instance_valid(field): + return + if not field.is_inside_tree(): + return + _is_syncing = true + var _sync_value: Variant = property.get_value() + if _sync_value == null and descriptor != null and descriptor.default_value != null: + var _dv: Variant = descriptor.default_value + _sync_value = _dv.duplicate(true) if _dv is Dictionary or _dv is Array else _dv + field.set_value(_sync_value) + field.clear_error() + _is_syncing = false + + +func _update_editable_state() -> void: + if not is_instance_valid(field) or not property: + return + var is_editable: bool = ( + property.get_settings_value(PropertySettings.KEY_EDITABLE, true) + and not property.get_settings_value(PropertySettings.KEY_READ_ONLY, false) + and not property.is_input_connected() + ) + field.set_editable(is_editable) + + +func _on_field_value_changed(value: Variant) -> void: + if _is_syncing or _is_released or value == property.get_value(): + return + if not is_instance_valid(field) or not field.is_inside_tree(): # FIXME: changing the type of a variable and next his value, crashes + return + _process_field_value(value, false) + + +func _on_field_value_committed(value: Variant) -> void: + if _is_syncing or _is_released or value == property.get_value(): + return + if not is_instance_valid(field) or not field.is_inside_tree(): + return + _process_field_value(value, true) + _refresh_owner_preview_from_change() + + +func _refresh_owner_preview_from_change() -> void: + if _is_syncing or _is_released: + return + if not owner or not is_instance_valid(owner): + return + owner.rebuild_preview() + + +func _process_field_value(value: Variant, is_commit: bool) -> void: + if not property or descriptor == null: + return + var validation_result: FieldValidationResult = descriptor.validate(value) + if not validation_result.is_valid: + if is_commit: + _sync_from_property() + else: + field.display_error(validation_result.message) + return + + var settings_result: FieldValidationResult = _validate_property_settings(value) + if not settings_result.is_valid: + if is_commit: + _sync_from_property() + else: + field.display_error(settings_result.message) + return + field.clear_error() + var formatted_value: Variant = descriptor.format(value) + + if not is_commit: + return + + # If we update through owner, it triggers an Undo/Redo command and emits property value_changed, + # which in turn will automatically trigger _sync_from_property(). + # However, we'll manually call it below if we just set the property directly. + if owner: + owner.set_property_value(property.name, formatted_value) + else: + property.set_value(formatted_value) + _sync_from_property() + + +func _on_property_value_changed(_old_value: Variant, _new_value: Variant) -> void: + if _is_released or _is_syncing: + return + if not is_instance_valid(field) or not field.is_node_ready(): + return + _is_syncing = true + var _sync_value: Variant = property.get_value() + if _sync_value == null and descriptor != null and descriptor.default_value != null: + var _dv: Variant = descriptor.default_value + _sync_value = _dv.duplicate(true) if _dv is Dictionary or _dv is Array else _dv + # Disabled to prevent useless ui rebuild. + # field.set_value(_sync_value) + field.clear_error() + _is_syncing = false + + +func _on_field_tree_exiting() -> void: + release() + + +func _validate_property_settings(value: Variant) -> FieldValidationResult: + # Required check + if property.get_settings_value(PropertySettings.KEY_REQUIRED, false): + var str_val: String = str(value).strip_edges() if value != null else "" + if str_val.is_empty(): + return FieldValidationResult.failure("This field is required.") + + # Unique check + if property.get_settings_value(PropertySettings.KEY_UNIQUE, false) and is_instance_valid(owner): + var parent_obj: InspectableObject = owner.get_parent_object() if owner.has_method("get_parent_object") else null + if parent_obj: + var parent_prop: String = owner.get_parent_property_name() + var siblings: Array = parent_obj.get_property_children(parent_prop) + + for sibling: InspectableObject in siblings: + if sibling == owner: + continue + var sib_prop: Property = sibling.get_property(property.name) + if sib_prop and sib_prop.value == value: + return FieldValidationResult.failure("This value must be unique.") + + # Validation dict: min_length, max_length, min, max + var rules: Variant = property.get_settings_value(PropertySettings.KEY_VALIDATION, {}) + if typeof(rules) != TYPE_DICTIONARY or rules.is_empty(): + return FieldValidationResult.success() + + var str_value: String = str(value) if value != null else "" + + if rules.has("min_length"): + var min_len: int = int(rules["min_length"]) + if str_value.length() < min_len: + return FieldValidationResult.failure( + "Must be at least %d character(s)." % min_len + ) + + if rules.has("max_length"): + var max_len: int = int(rules["max_length"]) + if str_value.length() > max_len: + return FieldValidationResult.failure( + "Must be at most %d character(s)." % max_len + ) + + if rules.has("min") and value != null: + if str(value).is_valid_float() or value is int or value is float: + if float(value) < float(rules["min"]): + return FieldValidationResult.failure("Must be at least %s." % str(rules["min"])) + + if rules.has("max") and value != null: + if str(value).is_valid_float() or value is int or value is float: + if float(value) > float(rules["max"]): + return FieldValidationResult.failure("Must be at most %s." % str(rules["max"])) + + return FieldValidationResult.success() diff --git a/common/fields/field_binding.gd.uid b/common/fields/field_binding.gd.uid new file mode 100644 index 00000000..4cfef785 --- /dev/null +++ b/common/fields/field_binding.gd.uid @@ -0,0 +1 @@ +uid://dkp1ix1cag4u5 diff --git a/common/fields/field_bucket.gd b/common/fields/field_bucket.gd new file mode 100644 index 00000000..77e055a6 --- /dev/null +++ b/common/fields/field_bucket.gd @@ -0,0 +1,136 @@ +# Autoload +extends Bucket + +const DEFAULT_FIELDS_LOCATION: String = "res://common/fields/" + +var _next_type_id: int = 1 + + +func register_descriptor(descriptor: BucketDescriptor) -> void: + if descriptor == null or descriptor is not FieldDescriptor: + push_warning("Attempted to register a null FieldDescriptor.") + return + if not descriptor.default_settings: + descriptor.default_settings = {} + descriptor.type_id = _next_type_id + _next_type_id += 1 + super.register_descriptor(descriptor) + + +func bind( + property: Property, field: Field, property_owner: InspectableObject = null +) -> FieldBinding: + if not property or not is_instance_valid(field): + return null + var descriptor: FieldDescriptor = get_field_descriptor(property.type) + if descriptor == null: + push_warning("No field descriptor found for type '%s'." % property.type) + return null + var binding: FieldBinding = FieldBinding.new(property, field, descriptor, property_owner) + binding.initialize() + return binding + + +func create_field(field_name: String) -> Field: + var descriptor: FieldDescriptor = get_field_descriptor(field_name) + if not descriptor: + return null + var field: Field = descriptor.instantiate_field() + if field == null: + return null + field.settings = descriptor.default_settings + return field + + +func safe_create_field(field_name: String) -> Control: + var new_field: Field = create_field(field_name) + if new_field: + return new_field + var warn_label: Label = Label.new() + warn_label.theme_type_variation = "WarnLabel" + warn_label.text = "Unknown property type" + return warn_label + + +func get_field_descriptor(field_name: String) -> FieldDescriptor: + return get_descriptor(field_name) as FieldDescriptor + + +func get_scene(field_name: String) -> PackedScene: + var descriptor: FieldDescriptor = get_field_descriptor(field_name) + if descriptor: + return descriptor.scene + return null + + +func get_type_id(field_name: String) -> int: + var descriptor: FieldDescriptor = get_field_descriptor(field_name) + return descriptor.type_id if descriptor else -1 + + +## Returns the metadata dictionary for a registered field type. +func get_metadata(field_name: String) -> Dictionary: + var descriptor: FieldDescriptor = get_field_descriptor(field_name) + if descriptor: + return descriptor.to_metadata() + return {} + + +func _on_refresh() -> void: + _next_type_id = 1 + + +func _search_types() -> void: + var directories: Array = DirAccess.get_directories_at(DEFAULT_FIELDS_LOCATION) + for dir: String in directories: + var script_path: String = DEFAULT_FIELDS_LOCATION.path_join(dir).path_join("index.gd") + if not FileAccess.file_exists(script_path): + continue + var script: GDScript = load(script_path) + if script == null: + push_warning("Failed to load field indexer at %s" % script_path) + continue + var indexer: MonologueIndexer = script.new() + var descriptor: FieldDescriptor = _descriptor_from_indexer(indexer) + if descriptor: + descriptor.default_settings = ( + descriptor.default_settings.duplicate(true) if descriptor.default_settings else {} + ) + register_descriptor(descriptor) + + +func is_compatible(type_id_a: int, type_id_b: int) -> bool: + if type_id_a == type_id_b: + return true + for descriptor: FieldDescriptor in list_descriptors(): + if descriptor.type_id != type_id_a and descriptor.type_id != type_id_b: + continue + var other_id: int = type_id_b if descriptor.type_id == type_id_a else type_id_a + for compat_name: String in descriptor.compatible_types: + if compat_name == "*" or get_type_id(compat_name) == other_id: + return true + return false + + +func _descriptor_from_indexer(indexer: MonologueIndexer) -> FieldDescriptor: + var descriptor: FieldDescriptor + if indexer and indexer.has_method("get_descriptor"): + descriptor = indexer.call("get_descriptor") + if descriptor is FieldDescriptor: + return descriptor + var metadata: Dictionary = {} + if indexer and indexer.has_method("get_metadata"): + metadata = indexer.call("get_metadata") + var descriptor_name: String = metadata.get("name", "") + var scene: PackedScene + if indexer and indexer.has_method("get_scene"): + scene = indexer.call("get_scene") + if descriptor_name.is_empty(): + return null + var new_descriptor: FieldDescriptor = FieldDescriptor.new(descriptor_name, scene, metadata) + var compat_raw: Variant = metadata.get("compatible_types", []) + if compat_raw is Array: + for item: Variant in compat_raw: + if item is String: + new_descriptor.compatible_types.append(item) + return new_descriptor diff --git a/common/fields/field_bucket.gd.uid b/common/fields/field_bucket.gd.uid new file mode 100644 index 00000000..070f8643 --- /dev/null +++ b/common/fields/field_bucket.gd.uid @@ -0,0 +1 @@ +uid://cmte2g7n7ogyd diff --git a/common/fields/field_descriptor.gd b/common/fields/field_descriptor.gd new file mode 100644 index 00000000..72dab188 --- /dev/null +++ b/common/fields/field_descriptor.gd @@ -0,0 +1,79 @@ +class_name FieldDescriptor extends BucketDescriptor + +var scene: PackedScene +var color: Color = Color.WHITE +var default_settings: Dictionary = {} +var validators: Array[Callable] = [] +var formatter: Callable +var type_id: int = -1 +## Names of other field types that this type is connection-compatible with. +var compatible_types: Array[String] = [] +var default_value: Variant = null + + +func _init(p_name: String = "", p_scene: PackedScene = null, metadata: Dictionary = {}) -> void: + super._init(p_name, metadata) + scene = p_scene + default_value = metadata.get("default_value", null) + var raw_color: Variant = metadata.get("color") + if raw_color is Color: + color = raw_color + elif raw_color is String and not raw_color.is_empty(): + color = Color(str(raw_color)) + default_settings = metadata.get("default_settings", {}).duplicate(true) + var validator_list: Variant = metadata.get("validators") + if validator_list is Array: + for validator: Variant in validator_list: + if validator is Callable: + var valid_callable: Callable = validator + register_validator(valid_callable) + var raw_formatter: Variant = metadata.get("formatter") + if raw_formatter is Callable and raw_formatter.is_valid(): + formatter = raw_formatter + + +func instantiate_field() -> Field: + if not scene or not scene.can_instantiate(): + return null + var instance: Node = scene.instantiate() + if instance is Field: + return instance + Log.error("FieldDescriptor '%s' scene does not inherit Field." % name) + return null + + +func register_validator(validator: Callable) -> void: + if validator is Callable and validator.is_valid(): + validators.append(validator) + + +func validate(value: Variant) -> FieldValidationResult: + for validator: Callable in validators: + var result: Variant = validator.call(value) + if result is FieldValidationResult: + if not result.is_valid: + return result + elif result is bool: + if not result: + return FieldValidationResult.failure("Validation failed.") + elif result is String: + var message: String = str(result).strip_edges() + if not message.is_empty(): + return FieldValidationResult.failure(message) + return FieldValidationResult.success() + + +func format(value: Variant) -> Variant: + if formatter and formatter.is_valid(): + return formatter.call(value) + return value + + +func to_metadata() -> Dictionary: + var base: Dictionary = super.to_metadata() + base["color"] = color + base["type_id"] = type_id + base["default_settings"] = default_settings.duplicate(true) + base["compatible_types"] = compatible_types.duplicate() + + return base diff --git a/common/fields/field_descriptor.gd.uid b/common/fields/field_descriptor.gd.uid new file mode 100644 index 00000000..7cda8633 --- /dev/null +++ b/common/fields/field_descriptor.gd.uid @@ -0,0 +1 @@ +uid://c1vc0itqeuxic diff --git a/common/fields/field_validation_result.gd b/common/fields/field_validation_result.gd new file mode 100644 index 00000000..1b7e5184 --- /dev/null +++ b/common/fields/field_validation_result.gd @@ -0,0 +1,17 @@ +class_name FieldValidationResult extends RefCounted + +var is_valid: bool +var message: String + + +func _init(p_is_valid: bool = true, p_message: String = "") -> void: + is_valid = p_is_valid + message = p_message + + +static func success() -> FieldValidationResult: + return FieldValidationResult.new(true, "") + + +static func failure(error_message: String) -> FieldValidationResult: + return FieldValidationResult.new(false, error_message) diff --git a/common/fields/field_validation_result.gd.uid b/common/fields/field_validation_result.gd.uid new file mode 100644 index 00000000..bdd68c06 --- /dev/null +++ b/common/fields/field_validation_result.gd.uid @@ -0,0 +1 @@ +uid://co6und0vwlcp8 diff --git a/common/fields/file/file_field.gd b/common/fields/file/file_field.gd new file mode 100644 index 00000000..17024107 --- /dev/null +++ b/common/fields/file/file_field.gd @@ -0,0 +1,49 @@ +class_name FileField extends Field + +var _filters: PackedStringArray = [] + +@onready var path_line_edit: LineEdit = %PathLineEdit +@onready var browse_button: Button = %BrowseButton + + +func _ready() -> void: + browse_button.pressed.connect(_on_browse_pressed) + + +func _on_initialize() -> void: + var raw_filters: Variant = settings.get(PropertySettings.KEY_FILE_FILTERS, []) + if raw_filters is Array: + _filters = PackedStringArray(raw_filters as Array) + else: + _filters = PackedStringArray() + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + path_line_edit.text = str(value) if value != null else "" + + +func get_value() -> Variant: + return path_line_edit.text + + +func set_editable(is_editable: bool) -> void: + browse_button.disabled = not is_editable + + +func _on_browse_pressed() -> void: + var root_dir: String = "" + var project: MonologueProject = ProjectManager.current_project + if project and not project.project_path.is_empty(): + root_dir = project.project_path.get_base_dir() + EventBus.open_file_request.emit(_on_file_selected, _filters, root_dir) + + +func _on_file_selected(absolute_path: String) -> void: + var relative_path: String = absolute_path + var project: MonologueProject = ProjectManager.current_project + if project and not project.file_path.is_empty(): + relative_path = PathUtil.absolute_to_relative(absolute_path, project.file_path) + path_line_edit.text = relative_path + emit_value_committed(get_value()) diff --git a/common/fields/file/file_field.gd.uid b/common/fields/file/file_field.gd.uid new file mode 100644 index 00000000..3ca66282 --- /dev/null +++ b/common/fields/file/file_field.gd.uid @@ -0,0 +1 @@ +uid://d3nlb4x8yyd80 diff --git a/common/fields/file/file_field.tscn b/common/fields/file/file_field.tscn new file mode 100644 index 00000000..5d911c72 --- /dev/null +++ b/common/fields/file/file_field.tscn @@ -0,0 +1,24 @@ +[gd_scene format=3 uid="uid://c2beyft8hr7na"] + +[ext_resource type="Script" uid="uid://d3nlb4x8yyd80" path="res://common/fields/file/file_field.gd" id="1_filefld"] + +[node name="FileField" type="VBoxContainer" unique_id=1539597147] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_filefld") + +[node name="HBox" type="HBoxContainer" parent="." unique_id=75452527] +layout_mode = 2 + +[node name="PathLineEdit" type="LineEdit" parent="HBox" unique_id=1621716622] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "No file selected" +editable = false + +[node name="BrowseButton" type="Button" parent="HBox" unique_id=1551303389] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Browse for a file" +text = "..." diff --git a/common/fields/file/index.gd b/common/fields/file/index.gd new file mode 100644 index 00000000..8779cdbd --- /dev/null +++ b/common/fields/file/index.gd @@ -0,0 +1,17 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("res://common/fields/file/file_field.tscn") + + +func get_metadata() -> Dictionary: + return { + "name": "file", + "type": ObjectType.FIELD, + "color": Color("80c0ffff"), + "default_value": "", + "default_settings": { + PropertySettings.KEY_FILE_FILTERS: [], + } + } diff --git a/common/fields/file/index.gd.uid b/common/fields/file/index.gd.uid new file mode 100644 index 00000000..cae610b2 --- /dev/null +++ b/common/fields/file/index.gd.uid @@ -0,0 +1 @@ +uid://cx4xv6krqk07m diff --git a/common/fields/float/index.gd b/common/fields/float/index.gd new file mode 100644 index 00000000..ef78dc33 --- /dev/null +++ b/common/fields/float/index.gd @@ -0,0 +1,21 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://c7513qgkxx0m8") + + +func get_metadata() -> Dictionary: + return { + "name": "float", + "type": ObjectType.FIELD, + "color": Color("45cee9ff"), + "compatible_types": ["slider", "float"], + "default_value": 0.0, + "default_settings": { + "rounded": false, + "step": 0.1, + "allow_greater": true, + "allow_lesser": true, + } + } diff --git a/common/fields/float/index.gd.uid b/common/fields/float/index.gd.uid new file mode 100644 index 00000000..055b9adb --- /dev/null +++ b/common/fields/float/index.gd.uid @@ -0,0 +1 @@ +uid://4jy78i6iq3n1 diff --git a/common/fields/int/index.gd b/common/fields/int/index.gd new file mode 100644 index 00000000..f2d120bb --- /dev/null +++ b/common/fields/int/index.gd @@ -0,0 +1,21 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://c7513qgkxx0m8") + + +func get_metadata() -> Dictionary: + return { + "name": "int", + "type": ObjectType.FIELD, + "color": Color("45cee9ff"), + "compatible_types": ["slider", "float"], + "default_value": 0, + "default_settings": { + "rounded": true, + "step": 1.0, + "allow_greater": true, + "allow_lesser": true, + } + } diff --git a/common/fields/int/index.gd.uid b/common/fields/int/index.gd.uid new file mode 100644 index 00000000..b885b671 --- /dev/null +++ b/common/fields/int/index.gd.uid @@ -0,0 +1 @@ +uid://dv7oqa6nhs012 diff --git a/common/fields/int/int_field.gd b/common/fields/int/int_field.gd new file mode 100644 index 00000000..dab6ad85 --- /dev/null +++ b/common/fields/int/int_field.gd @@ -0,0 +1,40 @@ +extends Field + +@onready var spin_box: SpinBox = %SpinBox + + +func _ready() -> void: + spin_box.value_changed.connect(_on_value_changed) + + spin_box.prefix = settings.get("prefix", "") + spin_box.suffix = settings.get("suffix", "") + spin_box.min_value = settings.get("min_value", 0.0) + spin_box.max_value = settings.get("max_value", 100.0) + spin_box.step = settings.get("step", 1.0) + spin_box.page = settings.get("page", 0.0) + spin_box.exp_edit = settings.get("exp_edit", false) + spin_box.rounded = settings.get("rounded", true) + spin_box.allow_greater = settings.get("allow_greater", true) + spin_box.allow_lesser = settings.get("allow_lesser", true) + + +func set_value(value: Variant) -> void: + if value is String or value is bool: + value = float(value) + + if not value is int: + return + + spin_box.value = value + + +func get_value() -> Variant: + return spin_box.value + + +func set_editable(is_editable: bool) -> void: + spin_box.editable = is_editable + + +func _on_value_changed(_value: float) -> void: + emit_value_committed(get_value()) diff --git a/common/fields/int/int_field.gd.uid b/common/fields/int/int_field.gd.uid new file mode 100644 index 00000000..ec64b95c --- /dev/null +++ b/common/fields/int/int_field.gd.uid @@ -0,0 +1 @@ +uid://cle3ul8nmvfd1 diff --git a/common/fields/int/int_field.tscn b/common/fields/int/int_field.tscn new file mode 100644 index 00000000..b223780b --- /dev/null +++ b/common/fields/int/int_field.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3 uid="uid://c7513qgkxx0m8"] + +[ext_resource type="Script" uid="uid://cle3ul8nmvfd1" path="res://common/fields/int/int_field.gd" id="1_ka65w"] + +[node name="IntField" type="VBoxContainer" unique_id=1573396120] +script = ExtResource("1_ka65w") + +[node name="HBox" type="HBoxContainer" parent="." unique_id=1734098527] +layout_mode = 2 + +[node name="SpinBox" type="SpinBox" parent="HBox" unique_id=1740520059] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 diff --git a/common/fields/list/base_list_field.gd b/common/fields/list/base_list_field.gd new file mode 100644 index 00000000..bccf639d --- /dev/null +++ b/common/fields/list/base_list_field.gd @@ -0,0 +1,93 @@ +@abstract +class_name BaseListField extends Field + +var _hide_items: Array[int] = [] +var _command_manager: CommandManager + +@onready var items_container: VBoxContainer = %ItemsContainer +@onready var add_button: Button = %AddButton + + +func _ready() -> void: + super._ready() + _command_manager = CommandManager.new() + if add_button and not add_button.pressed.is_connected(_on_add_button): + add_button.pressed.connect(_on_add_button) + + +func get_items() -> Array: + if not _binding: + return [] + return _binding.owner.get_property_children(_binding.property.name) + + +func hide_item(idx: int) -> void: + if not idx in _hide_items: + _hide_items.append(idx) + _rebuild_ui() + + +func show_all_items() -> void: + _hide_items.clear() + _rebuild_ui() + + +func _clear_container() -> void: + for item: Control in items_container.get_children(): + items_container.remove_child(item) + item.queue_free() + + +func _rebuild_ui() -> void: + if not is_node_ready(): + await ready + + _clear_container() + + var items: Array = get_items() + for item: CollectionItem in items: + var item_idx: int = items.find(item) + if item_idx in _hide_items: + continue + var container: PanelContainer = _create_item_container(not item_idx % 2) + items_container.add_child(container) + CollectionItemHelper.populate_item_view(self, container, item, item_idx) + + _populate_external_items() + + +func set_editable(is_editable: bool) -> void: + if not is_node_ready(): + await ready + + for child: Node in items_container.get_children(): + if child.has_method("set_editable"): + child.call("set_editable", is_editable) + + +func get_item_index(item: CollectionItem) -> int: + return get_items().find(item) + + +func get_list_item_controls(exclude_externals: bool = false) -> Array[Node]: + var items: Array[Node] = items_container.get_children() + for item: Node in items: + var row: ListItemReorderRow = item.get_child(0) + if row.external and exclude_externals: + items.erase(item) + + return items + + +func _create_item_container(is_odd: bool = false) -> PanelContainer: + var container: PanelContainer = PanelContainer.new() + container.theme_type_variation = "ListItemOddPanel" if is_odd else "ListItemPanel" + return container + + +@abstract func reorder_item(_from_index: int, _to_index: int) -> void +@abstract func _on_add_button() -> void +@abstract func _on_edit_item(_index: int) -> void +@abstract func _on_duplicate_item(_index: int) -> void +@abstract func _on_delete_item(_index: int) -> void +@abstract func _populate_external_items() -> void diff --git a/common/fields/list/base_list_field.gd.uid b/common/fields/list/base_list_field.gd.uid new file mode 100644 index 00000000..4c1980b6 --- /dev/null +++ b/common/fields/list/base_list_field.gd.uid @@ -0,0 +1 @@ +uid://btcx4x60v6bdn diff --git a/common/fields/list/collection_item.gd b/common/fields/list/collection_item.gd new file mode 100644 index 00000000..2c11320e --- /dev/null +++ b/common/fields/list/collection_item.gd @@ -0,0 +1,30 @@ +@abstract +class_name CollectionItem extends InspectableObject + + +var storyline_id: String = "" +var _main_property_defined: bool = false + + +func _init(command_manager: CommandManager = null) -> void: + super._init(command_manager) + + +## Define a main property for this list item. +## Items with a main property are automatically exported as sub-ports on the graph. +func define_main_property(pname: String, type: String) -> void: + define_property(pname, null, type, { + "visible_in_inspector": false, + "is_main_property": true, + }) + _main_property_defined = true + + +func has_main_property() -> bool: + return _main_property_defined + +func _on_property_changed(_pname: String) -> void: + pass + + +@abstract func get_preview_property_names() -> Array[String] diff --git a/common/fields/list/collection_item.gd.uid b/common/fields/list/collection_item.gd.uid new file mode 100644 index 00000000..e9d805a2 --- /dev/null +++ b/common/fields/list/collection_item.gd.uid @@ -0,0 +1 @@ +uid://c1xvddq53ohrn diff --git a/common/fields/list/collection_item_helper.gd b/common/fields/list/collection_item_helper.gd new file mode 100644 index 00000000..98bc29eb --- /dev/null +++ b/common/fields/list/collection_item_helper.gd @@ -0,0 +1,90 @@ +class_name CollectionItemHelper +enum MenuActions { EDIT, DUPLICATE, DELETE } + + +static func populate_item_view(owner: BaseListField, item_view: PanelContainer, item: CollectionItem, item_index: int) -> void: + _make_item_row(owner, item_view, item_index, item) + + +static func _create_drag_handle(external: bool = false) -> ListItemDragHandle: + var drag_handle: ListItemDragHandle = ListItemDragHandle.new() + drag_handle.custom_minimum_size = Vector2(24, 24) + drag_handle.theme_type_variation = "ListItemIconButton" + + if not external: + drag_handle.icon = preload("res://ui/assets/icons/drag_handle.svg") + drag_handle.mouse_default_cursor_shape = Control.CURSOR_DRAG + + return drag_handle + + +static func _make_item_row( + owner: BaseListField, + content: PanelContainer, + index: int, + item: CollectionItem, +) -> void: + var is_protected: bool = item.get_property_value("protected") == true + + var row: ListItemReorderRow = ListItemReorderRow.new() + row.owner_field = owner + row.item_index = index + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + content.add_child(row) + + var drag_handle: ListItemDragHandle = _create_drag_handle() + row.drag_handle = drag_handle + row.add_child(drag_handle) + row.init_drag() + + var preview_prop_name: String = item.get_preview_property_names()[0] + var preview_prop: Property = item.get_property(preview_prop_name) + var preview_field: Field = FieldBucket.create_field(preview_prop.type) + preview_field.set_preview() + preview_field.size_flags_horizontal = Control.SIZE_EXPAND_FILL + preview_field.mouse_filter = Control.MOUSE_FILTER_IGNORE + preview_prop.bind_field(preview_field, item, true) + row.add_child(preview_field) + + var edit_button: Button = Button.new() + edit_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER + edit_button.icon = preload("res://ui/assets/icons/pen.svg") + edit_button.tooltip_text = "Edit item" + edit_button.theme_type_variation = "ListItemIconButton" + edit_button.pressed.connect(owner._on_edit_item.bind(index)) + row.add_child(edit_button) + + if not is_protected: + var delete_button: Button = Button.new() + delete_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER + delete_button.icon = preload("res://ui/assets/icons/cross.svg") + delete_button.tooltip_text = "Delete item" + delete_button.theme_type_variation = "ListItemIconButton" + delete_button.pressed.connect(owner._on_delete_item.bind(index)) + row.add_child(delete_button) + + +static func _on_menu_button_id_pressed(id: MenuActions, owner: BaseListField, index: int) -> void: + match id: + MenuActions.DUPLICATE: + owner._on_duplicate_item(index) + MenuActions.DELETE: + owner._on_delete_item(index) + + +static func populate_external_item_view(content: PanelContainer, name: String) -> void: + var row: ListItemReorderRow = ListItemReorderRow.new() + row.external = true + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + content.add_child(row) + + var drag_handle: ListItemDragHandle = _create_drag_handle(true) + row.drag_handle = drag_handle + row.add_child(drag_handle) + + var ext_label: Label = Label.new() + ext_label.text = name + ext_label.theme_type_variation = "NoteLabel" + ext_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + ext_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.add_child(ext_label) diff --git a/common/fields/list/collection_item_helper.gd.uid b/common/fields/list/collection_item_helper.gd.uid new file mode 100644 index 00000000..1aeae2fb --- /dev/null +++ b/common/fields/list/collection_item_helper.gd.uid @@ -0,0 +1 @@ +uid://jlkufxttniin diff --git a/common/fields/list/index.gd b/common/fields/list/index.gd new file mode 100644 index 00000000..fa5f567e --- /dev/null +++ b/common/fields/list/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://d2s4kv1234abc") + + +func get_metadata() -> Dictionary: + return {"name": "list", "type": ObjectType.FIELD, "color": Color("b48eadff"), "default_value": []} diff --git a/common/fields/list/index.gd.uid b/common/fields/list/index.gd.uid new file mode 100644 index 00000000..b252ebed --- /dev/null +++ b/common/fields/list/index.gd.uid @@ -0,0 +1 @@ +uid://d5o9p0q1r2s3u diff --git a/common/fields/list/list_field.gd b/common/fields/list/list_field.gd new file mode 100644 index 00000000..0dec53e7 --- /dev/null +++ b/common/fields/list/list_field.gd @@ -0,0 +1,158 @@ +class_name ListField extends BaseListField + +var _item_type: String = "text" +var _item_properties: Array[Property] = [] +var _is_updating: bool = false + + +func _on_initialize() -> void: + super._on_initialize() + var property: Property = _binding.property if _binding else null + if not property: + Log.warning("ListField is not binded.") + return + + _item_type = str(property.get_settings_value(PropertySettings.KEY_ITEM_TYPE, "text")) + if not FieldBucket.get_field_descriptor(_item_type): + Log.error("Can't find list item field type %s." % _item_type) + + +func _get_items() -> Array: + return _item_properties + + +func set_value(value: Variant) -> void: + if value is not Array: + value = [] + + _is_updating = true + _item_properties.clear() + for item_value: Variant in value: + _item_properties.append(_create_item_property(item_value)) + _is_updating = false + _rebuild_ui() + + +func get_value() -> Variant: + var result: Array = [] + for item_property: Property in _item_properties: + result.append(item_property.get_value()) + return result + + +func add_item() -> void: + _item_properties.append(_create_item_property()) + emit_value_committed(get_value()) + _rebuild_ui() + + +func reorder_item(from_index: int, to_index: int) -> void: + if from_index < 0 or from_index >= _item_properties.size(): + return + if to_index < 0 or to_index >= _item_properties.size(): + return + + var item_property: Property = _item_properties.pop_at(from_index) + _item_properties.insert(to_index, item_property) + emit_value_committed(get_value()) + _rebuild_ui() + + +func _on_add_button() -> void: + add_item() + + +func _on_delete_item(index: int) -> void: + if not _is_valid_index(index): + return + _item_properties.remove_at(index) + emit_value_committed(get_value()) + _rebuild_ui() + + +func _on_edit_item(_index: int) -> void: + pass + + +func _on_duplicate_item(index: int) -> void: + if not _is_valid_index(index): + return + var duplicated_value: Variant = _item_properties[index].get_value() + _item_properties.insert(index + 1, _create_item_property(duplicated_value)) + emit_value_committed(get_value()) + _rebuild_ui() + + +func _rebuild_ui() -> void: + if not is_node_ready(): + await ready + + _clear_container() + + for index in range(_item_properties.size()): + if index in _hide_items: + continue + var item_property: Property = _item_properties[index] + var container: PanelContainer = _create_item_container(not index % 2) + items_container.add_child(container) + + var row: ListItemReorderRow = ListItemReorderRow.new() + row.owner_field = self + row.item_index = index + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + container.add_child(row) + + var drag_handle: ListItemDragHandle = ListItemDragHandle.new() + drag_handle.custom_minimum_size = Vector2(24, 24) + drag_handle.theme_type_variation = "ListItemIconButton" + drag_handle.icon = preload("res://ui/assets/icons/drag_handle.svg") + drag_handle.mouse_default_cursor_shape = Control.CURSOR_DRAG + row.drag_handle = drag_handle + row.add_child(drag_handle) + row.init_drag() + + var item_field: Field = FieldBucket.create_field(_item_type) + if not item_field: + continue + item_field.set_preview() + item_field.size_flags_horizontal = Control.SIZE_EXPAND_FILL + item_property.bind_field(item_field, null, true) + row.add_child(item_field) + + var delete_button: Button = Button.new() + delete_button.size_flags_vertical = Control.SIZE_SHRINK_CENTER + delete_button.icon = preload("res://ui/assets/icons/cross.svg") + delete_button.tooltip_text = "Delete item" + delete_button.theme_type_variation = "ListItemIconButton" + delete_button.pressed.connect(_on_delete_item.bind(index)) + row.add_child(delete_button) + + +func _create_item_property(value: Variant = null) -> Property: + var descriptor: FieldDescriptor = FieldBucket.get_field_descriptor(_item_type) + var default_value: Variant = value + if default_value == null and descriptor: + default_value = descriptor.default_value + if default_value is Dictionary or default_value is Array: + default_value = default_value.duplicate(true) + var prop: Property = Property.new("item", default_value, _item_type, { + "visible_in_inspector": false, + "visible_in_graph": false, + "expand": true, + }) + prop.value_changed.connect(_on_item_property_changed.bind(prop)) + return prop + + +func _on_item_property_changed(_old_value: Variant, _new_value: Variant, _item_property: Property) -> void: + if _is_updating: + return + emit_value_committed(get_value()) + + +func _is_valid_index(index: int) -> bool: + return index >= 0 and index < _item_properties.size() + +func _populate_external_items() -> void: + # TODO + pass diff --git a/common/fields/list/list_field.gd.uid b/common/fields/list/list_field.gd.uid new file mode 100644 index 00000000..f74b59ec --- /dev/null +++ b/common/fields/list/list_field.gd.uid @@ -0,0 +1 @@ +uid://e6p0q1r2s3t4v diff --git a/common/fields/list/list_field.tscn b/common/fields/list/list_field.tscn new file mode 100644 index 00000000..61d1a31a --- /dev/null +++ b/common/fields/list/list_field.tscn @@ -0,0 +1,27 @@ +[gd_scene format=3 uid="uid://d2s4kv1234abc"] + +[ext_resource type="Script" uid="uid://bcb45yus8vyd4" path="res://common/fields/list/list_field.gd" id="1_list"] + +[node name="ListField" type="VBoxContainer" unique_id=1282527206] +offset_right = 300.0 +offset_bottom = 35.0 +size_flags_horizontal = 3 +size_flags_vertical = 0 +script = ExtResource("1_list") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1918604036] +layout_mode = 2 +theme_type_variation = &"ListFieldPanel" + +[node name="ItemsContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1665726569] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"VBoxContainerCompact" + +[node name="AddButton" type="Button" parent="." unique_id=1510650974] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"PlainButton" +text = "Add Item" +text_overrun_behavior = 5 diff --git a/common/fields/list/list_item_drag_handle.gd b/common/fields/list/list_item_drag_handle.gd new file mode 100644 index 00000000..1da09951 --- /dev/null +++ b/common/fields/list/list_item_drag_handle.gd @@ -0,0 +1,19 @@ +class_name ListItemDragHandle extends Button + +signal grabbed +signal released + +var grabbed_position: Vector2 = Vector2.ZERO + +func _ready() -> void: + button_down.connect(_on_button_down) + button_up.connect(_on_button_up) + + +func _on_button_down() -> void: + grabbed_position = get_global_mouse_position() + grabbed.emit() + + +func _on_button_up() -> void: + released.emit() diff --git a/common/fields/list/list_item_drag_handle.gd.uid b/common/fields/list/list_item_drag_handle.gd.uid new file mode 100644 index 00000000..748d053e --- /dev/null +++ b/common/fields/list/list_item_drag_handle.gd.uid @@ -0,0 +1 @@ +uid://f2dipvo2mrcm diff --git a/common/fields/list/list_item_reorder_row.gd b/common/fields/list/list_item_reorder_row.gd new file mode 100644 index 00000000..ebcefbab --- /dev/null +++ b/common/fields/list/list_item_reorder_row.gd @@ -0,0 +1,50 @@ +class_name ListItemReorderRow extends HBoxContainer + +var owner_field: BaseListField +var item_index: int = -1 +var external: bool = false +var drag_handle: ListItemDragHandle + + +func init_drag() -> void: + drag_handle.grabbed.connect(_on_drag_handle_grabbed) + drag_handle.released.connect(_on_drag_handle_released) + + +func _on_drag_handle_grabbed() -> void: + if external: + return + + get_parent().offset_transform_enabled = true + get_parent().z_index = 10 + + +func _on_drag_handle_released() -> void: + var drag_position_y: float = get_parent().global_position.y + get_parent().offset_transform_position.y + var new_index: int = item_index + var rows: Array[Node] = owner_field.get_list_item_controls(true) + for row: Control in rows: + var row_index: int = rows.find(row) + if row == get_parent(): + continue + + # Higher row + if row_index < item_index and row.global_position.y > drag_position_y: + new_index = min(new_index, row_index) + elif row_index > item_index and row.global_position.y < drag_position_y: + new_index = max(new_index, row_index) + + owner_field.reorder_item(item_index, new_index) + + get_parent().offset_transform_enabled = false + get_parent().z_index = 0 + + +func _process(_delta: float) -> void: + if external: + return + + var mouse_position: Vector2 = get_global_mouse_position() + var difference: Vector2 = mouse_position - drag_handle.grabbed_position + + get_parent().offset_transform_position.y = difference.y diff --git a/common/fields/list/list_item_reorder_row.gd.uid b/common/fields/list/list_item_reorder_row.gd.uid new file mode 100644 index 00000000..5a5fca89 --- /dev/null +++ b/common/fields/list/list_item_reorder_row.gd.uid @@ -0,0 +1 @@ +uid://s0ainrojtr7v diff --git a/common/fields/option/index.gd b/common/fields/option/index.gd new file mode 100644 index 00000000..fbc09fae --- /dev/null +++ b/common/fields/option/index.gd @@ -0,0 +1,14 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return PackedScene.new() + + +func get_metadata() -> Dictionary: + return { + "name": "option", + "type": ObjectType.FIELD, + "color": Color("e89145"), + "compatible_types": ["option"], + } diff --git a/common/fields/option/index.gd.uid b/common/fields/option/index.gd.uid new file mode 100644 index 00000000..b7cdcf8b --- /dev/null +++ b/common/fields/option/index.gd.uid @@ -0,0 +1 @@ +uid://b5o10kpa7oacn diff --git a/common/fields/slider/index.gd b/common/fields/slider/index.gd new file mode 100644 index 00000000..a4d0373e --- /dev/null +++ b/common/fields/slider/index.gd @@ -0,0 +1,15 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://bap1tk0q1l1kf") + + +func get_metadata() -> Dictionary: + return { + "name": "slider", + "type": ObjectType.FIELD, + "color": Color("45cee9ff"), + "default_value": 0.0, + "compatible_types": ["int", "float"] + } diff --git a/common/fields/slider/index.gd.uid b/common/fields/slider/index.gd.uid new file mode 100644 index 00000000..faacb968 --- /dev/null +++ b/common/fields/slider/index.gd.uid @@ -0,0 +1 @@ +uid://dnduvfbag7bv5 diff --git a/common/fields/slider/slider_field.gd b/common/fields/slider/slider_field.gd new file mode 100644 index 00000000..ef466c5e --- /dev/null +++ b/common/fields/slider/slider_field.gd @@ -0,0 +1,65 @@ +extends Field + +@onready var slider: HSlider = %Slider +@onready var spin_box: SpinBox = %SpinBox + +var _is_updating: bool = false + + +func _ready() -> void: + slider.value_changed.connect(_on_slider_value_changed) + slider.drag_ended.connect(_on_slider_drag_ended) + spin_box.value_changed.connect(_on_spin_box_value_changed) + + for control: Range in [slider, spin_box]: + control.min_value = settings.get("min_value", 0.0) + control.max_value = settings.get("max_value", 100.0) + control.step = settings.get("step", 1.0) + control.page = settings.get("page", 0.0) + control.exp_edit = settings.get("exp_edit", false) + control.rounded = settings.get("rounded", false) + control.allow_greater = settings.get("allow_greater", false) + control.allow_lesser = settings.get("allow_lesser", false) + spin_box.prefix = settings.get("prefix", "") + spin_box.suffix = settings.get("suffix", "") + + +func set_value(value: Variant) -> void: + slider.value = value + spin_box.value = value + + +func get_value() -> Variant: + return slider.value + + +func set_editable(is_editable: bool) -> void: + slider.editable = is_editable + spin_box.editable = is_editable + + +func _on_slider_value_changed(value: float) -> void: + _is_updating = true + spin_box.value = value + _is_updating = false + + +func _on_slider_drag_ended(value: float) -> void: + if _is_updating: + return + + _is_updating = true + spin_box.value = value + _is_updating = false + emit_value_committed(get_value()) + + + +func _on_spin_box_value_changed(value: float) -> void: + if _is_updating: + return + + _is_updating = true + slider.value = value + _is_updating = false + emit_value_committed(get_value()) diff --git a/common/fields/slider/slider_field.gd.uid b/common/fields/slider/slider_field.gd.uid new file mode 100644 index 00000000..1883ebed --- /dev/null +++ b/common/fields/slider/slider_field.gd.uid @@ -0,0 +1 @@ +uid://drp555ysvqbi3 diff --git a/common/fields/slider/slider_field.tscn b/common/fields/slider/slider_field.tscn new file mode 100644 index 00000000..1222965a --- /dev/null +++ b/common/fields/slider/slider_field.tscn @@ -0,0 +1,20 @@ +[gd_scene format=3 uid="uid://bap1tk0q1l1kf"] + +[ext_resource type="Script" uid="uid://drp555ysvqbi3" path="res://common/fields/slider/slider_field.gd" id="1_7j6v4"] + +[node name="SliderField" type="VBoxContainer" unique_id=2139279077] +script = ExtResource("1_7j6v4") + +[node name="HBox" type="HBoxContainer" parent="." unique_id=2082805658] +layout_mode = 2 + +[node name="Slider" type="HSlider" parent="HBox" unique_id=608407825] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +ticks_position = 3 + +[node name="SpinBox" type="SpinBox" parent="HBox" unique_id=261675302] +unique_name_in_owner = true +layout_mode = 2 diff --git a/common/fields/text/index.gd b/common/fields/text/index.gd new file mode 100644 index 00000000..83dd5ca3 --- /dev/null +++ b/common/fields/text/index.gd @@ -0,0 +1,15 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://be0xxn5gocqjo") + + +func get_metadata() -> Dictionary: + return { + "name": "text", + "type": ObjectType.FIELD, + "color": Color("af85fdff"), + "default_value": "", + "compatible_types": ["textarea"], + } diff --git a/common/fields/text/index.gd.uid b/common/fields/text/index.gd.uid new file mode 100644 index 00000000..c82c935b --- /dev/null +++ b/common/fields/text/index.gd.uid @@ -0,0 +1 @@ +uid://86csxw8qmvb4 diff --git a/common/fields/text/text_field.gd b/common/fields/text/text_field.gd new file mode 100644 index 00000000..727c40a3 --- /dev/null +++ b/common/fields/text/text_field.gd @@ -0,0 +1,111 @@ +class_name TextField +extends Field + +var _is_multiline: bool = false +var _is_preview: bool = false + +@onready var line_edit: LineEdit = %LineEdit +@onready var text_edit: TextEdit = %TextEdit + + +func _ready() -> void: + super._ready() + line_edit.text_changed.connect(_on_text_changed) + line_edit.text_submitted.connect(_on_text_submitted) + line_edit.focus_exited.connect(_on_focus_exited) + text_edit.text_changed.connect(_on_textarea_changed) + text_edit.focus_exited.connect(_on_textarea_focus_exited) + + +func _on_initialize() -> void: + _is_multiline = settings.get(PropertySettings.KEY_MULTILINE, false) + var placeholder: String = settings.get(PropertySettings.KEY_PLACEHOLDER, + settings.get("placeholder", "")) + + text_edit.placeholder_text = placeholder + line_edit.placeholder_text = placeholder + var rows: int = settings.get(PropertySettings.KEY_ROWS, settings.get("rows", 4)) + _apply_textarea_height.call_deferred(text_edit, rows) + + if not _is_preview: + line_edit.visible = not _is_multiline + text_edit.visible = _is_multiline + + +func _apply_textarea_height(te: TextEdit, rows: int) -> void: + var reset_te: bool = not te.text + if reset_te: + te.text = " " + + var sb: StyleBox = te.get_theme_stylebox("normal") + var padding: float = (sb.content_margin_bottom + sb.content_margin_top) if sb else 8.0 + te.custom_minimum_size.y = te.get_line_height() * rows + padding + + if reset_te: + te.text = "" + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + + text_edit.text = str(value) + line_edit.text = str(value) + + +func get_value() -> Variant: + return text_edit.text if _is_multiline else line_edit.text + + +func set_editable(is_editable: bool) -> void: + line_edit.editable = is_editable + text_edit.editable = is_editable + + +func set_preview() -> void: + _is_preview = true + if not is_node_ready(): + await ready + + line_edit.show() + text_edit.hide() + line_edit.theme_type_variation = "LineEditListItemPreview" + + +func display_error(message: String) -> void: + super.display_error(message) + if message.is_empty(): + line_edit.remove_theme_color_override("font_color") + text_edit.remove_theme_color_override("font_color") + else: + var color: Color = ThemeLayout.fail_color + line_edit.add_theme_color_override("font_color", color) + text_edit.add_theme_color_override("font_color", color) + + +func prefers_vertical_layout(p_settings: Dictionary) -> bool: + return p_settings.get(PropertySettings.KEY_MULTILINE, false) + + +func _on_text_changed(new_text: String) -> void: + emit_value_changed(new_text) + + +func _on_text_submitted(submitted_text: String) -> void: + emit_value_committed(submitted_text) + + +func _on_focus_exited() -> void: + if not is_inside_tree(): + return + emit_value_committed(line_edit.text) + + +func _on_textarea_changed() -> void: + emit_value_changed(text_edit.text) + + +func _on_textarea_focus_exited() -> void: + if not is_inside_tree(): + return + emit_value_committed(text_edit.text) diff --git a/common/fields/text/text_field.gd.uid b/common/fields/text/text_field.gd.uid new file mode 100644 index 00000000..789d3ca6 --- /dev/null +++ b/common/fields/text/text_field.gd.uid @@ -0,0 +1 @@ +uid://ntsxdpbgyehu diff --git a/common/fields/text/text_field.tscn b/common/fields/text/text_field.tscn new file mode 100644 index 00000000..7f7648b3 --- /dev/null +++ b/common/fields/text/text_field.tscn @@ -0,0 +1,27 @@ +[gd_scene format=3 uid="uid://be0xxn5gocqjo"] + +[ext_resource type="Script" uid="uid://ntsxdpbgyehu" path="res://common/fields/text/text_field.gd" id="1_56141"] + +[node name="VBoxContainer" type="VBoxContainer" unique_id=1883872284] +offset_right = 76.0 +offset_bottom = 29.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1_56141") + +[node name="LineEdit" type="LineEdit" parent="." unique_id=882645481] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="TextEdit" type="TextEdit" parent="." unique_id=882645482] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +backspace_deletes_composite_character_enabled = true +wrap_mode = 1 +caret_blink = true +caret_multiple = false diff --git a/common/fields/textarea/index.gd b/common/fields/textarea/index.gd new file mode 100644 index 00000000..861beccf --- /dev/null +++ b/common/fields/textarea/index.gd @@ -0,0 +1,19 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://be0xxn5gocqjo") # text_field.tscn + + +func get_metadata() -> Dictionary: + return { + "name": "textarea", + "type": ObjectType.FIELD, + "color": Color("af85fdff"), + "default_value": "", + "compatible_types": ["text"], + "default_settings": { + PropertySettings.KEY_MULTILINE: true, + PropertySettings.KEY_ROWS: 3, + } + } diff --git a/common/fields/textarea/index.gd.uid b/common/fields/textarea/index.gd.uid new file mode 100644 index 00000000..82b0574f --- /dev/null +++ b/common/fields/textarea/index.gd.uid @@ -0,0 +1 @@ +uid://yocjhdu0idyk diff --git a/common/fields/translatable/index.gd b/common/fields/translatable/index.gd new file mode 100644 index 00000000..6da29946 --- /dev/null +++ b/common/fields/translatable/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://bg1c7vlg63ty1") + + +func get_metadata() -> Dictionary: + return {"name": "translatable", "type": ObjectType.FIELD, "color": Color("628cffff"), "default_value": {"en": ""}} diff --git a/common/fields/translatable/index.gd.uid b/common/fields/translatable/index.gd.uid new file mode 100644 index 00000000..43c205e7 --- /dev/null +++ b/common/fields/translatable/index.gd.uid @@ -0,0 +1 @@ +uid://dhdkaxtqf34sv diff --git a/common/fields/translatable/translatable_field.gd b/common/fields/translatable/translatable_field.gd new file mode 100644 index 00000000..cd4225d0 --- /dev/null +++ b/common/fields/translatable/translatable_field.gd @@ -0,0 +1,119 @@ +extends TextField + +var _values: Dictionary = {} + +@onready var localization_option: OptionButton = %LocalizationOption + + +func _ready() -> void: + super._ready() + localization_option.item_selected.connect(_on_localization_option_selected) + EventBus.load_languages.connect(_on_load_languages) + EventBus.refresh.connect(_on_language_refresh) + var project: MonologueProject = ProjectManager.current_project + if project: + _populate_option(project.get_collection_value("languages")) + + +func _populate_option(languages: Array) -> void: + if languages.size() <= 1: + localization_option.hide() + + localization_option.clear() + for lang: Dictionary in languages: + var code: String = lang.get("code", {}).get("value", "") + if code.is_empty(): + code = lang.get("code", "en") + localization_option.add_item(code) + localization_option.set_item_metadata(localization_option.item_count - 1, code) + _sync_option_to_global() + + +func _sync_option_to_global() -> void: + var project: MonologueProject = ProjectManager.current_project + var active: String = project.active_language_code if project else "en" + for i: int in localization_option.item_count: + if localization_option.get_item_metadata(i) == active: + localization_option.select(i) + return + if localization_option.item_count > 0: + localization_option.select(0) + + +func _current_language_code() -> String: + var project: MonologueProject = ProjectManager.current_project + var code: String = project.active_language_code if project else "en" + return code if not code.is_empty() else "en" + + +func set_value(value: Variant) -> void: + if not is_node_ready(): + await ready + _values = (value if value is Dictionary else {_current_language_code(): str(value)}).duplicate() + _refresh_widget_text() + + +func _refresh_widget_text() -> void: + var text: String = _values.get(_current_language_code(), "") + if text_edit.text != text: + text_edit.text = text + if line_edit.text != text: + line_edit.text = text + + +func get_value() -> Variant: + return _values.duplicate() + + +func set_editable(is_editable: bool) -> void: + super.set_editable(is_editable) + localization_option.disabled = not is_editable + + +func _on_text_changed(new_text: String) -> void: + _values[_current_language_code()] = new_text + emit_value_changed(_values.duplicate()) + + +func _on_text_submitted(submitted_text: String) -> void: + _values[_current_language_code()] = submitted_text + emit_value_committed(_values.duplicate()) + + +func _on_focus_exited() -> void: + if not is_inside_tree(): + return + _values[_current_language_code()] = line_edit.text + emit_value_committed(_values.duplicate()) + + +func _on_textarea_changed() -> void: + _values[_current_language_code()] = text_edit.text + emit_value_changed(_values.duplicate()) + + +func _on_textarea_focus_exited() -> void: + if not is_inside_tree(): + return + _values[_current_language_code()] = text_edit.text + emit_value_committed(_values.duplicate()) + + +func _on_load_languages(languages: Array, _graph: MonologueGraphEdit) -> void: + _populate_option(languages) + _refresh_widget_text() + + +func _on_language_refresh() -> void: + _sync_option_to_global() + _refresh_widget_text() + + +func _on_localization_option_selected(idx: int) -> void: + var code: String = localization_option.get_item_metadata(idx) + var project: MonologueProject = ProjectManager.current_project + if project and project.active_language_code == code: + return + if project: + project.active_language_code = code + EventBus.refresh.emit() diff --git a/common/fields/translatable/translatable_field.gd.uid b/common/fields/translatable/translatable_field.gd.uid new file mode 100644 index 00000000..037c7696 --- /dev/null +++ b/common/fields/translatable/translatable_field.gd.uid @@ -0,0 +1 @@ +uid://bmx1121byfwgx diff --git a/common/fields/translatable/translatable_field.tscn b/common/fields/translatable/translatable_field.tscn new file mode 100644 index 00000000..41f10d9c --- /dev/null +++ b/common/fields/translatable/translatable_field.tscn @@ -0,0 +1,33 @@ +[gd_scene format=3 uid="uid://bg1c7vlg63ty1"] + +[ext_resource type="Script" uid="uid://bmx1121byfwgx" path="res://common/fields/translatable/translatable_field.gd" id="1_mdw0x"] + +[node name="TranslatableField" type="VBoxContainer" unique_id=2026809464] +script = ExtResource("1_mdw0x") + +[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=1557230006] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="LineEdit" type="LineEdit" parent="HBoxContainer" unique_id=826857661] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +caret_blink = true + +[node name="TextEdit" type="TextEdit" parent="HBoxContainer" unique_id=826857662] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +caret_blink = true + +[node name="LocalizationOption" type="OptionButton" parent="HBoxContainer" unique_id=1666513133] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 0 + +[node name="RichTextLabel" type="RichTextLabel" parent="." unique_id=2010589506] +layout_mode = 2 diff --git a/common/fields/vector2/index.gd b/common/fields/vector2/index.gd new file mode 100644 index 00000000..2f2254b5 --- /dev/null +++ b/common/fields/vector2/index.gd @@ -0,0 +1,9 @@ +extends MonologueIndexer + + +func get_scene() -> PackedScene: + return preload("uid://bm7pyxhgln22j") + + +func get_metadata() -> Dictionary: + return {"name": "vector2", "type": ObjectType.FIELD, "color": Color("09b58dff"), "default_value": [0.0, 0.0]} diff --git a/common/fields/vector2/index.gd.uid b/common/fields/vector2/index.gd.uid new file mode 100644 index 00000000..aa4b7d7f --- /dev/null +++ b/common/fields/vector2/index.gd.uid @@ -0,0 +1 @@ +uid://x7lsoen43sjw diff --git a/common/fields/vector2/vector2_field.gd b/common/fields/vector2/vector2_field.gd new file mode 100644 index 00000000..37fd1023 --- /dev/null +++ b/common/fields/vector2/vector2_field.gd @@ -0,0 +1,32 @@ +class_name Vector2Field extends Field + +@onready var spinbox_x: SpinBox = %SpinBoxX +@onready var spinbox_y: SpinBox = %SpinBoxY + + +func _ready() -> void: + spinbox_x.value_changed.connect(_on_spinbox_value_changed) + spinbox_y.value_changed.connect(_on_spinbox_value_changed) + + +func set_value(value: Variant) -> void: + if value is Array: + spinbox_x.value = value[0] + spinbox_y.value = value[1] + elif value is Vector2: + spinbox_x.value = value.x + spinbox_y.value = value.y + + +func set_editable(is_editable: bool) -> void: + spinbox_x.editable = is_editable + spinbox_y.editable = is_editable + + +func get_value() -> Variant: + return [spinbox_x.value, spinbox_y.value] + + +func _on_spinbox_value_changed(_value: float) -> void: + emit_value_committed(get_value()) + diff --git a/common/fields/vector2/vector2_field.gd.uid b/common/fields/vector2/vector2_field.gd.uid new file mode 100644 index 00000000..22743d00 --- /dev/null +++ b/common/fields/vector2/vector2_field.gd.uid @@ -0,0 +1 @@ +uid://bg5qi01l20iph diff --git a/common/fields/vector2/vector2_field.tscn b/common/fields/vector2/vector2_field.tscn new file mode 100644 index 00000000..d253098a --- /dev/null +++ b/common/fields/vector2/vector2_field.tscn @@ -0,0 +1,29 @@ +[gd_scene format=3 uid="uid://bm7pyxhgln22j"] + +[ext_resource type="Script" uid="uid://bg5qi01l20iph" path="res://common/fields/vector2/vector2_field.gd" id="1_tvllh"] + +[node name="Vector2" type="VBoxContainer" unique_id=318006966] +script = ExtResource("1_tvllh") + +[node name="HBox" type="HBoxContainer" parent="." unique_id=1706315426] +layout_mode = 2 + +[node name="SpinBoxX" type="SpinBox" parent="HBox" unique_id=152250429] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +step = 0.1 +allow_greater = true +allow_lesser = true +alignment = 3 +suffix = "x" + +[node name="SpinBoxY" type="SpinBox" parent="HBox" unique_id=1669371096] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +step = 0.1 +allow_greater = true +allow_lesser = true +alignment = 3 +suffix = "y" diff --git a/common/graph_node_row.gd b/common/graph_node_row.gd new file mode 100644 index 00000000..58427ee0 --- /dev/null +++ b/common/graph_node_row.gd @@ -0,0 +1,36 @@ +class_name GraphNodeRow + +var _key: String = "" +var _type: String = "" +var _property_name: String = "" +var _enable_left_port: bool = false +var _enable_right_port: bool = false +## If non-empty, this row represents a sub-port of a list item. +## Format: "property_name:item_id" +var sub_property_id: String = "" +## Port size: "normal" or "large" +var port_size: String = "normal" + +func _init( + key: String, type: String = "", enable_left_port: bool = false, enable_right_port: bool = true +) -> void: + _key = key + _type = type + _enable_left_port = enable_left_port + _enable_right_port = enable_right_port + + +func get_key() -> String: + return _key + + +func get_type() -> String: + return _type + + +## Returns the connection-level identifier for this row. +## For sub-ports it is the composite "property:item_id"; otherwise the property name. +func get_connection_name() -> String: + if not sub_property_id.is_empty(): + return sub_property_id + return _property_name diff --git a/common/graph_node_row.gd.uid b/common/graph_node_row.gd.uid new file mode 100644 index 00000000..32da1f7f --- /dev/null +++ b/common/graph_node_row.gd.uid @@ -0,0 +1 @@ +uid://bu0v85m5t527i diff --git a/common/history/add_nodes_command.gd b/common/history/add_nodes_command.gd new file mode 100644 index 00000000..a062ec77 --- /dev/null +++ b/common/history/add_nodes_command.gd @@ -0,0 +1,36 @@ +class_name AddNodesCommand extends Command + +var storyline_id: String +var nodes: Array = [] + + +func _init( + p_storyline_id: String, + p_nodes: Array, +) -> void: + storyline_id = p_storyline_id + nodes = p_nodes + + +func execute() -> void: + var storyline: StorylineDocument = ProjectManager.current_project.get_storyline(storyline_id) + if not storyline: + return + + for node: InspectableNode in nodes: + storyline.add_node(node) + EventBus.refresh_graph.emit() + + +func undo() -> void: + var storyline: StorylineDocument = ProjectManager.current_project.get_storyline(storyline_id) + if not storyline: + return + + for node: InspectableNode in nodes: + storyline.remove_node(node) + EventBus.refresh_graph.emit() + + +func get_description() -> String: + return "Add %s nodes" % nodes.size() diff --git a/common/history/add_nodes_command.gd.uid b/common/history/add_nodes_command.gd.uid new file mode 100644 index 00000000..3d6a21d2 --- /dev/null +++ b/common/history/add_nodes_command.gd.uid @@ -0,0 +1 @@ +uid://cjf8xj7y7ka0r diff --git a/common/history/command.gd b/common/history/command.gd new file mode 100644 index 00000000..ea4db5b8 --- /dev/null +++ b/common/history/command.gd @@ -0,0 +1,6 @@ +@abstract +class_name Command extends RefCounted + +@abstract func execute() -> void +@abstract func undo() -> void +@abstract func get_description() -> String diff --git a/common/history/command.gd.uid b/common/history/command.gd.uid new file mode 100644 index 00000000..00111e41 --- /dev/null +++ b/common/history/command.gd.uid @@ -0,0 +1 @@ +uid://dxrwkktseecun diff --git a/common/history/command_manager.gd b/common/history/command_manager.gd new file mode 100644 index 00000000..5bcc024e --- /dev/null +++ b/common/history/command_manager.gd @@ -0,0 +1,102 @@ +class_name CommandManager extends RefCounted + +var undo_redo: UndoRedo + +signal command_executed +signal undone +signal redone +signal history_changed + + +func _init(max_history: int = 200) -> void: + undo_redo = UndoRedo.new() + undo_redo.max_steps = max_history + undo_redo.version_changed.connect(_on_version_changed) + + +func execute(command: Command, merge_mode: UndoRedo.MergeMode = UndoRedo.MERGE_DISABLE) -> void: + var description: String = command.get_description() + undo_redo.create_action(description, merge_mode) + undo_redo.add_do_method(command.execute.bind()) + undo_redo.add_undo_method(command.undo.bind()) + undo_redo.add_do_reference(command) + undo_redo.add_undo_reference(command) + + undo_redo.commit_action() + + command_executed.emit() + history_changed.emit() + + +func undo() -> bool: + if not can_undo(): + return false + + undo_redo.undo() + undone.emit() + history_changed.emit() + return true + + +func redo() -> bool: + if not can_redo(): + return false + + undo_redo.redo() + redone.emit() + history_changed.emit() + return true + + +func can_undo() -> bool: + return undo_redo.has_undo() + + +func can_redo() -> bool: + return undo_redo.has_redo() + + +func get_undo_description() -> String: + if not can_undo(): + return "" + return undo_redo.get_action_name(undo_redo.get_version() - 1) + + +func get_redo_description() -> String: + if not can_redo(): + return "" + return undo_redo.get_action_name(undo_redo.get_version()) + + +func clear() -> void: + undo_redo.clear_history() + history_changed.emit() + + +func get_version() -> int: + return undo_redo.get_version() + + +func get_history_count() -> int: + return undo_redo.get_history_count() + + +func _on_version_changed() -> void: + history_changed.emit() + + +func begin_group(description: String = "Group") -> void: + undo_redo.create_action(description) + + +func add_to_group(command: Command) -> void: + undo_redo.add_do_method(command.execute.bind()) + undo_redo.add_undo_method(command.undo.bind()) + undo_redo.add_do_reference(command) + undo_redo.add_undo_reference(command) + + +func end_group() -> void: + undo_redo.commit_action() + command_executed.emit() + history_changed.emit() diff --git a/common/history/command_manager.gd.uid b/common/history/command_manager.gd.uid new file mode 100644 index 00000000..948b9434 --- /dev/null +++ b/common/history/command_manager.gd.uid @@ -0,0 +1 @@ +uid://dwxg4gbtegie1 diff --git a/common/history/delete_nodes_command.gd b/common/history/delete_nodes_command.gd new file mode 100644 index 00000000..90b5886f --- /dev/null +++ b/common/history/delete_nodes_command.gd @@ -0,0 +1,13 @@ +class_name DeleteNodesCommand extends AddNodesCommand + + +func execute() -> void: + super.undo() + + +func undo() -> void: + super.execute() + + +func get_description() -> String: + return "Delete %s nodes" % nodes.size() diff --git a/common/history/delete_nodes_command.gd.uid b/common/history/delete_nodes_command.gd.uid new file mode 100644 index 00000000..2b244e3a --- /dev/null +++ b/common/history/delete_nodes_command.gd.uid @@ -0,0 +1 @@ +uid://bi0nf2bllnil5 diff --git a/common/history/node_connection_command.gd b/common/history/node_connection_command.gd new file mode 100644 index 00000000..2773b77d --- /dev/null +++ b/common/history/node_connection_command.gd @@ -0,0 +1,98 @@ +class_name NodeConnectionCommand extends Command + +var graph_view: MonologueGraphEdit +var from_node_id: String +var to_node_id: String +var from_property_name: String +var to_property_name: String +var disconnect_node: bool + + +func _init( + n_graph_view: MonologueGraphEdit, + n_from_node_id: String, + n_to_node_id: String, + n_from_property_name: String, + n_to_property_name: String, + n_disconnect: bool = false +) -> void: + graph_view = n_graph_view + from_node_id = n_from_node_id + to_node_id = n_to_node_id + from_property_name = n_from_property_name + to_property_name = n_to_property_name + disconnect_node = n_disconnect + + +func execute() -> void: + # Recalculate port indices in case they changed + var from_port: int = graph_view.get_port_index_for_property(from_node_id, from_property_name, true) + var to_port: int = graph_view.get_port_index_for_property(to_node_id, to_property_name, false) + + if disconnect_node: + if graph_view.connection_manager: + graph_view.connection_manager.unregister_connection( + from_node_id, from_port, to_node_id, to_port + ) + + if from_port >= 0 and to_port >= 0: + graph_view.disconnect_node(from_node_id, from_port, to_node_id, to_port) + else: + if graph_view.connection_manager: + graph_view.connection_manager.register_connection_by_property( + from_node_id, from_property_name, to_node_id, to_property_name + ) + + if from_port >= 0 and to_port >= 0: + graph_view.connect_node(from_node_id, from_port, to_node_id, to_port) + + _notify_node_changes() + + +func undo() -> void: + # Recalculate port indices in case they changed + var from_port: int = graph_view.get_port_index_for_property(from_node_id, from_property_name, true) + var to_port: int = graph_view.get_port_index_for_property(to_node_id, to_property_name, false) + + if not disconnect_node: + if graph_view.connection_manager: + graph_view.connection_manager.unregister_connection( + from_node_id, from_port, to_node_id, to_port + ) + + if from_port >= 0 and to_port >= 0: + graph_view.disconnect_node(from_node_id, from_port, to_node_id, to_port) + else: + if graph_view.connection_manager: + graph_view.connection_manager.register_connection_by_property( + from_node_id, from_property_name, to_node_id, to_property_name + ) + + if from_port >= 0 and to_port >= 0: + graph_view.connect_node(from_node_id, from_port, to_node_id, to_port) + + _notify_node_changes() + + +func _notify_node_changes() -> void: + var storyline: StorylineDocument = ProjectManager.current_project.get_storyline(graph_view.storyline_id) + var from_nodes: Array = storyline.nodes.filter( + func(n: InspectableNode) -> bool: return n.get_property_value("id") == from_node_id + ) + var to_nodes: Array = storyline.nodes.filter( + func(n: InspectableNode) -> bool: return n.get_property_value("id") == to_node_id + ) + + if not from_nodes.is_empty(): + var from_node: InspectableNode = from_nodes[0] + from_node.property_changed.emit(from_node_id) + + if not to_nodes.is_empty(): + var to_node: InspectableNode = to_nodes[0] + to_node.property_changed.emit(to_property_name) + + +func get_description() -> String: + return ( + "Connect %s.%s to %s.%s" % [from_node_id, from_property_name, to_node_id, to_property_name] + ) diff --git a/common/history/node_connection_command.gd.uid b/common/history/node_connection_command.gd.uid new file mode 100644 index 00000000..6e475902 --- /dev/null +++ b/common/history/node_connection_command.gd.uid @@ -0,0 +1 @@ +uid://7kht5ybdplta diff --git a/common/history/node_selection_command.gd b/common/history/node_selection_command.gd new file mode 100644 index 00000000..b51f9394 --- /dev/null +++ b/common/history/node_selection_command.gd @@ -0,0 +1,44 @@ +class_name NodeSelectionCommand extends Command + +var storyline_id: String +var previous_node: InspectableNode +var next_node: InspectableNode +var apply_callable: Callable + + +func _init( + p_storyline_id: String, + p_previous_node: InspectableNode, + p_next_node: InspectableNode, + p_apply_callable: Callable +) -> void: + storyline_id = p_storyline_id + previous_node = p_previous_node + next_node = p_next_node + apply_callable = p_apply_callable + + +func execute() -> void: + _apply_selection(next_node) + + +func undo() -> void: + _apply_selection(previous_node) + + +func get_description() -> String: + var target_id := "" + if next_node: + var id_property := next_node.get_property("id") + if id_property: + target_id = String(id_property.get_value()) + if target_id.is_empty(): + return "Change selection" + return "Select node %s" % target_id + + +func _apply_selection(target: InspectableNode) -> void: + if not apply_callable.is_valid(): + return + + apply_callable.call(target, storyline_id) diff --git a/common/history/node_selection_command.gd.uid b/common/history/node_selection_command.gd.uid new file mode 100644 index 00000000..87551d7f --- /dev/null +++ b/common/history/node_selection_command.gd.uid @@ -0,0 +1 @@ +uid://eghqtautsvc2 diff --git a/common/history/property_command.gd b/common/history/property_command.gd new file mode 100644 index 00000000..e082fc9c --- /dev/null +++ b/common/history/property_command.gd @@ -0,0 +1,28 @@ +class_name PropertyChangeCommand extends Command + +var target: InspectableObject +var property_name: String +var old_value: Variant +var new_value: Variant + + +func _init(p_target: InspectableObject, p_property: String, p_old: Variant, p_new: Variant) -> void: + target = p_target + property_name = p_property + old_value = p_old + new_value = p_new + + +func execute() -> void: + var property: Property = target.get_property(property_name) + property.set_value(new_value) + + +func undo() -> void: + var property: Property = target.get_property(property_name) + property.set_value(old_value) + target.property_changed.emit(property_name) + + +func get_description() -> String: + return "Change value of `%s` property" % property_name diff --git a/common/history/property_command.gd.uid b/common/history/property_command.gd.uid new file mode 100644 index 00000000..57f949eb --- /dev/null +++ b/common/history/property_command.gd.uid @@ -0,0 +1 @@ +uid://dmv35lufxhghw diff --git a/common/history/property_settings_command.gd b/common/history/property_settings_command.gd new file mode 100644 index 00000000..5dba2663 --- /dev/null +++ b/common/history/property_settings_command.gd @@ -0,0 +1,76 @@ +class_name PropertySettingsChangeCommand extends Command + +var target: InspectableObject +var property_name: String +var settings_name: String +var old_value: Variant +var new_value: Variant + + +func _init( + p_target: InspectableObject, + p_property: String, + p_settings_name: String, + p_old: Variant, + p_new: Variant, +) -> void: + target = p_target + property_name = p_property + settings_name = p_settings_name + old_value = p_old + new_value = p_new + + +func execute() -> void: + var property: Property = target.get_property(property_name) + property._overrides[settings_name] = new_value + _handle_connection_visibility(new_value) + target.property_changed.emit(property_name) + property.refresh_bindings() + + +func undo() -> void: + var property: Property = target.get_property(property_name) + if old_value: + property._overrides[settings_name] = old_value + else: + property._overrides.erase(settings_name) + _handle_connection_visibility(old_value) + target.property_changed.emit(property_name) + property.refresh_bindings() + + +func get_description() -> String: + return "Change settings of `%s` property" % property_name + + +func _handle_connection_visibility(setting_value: Variant) -> void: + if settings_name not in ["exposed", "export"]: + return + var node: InspectableNode = _get_target_node() + if not node: + return + var graph_view: GraphNode = node.graph_view + if not graph_view: + return + var graph_edit: MonologueGraphEdit = graph_view.get_parent() + var manager: ConnectionManager = graph_edit.connection_manager + if not manager: + return + var node_name: String = graph_view.name + if settings_name == "exposed": + if setting_value: + manager.restore_incoming_property_connections(node_name, property_name) + return + manager.suspend_incoming_property_connections(node_name, property_name) + else: + if setting_value: + manager.restore_outgoing_property_connections(node_name, property_name) + return + manager.suspend_outgoing_property_connections(node_name, property_name) + + +func _get_target_node() -> InspectableNode: + if target is InspectableNode: + return target + return null diff --git a/common/history/property_settings_command.gd.uid b/common/history/property_settings_command.gd.uid new file mode 100644 index 00000000..4201c437 --- /dev/null +++ b/common/history/property_settings_command.gd.uid @@ -0,0 +1 @@ +uid://dabyel4cj64ff diff --git a/common/inspectable_node.gd b/common/inspectable_node.gd new file mode 100644 index 00000000..8ea06581 --- /dev/null +++ b/common/inspectable_node.gd @@ -0,0 +1,160 @@ +@abstract +class_name InspectableNode extends InspectableObject + +var graph_view: GraphNode +var storyline_id: String = "" +var _main_property_defined: bool = false + + +func _init(command_manager: CommandManager = null) -> void: + define_property( + "color", + "#000000", + "color", + { + "visible_in_graph": false, + "visible_in_inspector": true, + "flat": true, + "expand": false, + }, + "Special:Header" + ) + # Keep all connection references in sync when the id changes + var _id_prop: Property = get_property("id") + if _id_prop and not _id_prop.value_changed.is_connected(_on_id_value_changed): + _id_prop.value_changed.connect(_on_id_value_changed) + super._init(command_manager) + define_property( + "notes", + "", + "textarea", + { + "visible_in_graph": false, + "visible_in_inspector": true, + "exposable": false, + }, + "Extra" + ) + define_property( + "position", + [0.0, 0.0], + "vector2", + { + "visible_in_graph": false, + "visible_in_inspector": false, + "read_only": false, + "exposable": false, + }, + "Extra" + ) + + if not _main_property_defined: + push_error("Main property not defined") + + +func define_main_property( + pname: String, + type: String, + editable: bool = false, + default_value: Variant = null, + psettings: Dictionary = {}, + category: String = "General" +) -> void: + if _main_property_defined: + push_error("Main property already defined") + return + + var default_settings: Dictionary = {} + default_settings["visible_in_graph"] = true + default_settings["visible_in_inspector"] = editable + default_settings["editable"] = editable + default_settings["exposable"] = true + default_settings["exposed"] = true + default_settings["export"] = true + default_settings["is_main_property"] = true + + var merged_settings: Dictionary = default_settings.duplicate() + merged_settings.merge(psettings, true) + + define_property(pname, default_value, type, merged_settings, category) + _main_property_defined = true + + +func define_property( + pname: String, + default_value: Variant, + ptype: String, + psettings: Dictionary = {}, + category: String = "General" +) -> void: + super.define_property(pname, default_value, ptype, psettings, category) + + +func get_id() -> String: + return get_property_value("id") + +func get_main_property() -> Property: + if not _main_property_defined: + return null + + for prop: Property in get_properties(): + if not prop.get_settings_value("is_main_property", false): + continue + return prop + + return null + + +## Returns properties visible in the graph, ordered with main property first. +## Used for port index calculation and graph node display. +func get_visible_properties() -> Array[Property]: + var visible: Array[Property] = [] + for prop: Property in get_properties(): + if not prop.is_visible_in_graph(): + continue + if prop.get_settings_value("is_main_property"): + visible.push_front(prop) + else: + visible.append(prop) + return visible + + +func rebuild_preview() -> void: + if not is_instance_valid(graph_view): + return + var graph_edit: MonologueGraphEdit = graph_view.get_parent() + if graph_edit and graph_edit is MonologueGraphEdit: + graph_edit.refresh_node(self) + + +@abstract func get_type() -> String + + +func _on_id_value_changed(old_value: Variant, new_value: Variant) -> void: + var old_id: String = str(old_value) + var new_id: String = str(new_value) + if old_id == new_id: + return + + # Update all connection references via connection manager + if is_instance_valid(graph_view): + var graph_edit: MonologueGraphEdit = graph_view.get_parent() + if graph_edit and graph_edit is MonologueGraphEdit and graph_edit.connection_manager: + graph_edit.connection_manager.rename_node_id(old_id, new_id) + + # Ensure the GraphNode uses the new id as its name and reconnect + if is_instance_valid(graph_view): + graph_view.name = new_id + var gv_parent: MonologueGraphEdit = graph_view.get_parent() + if gv_parent and gv_parent is MonologueGraphEdit: + gv_parent.clear_connections() + gv_parent._reconnect_all_slots() + + +@warning_ignore("native_method_override") +func duplicate(deep: bool = false) -> Resource: + var duplicated: InspectableNode = super.duplicate(deep) + duplicated.get_property("id").value = IDGen.generate(ID_LENGTH) + duplicated.get_property("position").value += [30, 30] + + return duplicated diff --git a/common/monologue_graph_node.gd.uid b/common/inspectable_node.gd.uid similarity index 100% rename from common/monologue_graph_node.gd.uid rename to common/inspectable_node.gd.uid diff --git a/common/inspectable_object.gd b/common/inspectable_object.gd new file mode 100644 index 00000000..fee1b28e --- /dev/null +++ b/common/inspectable_object.gd @@ -0,0 +1,227 @@ +@abstract +class_name InspectableObject extends Resource + +const ID_LENGTH: int = 4 + +signal property_changed(property_name: String) + +var _properties: Dictionary[String, Property] = {} +var _children: Dictionary[String, Array] = {} # Children object of properties +var history: CommandManager +var settings: Dictionary = {} +var _parent_object: InspectableObject +var _parent_property_name: String + + +func get_parent_object() -> InspectableObject: + return _parent_object + + +func get_parent_property_name() -> String: + return _parent_property_name + + +func _set_parent_info(parent: InspectableObject, pname: String) -> void: + _parent_object = parent + _parent_property_name = pname + + +func _init(command_manager: CommandManager = null) -> void: + if not command_manager: + Log.warn("InspectableObject does not have a command manager.") + history = command_manager + + define_property( + "id", + "%s-%s" % [get_type(), IDGen.generate(ID_LENGTH)], + "text", + { + "visible_in_graph": false, + "visible_in_inspector": true, + "flat": true, + "unique": true, + }, + "Special:Header" + ) + + initialize_properties() + _load_settings() + + +func _load_settings() -> void: + var new_settings: Dictionary = {} + new_settings.merge(get_settings(), true) + settings = new_settings + + +func define_property( + pname: String, + default_value: Variant, + type: String, + psettings: Dictionary = {}, + category: String = "General" +) -> void: + var merged_settings: Dictionary = psettings.duplicate(true) + merged_settings["category"] = category + + var property: Property = Property.new(pname, default_value, type, merged_settings) + _properties.set(pname, property) + + property.value_changed.connect( + func(_old: Variant, _new: Variant) -> void: + property_changed.emit(pname) + ) + + +func get_properties() -> Array[Property]: + var properties: Array[Property] = [] + for pname: String in _properties.keys(): + properties.append(_properties[pname]) + + return properties + + +func get_property(pname: String) -> Property: + return _properties.get(pname) + + +func get_property_value(pname: String) -> Variant: + var property: Property = get_property(pname) + if not property: + return + return property.get_value() + + +func get_property_settings_value(pname: String, skey: String) -> Variant: + var property: Property = get_property(pname) + return property._overrides.get(skey) + + +func set_property_value(pname: String, pvalue: Variant) -> void: + if not _properties.has(pname): + return + + var old_value: Variant = get_property_value(pname) + + if typeof(pvalue) == typeof(old_value) and pvalue == old_value: + return + + var command: PropertyChangeCommand = PropertyChangeCommand.new(self, pname, old_value, pvalue) + history.execute(command) + + +func set_property_settings_value(pname: String, skey: String, svalue: Variant) -> void: + if not _properties.has(pname): + return + + var old_value: Variant = get_property_settings_value(pname, skey) + + var command: PropertySettingsChangeCommand = PropertySettingsChangeCommand.new( + self, pname, skey, old_value, svalue + ) + history.execute(command) + + +func get_property_children(property_name: String) -> Array: + return _children.get(property_name, []) + + +func set_property_children(property_name: String, objects: Array) -> void: + for object: InspectableObject in objects: + if not object.property_changed.is_connected(_on_property_children_property_change): + object.property_changed.connect(_on_property_children_property_change.bind(property_name)) + object._set_parent_info(self, property_name) + + _children[property_name] = objects + + +func add_property_children(property_name: String, object: InspectableObject) -> void: + if not object.property_changed.is_connected(_on_property_children_property_change): + object.property_changed.connect(_on_property_children_property_change.bind(property_name)) + object._set_parent_info(self, property_name) + + _children.get_or_add(property_name, []) + _children.get(property_name).append(object) + + +func remove_property_children(property_name: String, object: InspectableObject) -> void: + if not property_name in _children: + return + if object.property_changed.is_connected(_on_property_children_property_change): + object.property_changed.disconnect(_on_property_children_property_change) + object._set_parent_info(null, "") + _children.get(property_name).erase(object) + + +func move_property_child(property_name: String, object: InspectableObject, to_index: int) -> void: + if not property_name in _children: + return + + var children_array: Array = _children.get(property_name) + var current_index: int = children_array.find(object) + + if current_index == -1: + return + + if to_index < 0: + to_index = 0 + if to_index >= children_array.size(): + to_index = children_array.size() - 1 + + children_array.remove_at(current_index) + children_array.insert(to_index, object) + + +func _on_property_children_property_change(_children_property_name: String, property_name: String) -> void: + var childrens: Array = get_property_children(property_name) + var value: Array = [] + for object: InspectableObject in childrens: + value.append(object._to_dict()) + + set_property_value(property_name, value) + + +func _to_dict() -> Dictionary: + var dict: Dictionary = {"$type": get_type()} + for property: Property in get_properties(): + var property_dict: Dictionary = property._to_dict() + dict[property.name] = property_dict + return dict + + +func _from_dict(dict: Dictionary) -> void: + dict.erase("$type") + for property: Property in get_properties(): + var property_dict: Dictionary = dict.get(property.name, {}) + property._from_dict(property_dict) + + +func get_settings() -> Dictionary: + return {} + + +## Returns external list items for a given list property (e.g. connected OptionNodes). +## Override in subclasses that support externally-sourced list items. +func get_external_list_items(_property_name: String) -> Array[Dictionary]: + return [] + + +func rebuild_preview() -> void: + pass + + +@warning_ignore("native_method_override") +func duplicate(deep: bool = false) -> Resource: + var duplicated: InspectableNode = super.duplicate(deep) + duplicated.history = history + duplicated.settings = settings + + for property: Property in get_properties(): + var d_prop: Property = duplicated.get_property(property.name) + d_prop.value = property.get_value() + + return duplicated + + +@abstract func get_type() -> String +@abstract func initialize_properties() -> void diff --git a/common/inspectable_object.gd.uid b/common/inspectable_object.gd.uid new file mode 100644 index 00000000..d5c2a3e0 --- /dev/null +++ b/common/inspectable_object.gd.uid @@ -0,0 +1 @@ +uid://c7ehitto0jxu3 diff --git a/common/layouts/character_edit/alpha_bg.gd b/common/layouts/character_edit/alpha_bg.gd deleted file mode 100644 index e821bdc5..00000000 --- a/common/layouts/character_edit/alpha_bg.gd +++ /dev/null @@ -1,20 +0,0 @@ -extends ColorRect - -@export var square_scale: float = 1.0 - - -func _ready() -> void: - resized.connect(queue_redraw) - queue_redraw() - - -func _draw() -> void: - var square_size: float = 5.0 * square_scale - - for x in range(0, size.x, square_size): - for y in range(0, size.y, square_size): - var rect_color: Color = Color("c0c0c0") - if (x % 2 and y % 2) or (not (x % 2) and not (y % 2)): - rect_color = Color("808080") - var rect := Rect2(x, y, square_size, square_size) - draw_rect(rect, rect_color) diff --git a/common/layouts/character_edit/alpha_bg.gd.uid b/common/layouts/character_edit/alpha_bg.gd.uid deleted file mode 100644 index e1746454..00000000 --- a/common/layouts/character_edit/alpha_bg.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dqhwi718vnyxk diff --git a/common/layouts/character_edit/assets/triangle_pointer.svg b/common/layouts/character_edit/assets/triangle_pointer.svg deleted file mode 100644 index 1aa082f4..00000000 --- a/common/layouts/character_edit/assets/triangle_pointer.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/common/layouts/character_edit/assets/triangle_pointer.svg.import b/common/layouts/character_edit/assets/triangle_pointer.svg.import deleted file mode 100644 index 5d5ecb14..00000000 --- a/common/layouts/character_edit/assets/triangle_pointer.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://mjb34j4suabp" -path="res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/layouts/character_edit/assets/triangle_pointer.svg" -dest_files=["res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=4.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/layouts/character_edit/character_edit.gd b/common/layouts/character_edit/character_edit.gd deleted file mode 100644 index 87f59a03..00000000 --- a/common/layouts/character_edit/character_edit.gd +++ /dev/null @@ -1,70 +0,0 @@ -class_name CharacterEdit extends CharacterEditSection - -var nicknames := Property.new(MonologueGraphNode.LINE) -var display_name := Property.new(MonologueGraphNode.LINE) -var description := Property.new(MonologueGraphNode.TEXT) - - -func _ready() -> void: - close() - GlobalSignal.add_listener("open_character_edit", open) - GlobalSignal.add_listener("close_character_edit", close) - GlobalSignal.add_listener("reload_character_edit", reload) - super._ready() - - -func open(graph: MonologueGraphEdit, index: int) -> void: - if index >= 0: - graph_edit = graph - character_index = index - %PortraitSettingsSection.base_path = graph.file_path - %PortraitListSection.selected = -1 - _from_dict(graph.characters[index]) - show() - else: - close() - - -func close() -> void: - # also triggered when 'Done' button is pressed - if graph_edit: - graph_edit.characters[character_index]["Character"].merge(_to_dict(), true) - hide() - graph_edit = null - character_index = -1 - - -func reload(index: int) -> void: - # triggered if character edit is opened but character index is different - if character_index != index: - open(graph_edit, index) - - -func _from_dict(dict: Dictionary = {}) -> void: - var character_dict: Dictionary = dict.get("Character", {}) - super._from_dict(character_dict) - _from_recursive(character_dict, linked_sections) - - -func _from_recursive(dict: Dictionary, subsections: Array[CharacterEditSection]) -> void: - for subsection in subsections: - subsection._from_dict(dict) - _from_recursive(dict, subsection.linked_sections) - - -func _to_dict() -> Dictionary: - var flat_map = super._to_dict() - for section in linked_sections: - flat_map.merge(section._to_dict(), true) - return flat_map - - -func _on_visibility_changed() -> void: - if visible: - GlobalSignal.emit("show_dimmer", [self]) - return - GlobalSignal.emit("hide_dimmer", [self]) - - -func _on_timeline_section_visibility_changed() -> void: - pass # Replace with function body. diff --git a/common/layouts/character_edit/character_edit.gd.uid b/common/layouts/character_edit/character_edit.gd.uid deleted file mode 100644 index 742ad742..00000000 --- a/common/layouts/character_edit/character_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ci3lk3sx5wxx7 diff --git a/common/layouts/character_edit/character_edit.tscn b/common/layouts/character_edit/character_edit.tscn deleted file mode 100644 index 1894c18a..00000000 --- a/common/layouts/character_edit/character_edit.tscn +++ /dev/null @@ -1,278 +0,0 @@ -[gd_scene load_steps=12 format=3 uid="uid://c7cf7rfwnhf77"] - -[ext_resource type="Script" uid="uid://ci3lk3sx5wxx7" path="res://common/layouts/character_edit/character_edit.gd" id="1_mo23b"] -[ext_resource type="LabelSettings" uid="uid://cu53lojswyksx" path="res://common/layouts/character_edit/theme/title_label_settings.tres" id="2_50fu3"] -[ext_resource type="Script" uid="uid://ckwej8c2tv20v" path="res://common/layouts/split_container.gd" id="4_r51dr"] -[ext_resource type="Script" uid="uid://0j3efmsb2t3o" path="res://common/layouts/character_edit/portrait_list_section.gd" id="5_mpn8l"] -[ext_resource type="Texture2D" uid="uid://c7ee44ql1gv6b" path="res://ui/assets/icons/offset_cross.svg" id="6_q7cn4"] -[ext_resource type="Script" uid="uid://bhrgrnvpc1gqw" path="res://common/layouts/character_edit/portrait_settings_section.gd" id="7_gu775"] -[ext_resource type="Script" uid="uid://djgxd3whcbthm" path="res://common/layouts/character_edit/timeline_section.gd" id="9_shsbx"] -[ext_resource type="Texture2D" uid="uid://hmjhxdsk3pwj" path="res://ui/assets/icons/trash.svg" id="10_m01ot"] -[ext_resource type="Script" uid="uid://ta4lilswattg" path="res://common/layouts/character_edit/preview_section.gd" id="13_nmxnm"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ng6uw"] -content_margin_left = 10.0 -content_margin_top = 10.0 -content_margin_right = 10.0 -content_margin_bottom = 10.0 -bg_color = Color(0.275, 0.275, 0.302, 0.25) - -[sub_resource type="ImageTexture" id="ImageTexture_qnpab"] - -[node name="CharacterEdit" type="MarginContainer" node_paths=PackedStringArray("field_vbox", "linked_sections")] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -scale = Vector2(1.00019, 1) -theme_override_constants/margin_left = 50 -theme_override_constants/margin_top = 50 -theme_override_constants/margin_right = 50 -theme_override_constants/margin_bottom = 50 -script = ExtResource("1_mo23b") -field_vbox = NodePath("PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/MainSettingsSection/MarginContainer/FieldVBox") -linked_sections = [NodePath("PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/PortraitListSection")] - -[node name="PanelContainer" type="PanelContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] -layout_mode = 2 -theme_override_constants/separation = 20 - -[node name="HSplitContainer" type="HSplitContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/separation = 20 -theme_override_constants/autohide = 0 - -[node name="RightContainer" type="VSplitContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer"] -layout_mode = 2 -theme_override_constants/separation = 20 - -[node name="MainSettingsSection" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(400, 0) -layout_mode = 2 - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/MainSettingsSection"] -layout_mode = 2 -text = "Main Settings" -label_settings = ExtResource("2_50fu3") - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/MainSettingsSection"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="FieldVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/MainSettingsSection/MarginContainer"] -unique_name_in_owner = true -layout_mode = 2 - -[node name="PortraitListSection" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer" node_paths=PackedStringArray("field_vbox", "linked_sections")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -script = ExtResource("5_mpn8l") -field_vbox = NodePath("ScrollContainer/FieldVBox") -linked_sections = [NodePath("../../LeftContainer/HSplitContainer/PortraitSettingsSection"), NodePath("../../LeftContainer/TimelineSection")] - -[node name="SectionLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/PortraitListSection"] -layout_mode = 2 -text = "Portraits" -label_settings = ExtResource("2_50fu3") - -[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/PortraitListSection"] -layout_mode = 2 -size_flags_vertical = 3 -follow_focus = true - -[node name="FieldVBox" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/RightContainer/PortraitListSection/ScrollContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="LeftContainer" type="VSplitContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 20 -split_offset = 400 -script = ExtResource("4_r51dr") - -[node name="HSplitContainer" type="HSplitContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/separation = 20 - -[node name="PortraitSettingsSection" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer" node_paths=PackedStringArray("field_vbox")] -unique_name_in_owner = true -custom_minimum_size = Vector2(350, 0) -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 20 -script = ExtResource("7_gu775") -field_vbox = NodePath("MarginContainer/VBoxContainer/FieldVBox") - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection"] -layout_mode = 2 -text = "Portrait Settings" -label_settings = ExtResource("2_50fu3") - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection"] -layout_mode = 2 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer"] -layout_mode = 2 - -[node name="FieldVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer/VBoxContainer"] -layout_mode = 2 - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer/VBoxContainer"] -layout_mode = 2 -theme_override_constants/margin_left = 0 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer/VBoxContainer/MarginContainer"] -layout_mode = 2 -alignment = 2 - -[node name="Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] -layout_mode = 2 -theme_type_variation = &"ButtonWarning" -text = "Delete this portrait" -icon = ExtResource("10_m01ot") - -[node name="PreviewSection" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 -script = ExtResource("13_nmxnm") - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection"] -layout_mode = 2 -text = "Preview" -label_settings = ExtResource("2_50fu3") - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection"] -custom_minimum_size = Vector2(0, 200) -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="PanelContainer" type="PanelContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer"] -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_ng6uw") - -[node name="PreviewViewport" type="SubViewportContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer"] -layout_mode = 2 -stretch = true - -[node name="Viewport" type="SubViewport" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/PreviewViewport"] -disable_3d = true -transparent_bg = true -handle_input_locally = false -size = Vector2i(2, 2) -size_2d_override_stretch = true -render_target_update_mode = 4 - -[node name="2DScene" type="Node2D" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/PreviewViewport/Viewport"] -scale = Vector2(0.5, 0.5) - -[node name="ViewportCamera" type="Camera2D" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/PreviewViewport/Viewport/2DScene"] -unique_name_in_owner = true - -[node name="ViewportSprite" type="Sprite2D" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/PreviewViewport/Viewport/2DScene"] -unique_name_in_owner = true -texture = SubResource("ImageTexture_qnpab") - -[node name="ASContainer" type="Node2D" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/PreviewViewport/Viewport/2DScene"] -unique_name_in_owner = true - -[node name="CenterContainer" type="CenterContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer"] -layout_mode = 2 - -[node name="TextureRect2" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/CenterContainer"] -custom_minimum_size = Vector2(36, 36) -layout_mode = 2 -texture = ExtResource("6_q7cn4") -expand_mode = 1 -stretch_mode = 5 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer"] -layout_mode = 2 -alignment = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/VBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -text = "Zoom" - -[node name="HSlider" type="HSlider" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -min_value = 0.1 -max_value = 5.0 -step = 0.01 -value = 1.0 -exp_edit = true - -[node name="TimelineSection" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer" node_paths=PackedStringArray("field_vbox")] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -script = ExtResource("9_shsbx") -field_vbox = NodePath("TimelineContainer/FieldVBox") - -[node name="SectionLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/TimelineSection"] -layout_mode = 2 -text = "Timeline" -label_settings = ExtResource("2_50fu3") - -[node name="TimelineContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/TimelineSection"] -custom_minimum_size = Vector2(0, 250) -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="FieldVBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/TimelineSection/TimelineContainer"] -layout_mode = 2 - -[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -alignment = 1 - -[node name="Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2"] -layout_mode = 2 -text = "Done" - -[connection signal="pressed" from="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button" to="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PortraitSettingsSection" method="_on_delete_button_pressed"] -[connection signal="value_changed" from="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection/MarginContainer/PanelContainer/VBoxContainer/HBoxContainer/HSlider" to="PanelContainer/MarginContainer/VBoxContainer/HSplitContainer/LeftContainer/HSplitContainer/PreviewSection" method="_on_h_slider_value_changed"] -[connection signal="pressed" from="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer2/Button" to="." method="close"] diff --git a/common/layouts/character_edit/character_edit_section.gd b/common/layouts/character_edit/character_edit_section.gd deleted file mode 100644 index 9d06c5c2..00000000 --- a/common/layouts/character_edit/character_edit_section.gd +++ /dev/null @@ -1,78 +0,0 @@ -class_name CharacterEditSection extends Control - -## Character index that is being edited in the graph_edit. -@export var character_index: int = -1: - set = set_index -## Container which will house all the fields for this section. -@export var field_vbox: Control -## The graph_edit storing the character. -@export var graph_edit: MonologueGraphEdit: - set = set_graph_edit -## List of other sections that are linked to this section. -@export var linked_sections: Array[CharacterEditSection] - - -func _ready() -> void: - for entry in get_property_list(): - if Constants.PROPERTY_CLASSES.has(entry.class_name): - get(entry.name).connect("change", change.bind(entry.name)) - - -func change(old_value: Variant, new_value: Variant, property: String) -> void: - var changes: Array[PropertyChange] = [] - changes.append(PropertyChange.new(property, old_value, new_value)) - - var undo_redo = graph_edit.undo_redo - undo_redo.create_action("%s: %s => %s" % [property, old_value, new_value]) - var history = CharacterHistory.new( - character_index, graph_edit, graph_edit.get_path_to(self), changes - ) - undo_redo.add_prepared_history(history) - undo_redo.commit_action() - - -## Deletes all field controls in the field_vbox. -func flush() -> void: - for field in field_vbox.get_children(): - field.queue_free() - - -func set_graph_edit(graph: MonologueGraphEdit) -> void: - graph_edit = graph - for section in linked_sections: - section.graph_edit = graph - - -func set_index(index: int) -> void: - character_index = index - for section in linked_sections: - section.character_index = index - - -func _from_dict(dict: Dictionary) -> void: - flush() - for entry in get_property_list(): - if Constants.PROPERTY_CLASSES.has(entry.class_name): - var key = Util.to_key_name(entry.name) - var property = get(entry.name) - var is_value = property is Property - var is_raw = property is Localizable - if property and (is_value or is_raw): - if is_value: - property.value = dict.get(key, property.default_value) - elif is_raw: - property.raw_data = dict.get(key, {}) - var label = key.capitalize() - property.callers["set_label_text"] = [label] - property.show(field_vbox) - - -func _to_dict() -> Dictionary: - var dict: Dictionary = {} - for property in get_property_list(): - if Constants.PROPERTY_CLASSES.has(property.class_name): - var reference = get(property.name) - var is_raw = reference is Localizable - var value = reference.to_raw_value() if is_raw else reference.value - dict[Util.to_key_name(property.name)] = value - return dict diff --git a/common/layouts/character_edit/character_edit_section.gd.uid b/common/layouts/character_edit/character_edit_section.gd.uid deleted file mode 100644 index 97c6016d..00000000 --- a/common/layouts/character_edit/character_edit_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d1iy50esfv1od diff --git a/common/layouts/character_edit/image_picker_line_edit.gd.uid b/common/layouts/character_edit/image_picker_line_edit.gd.uid deleted file mode 100644 index bd4a8f0a..00000000 --- a/common/layouts/character_edit/image_picker_line_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cobx5ud0kmkmu diff --git a/common/layouts/character_edit/portrait_edit_section.gd b/common/layouts/character_edit/portrait_edit_section.gd deleted file mode 100644 index 834f8643..00000000 --- a/common/layouts/character_edit/portrait_edit_section.gd +++ /dev/null @@ -1,17 +0,0 @@ -class_name PortraitEditSection extends CharacterEditSection - -## Selected portrait index. -@export var portrait_index: int = -1 - - -func change(old_value: Variant, new_value: Variant, property: String) -> void: - var changes: Array[PropertyChange] = [] - changes.append(PropertyChange.new(property, old_value, new_value)) - - var undo_redo = graph_edit.undo_redo - undo_redo.create_action("%s: %s => %s" % [property, old_value, new_value]) - var history = PortraitHistory.new( - character_index, portrait_index, graph_edit, graph_edit.get_path_to(self), changes - ) - undo_redo.add_prepared_history(history) - undo_redo.commit_action() diff --git a/common/layouts/character_edit/portrait_edit_section.gd.uid b/common/layouts/character_edit/portrait_edit_section.gd.uid deleted file mode 100644 index 5d050773..00000000 --- a/common/layouts/character_edit/portrait_edit_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://btswkabtfw08o diff --git a/common/layouts/character_edit/portrait_list_section.gd b/common/layouts/character_edit/portrait_list_section.gd deleted file mode 100644 index 4974edb3..00000000 --- a/common/layouts/character_edit/portrait_list_section.gd +++ /dev/null @@ -1,171 +0,0 @@ -class_name PortraitListSection extends CharacterEditSection - -signal portrait_selected - -const DEFAULT_PORTRAIT_NAME = "new portrait %s" - -var portraits := Property.new(MonologueGraphNode.LIST, {}, []) -var default_portrait := Property.new(MonologueGraphNode.LINE, {}, "") - -var selected: int = -1 -var references: Array[AbstractPortraitOption] = [] - -@onready var portrait_settings_section := %PortraitSettingsSection -@onready var timeline_section := %TimelineSection -@onready var preview_section := %PreviewSection -@onready var scroll_container := $ScrollContainer - - -func _ready() -> void: - GlobalSignal.add_listener("select_portrait_option", select_option) - portraits.setters["add_callback"] = add_portrait - portraits.setters["get_callback"] = get_portraits - portraits.setters["flat"] = true - portraits.setters["expand"] = true - portraits.connect("preview", load_portraits) - portraits.connect("change", _on_portraits_change) - default_portrait.visible = false - super._ready() - - -func add_portrait(option_dict: Dictionary = {}) -> AbstractPortraitOption: - _sync_references() - var new_portrait := AbstractPortraitOption.new(self) - new_portrait.graph = graph_edit - if option_dict: - new_portrait._from_dict(option_dict) - else: - new_portrait.portrait_name.value = DEFAULT_PORTRAIT_NAME % (references.size() + 1) - new_portrait.portrait.callers["set_option_name"] = [new_portrait.portrait_name.value] - new_portrait.idx.value = references.size() - new_portrait.portrait.connecters[_on_portrait_option_pressed] = "pressed" - new_portrait.portrait.connecters[_on_portrait_option_set_to_default] = "set_to_default" - new_portrait.portrait.connecters[_on_portrait_option_name_submitted] = "name_submitted" - references.append(new_portrait) - - if new_portrait.idx.value == selected: - new_portrait.portrait.callers["set_active"] = [] - else: - new_portrait.portrait.callers["release_active"] = [] - return new_portrait - - -func get_portraits() -> Array: - return references - - -func _on_portraits_change(old_value: Variant, new_value: Variant) -> void: - if new_value.size() <= 0: - select_option(-1) - elif new_value.size() < old_value.size(): - select_option(0) - elif new_value.size() > old_value.size() and false: - select_option(references.size()) - _update_portrait() - - -func get_portrait_options() -> Array: - return get_portraits().map(func(i: AbstractPortraitOption): return i.portrait.field) - - -## Perform loading of speakers and set indexes correctly. -func load_portraits(new_portrait_list: Array) -> void: - references.clear() - var ascending = func(a, b): return a.get("EditorIndex") < b.get("EditorIndex") - new_portrait_list.sort_custom(ascending) - - for portrait_data in new_portrait_list: - var abstract_option = add_portrait(portrait_data) - if new_portrait_list.size() <= 1: - default_portrait.value = abstract_option.id.value - if default_portrait.value == abstract_option.id.value: - abstract_option.portrait.callers["set_default"] = [] - - portraits.value = new_portrait_list - _update_portrait() - - -## Selects portrait option by index. -func select_option(index: int) -> void: - if selected == index: - return - - selected = index - if index < 0: - return - - var all_options := get_portrait_options() - if index < all_options.size(): - _update_option(all_options[index]) - - -func _from_dict(dict: Dictionary) -> void: - super._from_dict(dict) - # custom handling because the default_portrait property is a little special - default_portrait.value = dict.get("DefaultPortrait", "") - load_portraits(dict.get("Portraits", [])) - portraits.propagate(portraits.value) - _update_portrait() - - -func _on_portrait_option_pressed(portrait_option: PortraitOption) -> void: - var all_options := get_portrait_options() - selected = all_options.find(portrait_option) - _update_option(portrait_option) - - -func _on_portrait_option_set_to_default(portrait_option: PortraitOption) -> void: - var all_options := get_portrait_options() - for option: PortraitOption in all_options: - if option == portrait_option: - var index = all_options.find(option) - default_portrait.save_value(references[index].id.value) - else: - option.release_default() - - -func _on_portrait_option_name_submitted(portrait_option: PortraitOption) -> void: - var all_options := get_portrait_options() - for option: PortraitOption in all_options: - if option == portrait_option: - var index = all_options.find(option) - references[index].portrait_name.save_value(portrait_option.line_edit.text) - _sync_references() - - -func _update_option(selected_option: PortraitOption) -> void: - for option: PortraitOption in get_portrait_options(): - if option == selected_option: - option.set_active() - for section in linked_sections: - section.portrait_index = selected - else: - option.release_active() - portrait_selected.emit() - _update_portrait() - - await get_tree().process_frame - scroll_container.ensure_control_visible(selected_option) - - -func _sync_references() -> void: - var data_list: Array = portraits.value - for ref: AbstractPortraitOption in references: - var ref_candidates: Array = data_list.filter( - func(p: Dictionary): return p.get("EditorIndex", -1) == ref.idx.value - ) - if ref_candidates.size() <= 0: - continue - - var data: Dictionary = ref_candidates[0] - ref._from_dict(data) - - -func _update_portrait() -> void: - var show_portrait_sections: bool = selected >= 0 - portrait_settings_section.visible = show_portrait_sections - preview_section.visible = show_portrait_sections - timeline_section.visible = show_portrait_sections - if show_portrait_sections: - var character_dict = graph_edit.characters[character_index]["Character"] - portrait_settings_section._from_dict(character_dict) diff --git a/common/layouts/character_edit/portrait_list_section.gd.uid b/common/layouts/character_edit/portrait_list_section.gd.uid deleted file mode 100644 index eee333fc..00000000 --- a/common/layouts/character_edit/portrait_list_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://0j3efmsb2t3o diff --git a/common/layouts/character_edit/portrait_section_container.gd.uid b/common/layouts/character_edit/portrait_section_container.gd.uid deleted file mode 100644 index 4a892f2d..00000000 --- a/common/layouts/character_edit/portrait_section_container.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://o62twtfhdgvw diff --git a/common/layouts/character_edit/portrait_settings_section.gd b/common/layouts/character_edit/portrait_settings_section.gd deleted file mode 100644 index 075278cb..00000000 --- a/common/layouts/character_edit/portrait_settings_section.gd +++ /dev/null @@ -1,116 +0,0 @@ -class_name PortraitSettingsSection extends PortraitEditSection - -@warning_ignore("unused_signal") -signal changed - -var portrait_type := Property.new(MonologueGraphNode.DROPDOWN, {}, "Image", "PortraitType") -var image_path := Property.new( - MonologueGraphNode.FILE, {"filters": FilePicker.IMAGE}, "", "ImagePath" -) -var offset := Property.new(MonologueGraphNode.VECTOR, {}, [0, 0], "Offset") -var mirror := Property.new(MonologueGraphNode.TOGGLE, {}, false, "Mirror") -var one_shot := Property.new(MonologueGraphNode.TOGGLE, {}, false, "OneShot") - -@onready var preview_section := %PreviewSection -@onready var timeline_section: TimelineSection = %TimelineSection - -var id: String -var base_path: String: - set = _set_base_path - -var _control_groups = { - "Image": [portrait_type, image_path, offset, mirror], - "Animation": [portrait_type, offset, mirror, one_shot], -} - - -func _ready() -> void: - portrait_type.callers["set_items"] = [ - [ - {"id": 0, "text": "Image"}, - {"id": 1, "text": "Animation"}, - ] - ] - portrait_type.change.connect(_on_portrait_type_change) - portrait_type.connect("preview", _show_group) - image_path.change.connect(_on_image_path_change) - offset.change.connect(_on_offset_change) - mirror.change.connect(_on_mirror_change) - super._ready() - - -func _set_base_path(val: String) -> void: - base_path = val - if not image_path.field: - image_path.setters["base_path"] = val - else: - image_path.field.base_path = val - - -func _from_dict(dict: Dictionary = {}) -> void: - var portrait_list: Array = dict.get("Portraits", []) - if ( - portrait_index >= 0 - and portrait_index < portrait_list.size() - and portrait_index == %PortraitListSection.selected - ): - var portrait_dict: Dictionary = portrait_list[portrait_index]["Portrait"] - super._from_dict(portrait_dict) - timeline_section._from_dict.bind(portrait_dict).call_deferred() - timeline_section.portrait_index = portrait_index - timeline_section.character_index = character_index - timeline_section.base_path = base_path - _on_portrait_type_change.call_deferred() - - -func _to_dict() -> Dictionary: - return super._to_dict() - - -func _on_check_button_toggled(toggled_on: bool) -> void: - preview_section.update_mirror(toggled_on) - - -func _on_portrait_type_change(_old_value: Variant = null, _new_value: Variant = null) -> void: - var _process_type_change = func(): - if visible: - timeline_section.visible = portrait_type.value == "Animation" - if portrait_type.value == "Animation": - preview_section.update_animation([]) - else: - _on_image_path_change(null, image_path.value) - _show_group() - - _process_type_change.call_deferred() - - -func _on_image_path_change(_old_value: Variant = null, new_value: Variant = null) -> void: - if not new_value or not image_path.field: - return - - var is_valid: bool = image_path.field.validate(image_path.value) - if is_valid: - var abs_image_path: String = Path.relative_to_absolute(new_value, base_path) - var texture: ImageTexture = ImageLoader.load_image(abs_image_path) - preview_section.update_preview(texture) - return - preview_section.update_preview(ImageTexture.new()) - - -func _on_offset_change(_old_value: Variant = null, new_value: Variant = null) -> void: - preview_section.update_offset(new_value) - - -func _on_mirror_change(_old_value: Variant = null, new_value: Variant = null) -> void: - preview_section.update_mirror(new_value) - - -func _show_group(prt_type: Variant = portrait_type.value) -> void: - var group = _control_groups.get(prt_type) - for key in _control_groups.keys(): - for property: Property in _control_groups.get(key): - property.set_visible(group.has(property)) - - -func _on_delete_button_pressed() -> void: - %PortraitListSection.references[portrait_index].custom_delete_button.pressed.emit() diff --git a/common/layouts/character_edit/portrait_settings_section.gd.uid b/common/layouts/character_edit/portrait_settings_section.gd.uid deleted file mode 100644 index eed06e32..00000000 --- a/common/layouts/character_edit/portrait_settings_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhrgrnvpc1gqw diff --git a/common/layouts/character_edit/preview_section.gd b/common/layouts/character_edit/preview_section.gd deleted file mode 100644 index 1d8f5411..00000000 --- a/common/layouts/character_edit/preview_section.gd +++ /dev/null @@ -1,62 +0,0 @@ -extends VBoxContainer - -@onready var preview_texture: Sprite2D = %ViewportSprite -@onready var preview_anim: Node2D = %ASContainer -@onready var preview_camera: Camera2D = %ViewportCamera -@onready -var zoom_slider: HSlider = $MarginContainer/PanelContainer/VBoxContainer/HBoxContainer/HSlider - - -func update_preview(texture: Texture2D = Texture2D.new()) -> void: - if preview_texture: - preview_texture.texture = texture - - preview_anim.hide() - preview_texture.show() - - -func update_animation(sprites: Array) -> void: - _clear_anim() - for sprite in sprites: - var animated_sprite := AnimatedSprite2D.new() - animated_sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST - animated_sprite.sprite_frames = sprite - preview_anim.add_child(animated_sprite) - - preview_texture.hide() - preview_anim.show() - - -func _clear_anim() -> void: - for child in preview_anim.get_children(): - child.queue_free() - - -func update_offset(offset: Array) -> void: - preview_camera.offset.x = -offset[0] - preview_camera.offset.y = -offset[1] - - -func update_mirror(mirror: bool) -> void: - preview_texture.flip_h = mirror - for child_anim in preview_anim.get_children(): - child_anim.flip_h = mirror - - -func _on_h_slider_value_changed(value: float) -> void: - preview_camera.zoom = Vector2(value, value) - - -func play_backwards() -> void: - for animated_sprite: AnimatedSprite2D in preview_anim.get_children(): - animated_sprite.play_backwards("default") - - -func stop() -> void: - for animated_sprite: AnimatedSprite2D in preview_anim.get_children(): - animated_sprite.stop() - - -func play() -> void: - for animated_sprite: AnimatedSprite2D in preview_anim.get_children(): - animated_sprite.play("default") diff --git a/common/layouts/character_edit/preview_section.gd.uid b/common/layouts/character_edit/preview_section.gd.uid deleted file mode 100644 index bc299de5..00000000 --- a/common/layouts/character_edit/preview_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ta4lilswattg diff --git a/common/layouts/character_edit/theme/property_label_settings.tres b/common/layouts/character_edit/theme/property_label_settings.tres deleted file mode 100644 index 417db014..00000000 --- a/common/layouts/character_edit/theme/property_label_settings.tres +++ /dev/null @@ -1,4 +0,0 @@ -[gd_resource type="LabelSettings" format=3 uid="uid://bbx22ewomxes8"] - -[resource] -font_color = Color(1, 1, 1, 0.5) diff --git a/common/layouts/character_edit/theme/title_label_settings.tres b/common/layouts/character_edit/theme/title_label_settings.tres deleted file mode 100644 index 82492365..00000000 --- a/common/layouts/character_edit/theme/title_label_settings.tres +++ /dev/null @@ -1,5 +0,0 @@ -[gd_resource type="LabelSettings" format=3 uid="uid://cu53lojswyksx"] - -[resource] -line_spacing = 0.0 -font_size = 22 diff --git a/common/layouts/character_edit/timeline_section.gd b/common/layouts/character_edit/timeline_section.gd deleted file mode 100644 index bde6dede..00000000 --- a/common/layouts/character_edit/timeline_section.gd +++ /dev/null @@ -1,28 +0,0 @@ -class_name TimelineSection extends PortraitEditSection - -@warning_ignore("unused_signal") -signal changed - -var animation := Property.new(MonologueGraphNode.TIMELINE, {}, {}) - -var id: String -var base_path: String: - set = _set_base_path - - -func _ready() -> void: - super._ready() - animation.setters["preview_section"] = %PreviewSection - - -func _set_base_path(val: String) -> void: - base_path = val - animation.setters["base_path"] = val - - -func _from_dict(dict: Dictionary = {}) -> void: - super._from_dict(dict) - - -func _to_dict() -> Dictionary: - return super._to_dict() diff --git a/common/layouts/character_edit/timeline_section.gd.uid b/common/layouts/character_edit/timeline_section.gd.uid deleted file mode 100644 index 6ab51567..00000000 --- a/common/layouts/character_edit/timeline_section.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://djgxd3whcbthm diff --git a/common/layouts/dimmer/dimmer.gd b/common/layouts/dimmer/dimmer.gd deleted file mode 100644 index 017f196c..00000000 --- a/common/layouts/dimmer/dimmer.gd +++ /dev/null @@ -1,20 +0,0 @@ -extends ColorRect - -var request_count: int = 0 - - -func _ready() -> void: - hide() - GlobalSignal.add_listener("show_dimmer", _on_show_dimmer) - GlobalSignal.add_listener("hide_dimmer", _on_hide_dimmer) - - -func _on_show_dimmer(_focus_node: Node = null) -> void: - request_count = max(1, request_count + 1) - show() - - -func _on_hide_dimmer(_focus_node: Node = null) -> void: - request_count = max(0, request_count - 1) - if request_count == 0: - hide() diff --git a/common/layouts/dimmer/dimmer.gd.uid b/common/layouts/dimmer/dimmer.gd.uid deleted file mode 100644 index e8cd3a03..00000000 --- a/common/layouts/dimmer/dimmer.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d0xgy6gflipxs diff --git a/common/layouts/expanded_text_edit/expanded_text_edit_container.gd b/common/layouts/expanded_text_edit/expanded_text_edit_container.gd deleted file mode 100644 index 3bdfe18e..00000000 --- a/common/layouts/expanded_text_edit/expanded_text_edit_container.gd +++ /dev/null @@ -1,33 +0,0 @@ -extends MarginContainer - -@onready var text_edit: TextEdit = %TextEdit -var little_text_edit: TextEdit - - -func _ready() -> void: - hide() - GlobalSignal.add_listener("expand_text_edit", _on_expand_text_edit) - - -func _on_expand_text_edit(little_te: TextEdit) -> void: - little_text_edit = little_te - text_edit.text = little_text_edit.text - - show() - - -func _on_button_pressed() -> void: - hide() - little_text_edit.focus_exited.emit() - - -func _on_text_edit_text_changed() -> void: - little_text_edit.text = text_edit.text - little_text_edit.text_changed.emit() - - -func _on_visibility_changed() -> void: - if visible: - GlobalSignal.emit("show_dimmer", [self]) - return - GlobalSignal.emit("hide_dimmer", [self]) diff --git a/common/layouts/graph_edit/monologue_graph_edit.gd b/common/layouts/graph_edit/monologue_graph_edit.gd deleted file mode 100644 index 2a24a655..00000000 --- a/common/layouts/graph_edit/monologue_graph_edit.gd +++ /dev/null @@ -1,422 +0,0 @@ -## Represents the graph area which creates and connects MonologueGraphNodes. -class_name MonologueGraphEdit extends GraphEdit - -var close_button_scene = preload("res://common/ui/buttons/close_button.tscn") -var base_options = {} -var data: Dictionary -var file_path: String -var undo_redo := HistoryHandler.new() -var version = undo_redo.get_version() - -var languages = [] -var characters = [] -var variables = [] - -var active_graphnode: MonologueGraphNode # for tab-switching purpose -var connecting_mode: bool -var current_language_index: int -var moving_mode: bool -var recorded_positions: Dictionary = {} # for undo/redo positoning purpose -var selected_nodes: Array[MonologueGraphNode] = [] # for group delete -var mouse_hovering: bool = false - - -func _ready() -> void: - var auto_arrange_button = get_menu_hbox().get_children().back() - auto_arrange_button.connect("pressed", _on_auto_arrange_nodes) - - center_offset.call_deferred() - - # Hide scroll bar - for child in get_children(true): - if child is GraphNode: - continue - - for subchild in child.get_children(true): - if subchild is not ScrollBar: - continue - - for sb_name in ["grabber", "scroll"]: - subchild.add_theme_stylebox_override(sb_name, StyleBoxEmpty.new()) - - -func _on_add_btn() -> void: - GlobalSignal.emit("select_new_node") - - -func center_offset(): - var base_offset = Vector2.ZERO - var root_node: RootNode = get_root_node() - if root_node: - base_offset = root_node.position_offset + (root_node.size / 2) * zoom - - scroll_offset = -size / 2 + base_offset - - -func _input(event: InputEvent) -> void: - moving_mode = ( - Input.is_action_pressed("Select") - and event is InputEventMouseMotion - and not selected_nodes.is_empty() - ) - - -func _gui_input(_event: InputEvent) -> void: - if not mouse_hovering: - return - - var cursor_drag: bool = false - var cursor_hand_closed: bool = false - - if Input.is_action_pressed("Spacebar"): - cursor_drag = true - if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): - cursor_hand_closed = true - - if Input.is_mouse_button_pressed(MOUSE_BUTTON_MIDDLE): - cursor_hand_closed = true - - if cursor_hand_closed: - DisplayServer.cursor_set_custom_image(Cursor.closed_hand) - elif cursor_drag: - DisplayServer.cursor_set_custom_image(Cursor.hand) - else: - DisplayServer.cursor_set_custom_image(Cursor.arrow) - - -## Adds a node of the given type to this graph. -func add_node( - node_type, record: bool = true, picker: GraphNodePicker = null -) -> Array[MonologueGraphNode]: - # if adding from picker, track existing to_nodes of the picker_from_node - var picker_to_names = [] - if picker: - for picker_to_node in get_all_connections_from_slot(picker.from_node, picker.from_port): - picker_to_names.append(picker_to_node.name) - - var node_scene = Constants.NODE_SCENES.get(node_type) - var new_node = node_scene.instantiate() - - # created_nodes include auxilliary nodes from new_node, such as BridgeOut - var created_nodes = new_node.add_to(self) - - # if enabled, track the addition of created_nodes into the graph history - if record: - var addition = AddNodeHistory.new(self, created_nodes) - if not picker_to_names.is_empty(): - addition.picker_from_node = picker.from_node - addition.picker_from_port = picker.from_port - addition.picker_to_names = picker_to_names - - undo_redo.create_action("Add new %s" % [new_node.node_type]) - undo_redo.add_prepared_history(addition) - undo_redo.commit_action(false) - - return created_nodes - - -func clear(): - for node in get_nodes(): - node.queue_free() - clear_connections() - - -## Disconnect all outbound connections of the given graphnode and port. -func disconnect_outbound_from_node(from_node: StringName, from_port: int) -> void: - for connection in get_connection_list(): - if connection.get("from_node") == from_node: - var to_node = connection.get("to_node") - var to_port = connection.get("to_port") - disconnect_node(from_node, from_port, to_node, to_port) - - -## Deletes the given graphnode and return its dictionary data. -func free_graphnode(node: MonologueGraphNode) -> Dictionary: - var inbound_connections = get_all_inbound_connections(node.name) - var outbound_connections = get_all_outbound_connections(node.name) - for c in inbound_connections + outbound_connections: - disconnect_node(c.get("from_node"), c.get("from_port"), c.get("to_node"), c.get("to_port")) - - var node_data = node._to_dict() - if "options" in node: - # tag options into the node_data - node_data.merge({"Options": node.options.value}) - if active_graphnode == node: - active_graphnode = null - - selected_nodes.erase(node) - recorded_positions.erase(node) - node.queue_free() - # if side panel is showing this node, close it since it's gone - GlobalSignal.emit("close_panel", [node]) - return node_data - - -## Find all other connections that connect to the given graphnode. -func get_all_inbound_connections(from_node: StringName) -> Array: - var node_connections = [] - for connection in get_connection_list(): - if connection.get("to_node") == from_node: - node_connections.append(connection) - return node_connections - - -## Find all connections that originate from the given graphnode. -func get_all_outbound_connections(from_node: StringName) -> Array: - var node_connections = [] - for connection in get_connection_list(): - if connection.get("from_node") == from_node: - node_connections.append(connection) - return node_connections - - -## Find connections of the given [param from_node] at its [param from_port]. -func get_all_connections_from_slot(from_node: StringName, from_port: int) -> Array: - var node_connections = [] - for connection in get_connection_list(): - if connection.get("from_node") == from_node and connection.get("from_port") == from_port: - var to = get_node_or_null(NodePath(connection.get("to_node"))) - node_connections.append(to) - return node_connections - - -func get_free_bridge_number(_n = 1, lp_max = 50) -> int: - for node in get_nodes(): - if ( - (node.node_type == "NodeBridgeOut" or node.node_type == "NodeBridgeIn") - and node.number_selector.value == _n - ): - if lp_max <= 0: - return _n - - return get_free_bridge_number(_n + 1, lp_max - 1) - return _n - - -func get_linked_bridge_node(target_number) -> MonologueGraphNode: - for node in get_nodes(): - if node.node_type == "NodeBridgeOut" and node.number_selector.value == target_number: - return node - return null - - -func get_root_node() -> RootNode: - for node in get_nodes(): - if node is RootNode: - return node - return null - - -## Find a graph node by ID. Includes OptionNodes. -func get_node_by_id(id: String) -> MonologueGraphNode: - if not id.is_empty(): - for node in get_nodes(): - if node.id.value == id: - return node - elif node is ChoiceNode: - var option = node.get_option_by_id(id) - if option: - return option - return null - - -func is_unsaved() -> bool: - return version != undo_redo.get_version() - - -## Connect picker_from_node to [param node] if needed, reposition nodes. -func pick_and_center( - nodes: Array[MonologueGraphNode], picker: GraphNodePicker -) -> PackedStringArray: - var to_names = [] - var offset = (scroll_offset + size/2) / zoom # center of graph - - if picker.from_node and picker.from_port != -1: - if nodes[0].get_input_port_count() > 0: - var from_node = picker.from_node - var from_port = picker.from_port - disconnect_outbound_from_node(from_node, from_port) - propagate_connection(from_node, from_port, nodes[0].name, 0) - if picker.graph_release: - offset = (picker.release + scroll_offset) / zoom - - picker.flush() - - for node in nodes: - node.position_offset = offset - - post_node_offset.call_deferred(nodes) - return to_names - - -func post_node_offset(nodes: Array[MonologueGraphNode]) -> void: - for node in nodes: - node.position_offset -= node.size/2 - - if not nodes[0].is_slot_enabled_left(0): - return - - var first_port_pos = nodes[0].get_input_port_position(0) - for node in nodes: - node.position_offset -= first_port_pos - node.position_offset = round(node.position_offset / snapping_distance) * snapping_distance - - -## Connects/disconnects and updates a given connection's NextID if possible. -## If [param next] is true, establish connection and propagate NextIDs. -## If it is false, destroy connection and clear all linked NextIDs. -func propagate_connection(from_node, from_port, to_node, to_port, next = true) -> void: - if next: - connect_node(from_node, from_port, to_node, to_port) - - else: - disconnect_node(from_node, from_port, to_node, to_port) - - var graph_node = get_node_or_null(NodePath(from_node)) - if graph_node and graph_node.has_method("update_next_id"): - if next: - var next_node = get_node_or_null(NodePath(to_node)) - graph_node.update_next_id(from_port, next_node) - else: - graph_node.update_next_id(from_port, null) - - -func trigger_delete(): - if not active_graphnode and selected_nodes: - var root_filter = func(n): return n is not RootNode - var selected_copy = selected_nodes.duplicate().filter(root_filter) - var delete_history = DeleteNodeHistory.new(self, selected_copy) - undo_redo.create_action("Delete %s" % str(selected_copy)) - undo_redo.add_prepared_history(delete_history) - undo_redo.commit_action() - - -## Checks and ensure graph is ready before triggering undo. -func trigger_undo() -> void: - if not connecting_mode: - undo_redo.undo() - - -## Checks and ensure graph is ready before triggering redo. -func trigger_redo() -> void: - if not connecting_mode: - undo_redo.redo() - - -func update_node_positions() -> void: - var affected_nodes = selected_nodes if selected_nodes else get_nodes() - for node in affected_nodes: - recorded_positions[node] = node.position_offset - - -func update_version() -> void: - version = undo_redo.get_version() - - -func _on_auto_arrange_nodes() -> void: - var affected = selected_nodes if selected_nodes else get_nodes() - var changed = affected.filter(func(n): return n.position_offset != recorded_positions[n]) - if changed and affected.size() > 1: - undo_redo.create_action("Auto arrange nodes") - for node in changed: - undo_redo.add_do_property(node, "position_offset", node.position_offset) - undo_redo.add_undo_property(node, "position_offset", recorded_positions[node]) - undo_redo.commit_action(false) - update_node_positions() - - -func _on_child_entered_tree(node: Node) -> void: - if node is MonologueGraphNode: - if node is RootNode: - return - - if not node.show_close_button: - return - - var node_header = node.get_children(true)[0] - var close_button: TextureButton = close_button_scene.instantiate() - - var close_callback = func(): - var delete_history = DeleteNodeHistory.new(self, [node]) - var message = "Delete %s (id: %s)" - undo_redo.create_action(message % [node.node_type, node.id.value]) - undo_redo.add_prepared_history(delete_history) - undo_redo.commit_action(false) - selected_nodes.erase(node) - recorded_positions.erase(node) - free_graphnode(node) - - close_button.connect("pressed", close_callback) - node_header.add_child(close_button) - - -func _on_connection_drag_started(_from_node, _from_port, _is_output) -> void: - connecting_mode = true - - -func _on_connection_drag_ended() -> void: - connecting_mode = false - - -func _on_connection_request(from_node, from_port, to_node, to_port) -> void: - # so check to make sure there are no other connections before connecting - if get_all_connections_from_slot(from_node, from_port).size() <= 0: - var arguments = [from_node, from_port, to_node, to_port] - var message = "Connect %s port %d to %s port %d" - undo_redo.create_action(message % arguments) - undo_redo.add_do_method(propagate_connection.bindv(arguments)) - undo_redo.add_undo_method(propagate_connection.bindv(arguments + [false])) - undo_redo.commit_action() - - -func _on_disconnection_request(from_node, from_port, to_node, to_port) -> void: - var arguments = [from_node, from_port, to_node, to_port] - var message = "Disconnect %s from %s port %d" - undo_redo.create_action(message % [to_node, from_node, from_port]) - undo_redo.add_do_method(propagate_connection.bindv(arguments + [false])) - undo_redo.add_undo_method(propagate_connection.bindv(arguments)) - undo_redo.commit_action() - - -func _on_connection_to_empty(node: String, port: int, release: Vector2) -> void: - var center = (get_local_mouse_position() + scroll_offset) / zoom - var graph_release = (release + scroll_offset) / zoom - GlobalSignal.emit("enable_picker_mode", [node, port, release, graph_release, center]) - - -func _on_gui_input(event: InputEvent) -> void: - # when the user clicks on the graph edit, close unnecessary stuff - if ( - event is InputEventMouseButton - and event.is_pressed() - and event.button_index == MOUSE_BUTTON_LEFT - ): - GlobalSignal.emit("show_languages", [false]) - - -func _on_node_selected(node) -> void: - if node is MonologueGraphNode: - selected_nodes.append(node) - - -func _on_node_deselected(node) -> void: - recorded_positions[node] = node.position_offset - selected_nodes.erase(node) - active_graphnode = null # when a deselection happens, clear active node - - -func get_nodes() -> Array[MonologueGraphNode]: - var list: Array[MonologueGraphNode] = [] - for node in get_children(): - if node is MonologueGraphNode: - list.append(node) - return list - - -func _on_mouse_entered() -> void: - mouse_hovering = true - - -func _on_mouse_exited() -> void: - DisplayServer.cursor_set_custom_image(null) - mouse_hovering = false diff --git a/common/layouts/graph_edit/monologue_graph_edit.tscn b/common/layouts/graph_edit/monologue_graph_edit.tscn deleted file mode 100644 index 6cb08519..00000000 --- a/common/layouts/graph_edit/monologue_graph_edit.tscn +++ /dev/null @@ -1,38 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://qdgl8co6qy6"] - -[ext_resource type="Script" uid="uid://bvrbvq6oihg7d" path="res://common/layouts/graph_edit/monologue_graph_edit.gd" id="1_ivjsb"] - -[node name="GraphEdit" type="GraphEdit"] -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 -grid_pattern = 1 -snapping_enabled = false -snapping_distance = 30 -right_disconnects = true -zoom = 0.6 -zoom_min = 0.1 -zoom_max = 1.2 -minimap_enabled = false -show_menu = false -show_zoom_buttons = false -show_grid_buttons = false -show_minimap_button = false -show_arrange_button = false -script = ExtResource("1_ivjsb") - -[connection signal="child_entered_tree" from="." to="." method="_on_child_entered_tree"] -[connection signal="connection_drag_ended" from="." to="." method="_on_connection_drag_ended"] -[connection signal="connection_drag_started" from="." to="." method="_on_connection_drag_started"] -[connection signal="connection_request" from="." to="." method="_on_connection_request"] -[connection signal="connection_to_empty" from="." to="." method="_on_connection_to_empty"] -[connection signal="disconnection_request" from="." to="." method="_on_disconnection_request"] -[connection signal="gui_input" from="." to="." method="_on_gui_input"] -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] -[connection signal="node_deselected" from="." to="." method="_on_node_deselected"] -[connection signal="node_selected" from="." to="." method="_on_node_selected"] diff --git a/common/layouts/language_switcher/language_switcher.gd b/common/layouts/language_switcher/language_switcher.gd deleted file mode 100644 index 12ec91f5..00000000 --- a/common/layouts/language_switcher/language_switcher.gd +++ /dev/null @@ -1,152 +0,0 @@ -class_name LanguageSwitcher extends Button - -const DEFAULT_LOCALE = "English" - -## Current graph edit which has the loaded languages. -@export var graph_edit: MonologueGraphEdit -## Currently selected index from the dropdown of languages. -@export var selected_index: int - -var arrow_left := preload("res://ui/assets/icons/arrow_left.svg") -var arrow_down := preload("res://ui/assets/icons/arrow_down.svg") -var option_scene := preload("res://common/layouts/language_switcher/language_option.tscn") - -@onready var dropdown_container: Control = $Dropdown -@onready var vbox: VBoxContainer = $Dropdown/PanelContainer/VBox/Scroll/Options - - -func _ready() -> void: - dropdown_container.hide() - GlobalVariables.language_switcher = self - GlobalSignal.add_listener("load_languages", load_languages) - GlobalSignal.add_listener("show_languages", show_dropdown) - GlobalSignal.add_listener("enable_language_switcher", set_enabled) - GlobalSignal.add_listener("disable_language_switcher", set_enabled.bind(false)) - load_languages() - - -func add_language(locale_name: String = "") -> LanguageOption: - var new_option: LanguageOption = option_scene.instantiate() - vbox.add_child(new_option, true) - new_option.language_name = locale_name - new_option.language_name_changed.connect(_on_option_rename) - new_option.language_removed.connect(_on_option_removed) - new_option.pressed.connect(_on_option_selected.bind(new_option)) - return new_option - - -func get_current_language() -> LanguageOption: - return vbox.get_child(selected_index) - - -## Returns language option node names as Dictionary, where key = language name. -func get_languages() -> Dictionary: - var option_dictionary = {} - for option in vbox.get_children(): - if is_instance_valid(option) and not option.is_queued_for_deletion(): - option_dictionary[str(option)] = option.name - return option_dictionary - - -## Returns the language option that has the given node name. -## The node name is separate from language_name because it allows the user -## to rename the language without changing the reference for undo/redo. -func get_by_node_name(node_name: String) -> LanguageOption: - return vbox.get_node_or_null(node_name) - - -func load_languages(list: PackedStringArray = [], graph: MonologueGraphEdit = null) -> void: - graph_edit = graph - for child in vbox.get_children(): - remove_language(child) - - if graph: - selected_index = graph.current_language_index - selected_index = 0 if selected_index >= list.size() else selected_index - - if list.is_empty(): - list.append(DEFAULT_LOCALE) - - var already_added: PackedStringArray = [] - for i in range(list.size()): - if not already_added.has(list[i]): - var new_option = add_language(list[i]) - if i == 0: - new_option.show_delete_button(false) - already_added.append(list[i]) - _on_option_selected(vbox.get_child(selected_index)) - - -func remove_language(option_node) -> void: - vbox.remove_child(option_node) - option_node.queue_free() - - -func select_by_locale(locale: String, refresh: bool = true) -> void: - var selected_option: LanguageOption - for child in vbox.get_children(): - if child.language_name == locale: - selected_option = child - break - if selected_option: - _on_option_selected(selected_option, refresh) - - -func set_enabled(active: bool = true) -> void: - if not active: - show_dropdown(false) - disabled = true - focus_mode = FOCUS_NONE - else: - disabled = false - focus_mode = FOCUS_ALL - - -func show_dropdown(can_see: bool = true) -> void: - dropdown_container.visible = can_see - icon = arrow_down if can_see else arrow_left - - if !can_see: - release_focus() - - -func _on_option_removed(option: LanguageOption) -> void: - var act_text = [option.language_name, graph_edit.file_path] - graph_edit.undo_redo.create_action("Delete %s language from %s" % act_text) - var deletion = DeleteLanguageHistory.new(graph_edit, option.language_name, option.name) - GlobalSignal.emit("language_deleted", [option.name, deletion.restoration, deletion.choices]) - graph_edit.undo_redo.add_prepared_history(deletion) - graph_edit.undo_redo.commit_action() - - -func _on_option_rename(old: String, new: String, option: LanguageOption) -> void: - graph_edit.undo_redo.create_action("Change %s language to %s" % [old, new]) - var change = ModifyLanguageHistory.new(graph_edit, option.name, option.language_name, new) - graph_edit.undo_redo.add_prepared_history(change) - graph_edit.undo_redo.commit_action() - - var selected_option: LanguageOption = vbox.get_children()[selected_index] - _on_option_selected(selected_option, false) - - -func _on_option_selected(option: LanguageOption, refresh: bool = true) -> void: - for opt: LanguageOption in vbox.get_children(): - opt.unselect() - option.select() - - selected_index = option.get_index() - if graph_edit: - graph_edit.current_language_index = selected_index - text = option.language_name - if refresh: - GlobalSignal.emit("refresh") # update all localizable values - - -func _on_pressed() -> void: - show_dropdown(!dropdown_container.visible) - - -func _on_btn_add_pressed() -> void: - graph_edit.undo_redo.create_action("Add language to %s" % graph_edit.file_path) - graph_edit.undo_redo.add_prepared_history(AddLanguageHistory.new(graph_edit)) - graph_edit.undo_redo.commit_action() diff --git a/common/layouts/language_switcher/language_switcher.tscn b/common/layouts/language_switcher/language_switcher.tscn deleted file mode 100644 index cc85123d..00000000 --- a/common/layouts/language_switcher/language_switcher.tscn +++ /dev/null @@ -1,66 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://cb3se7h7akt47"] - -[ext_resource type="Texture2D" uid="uid://c0cmik715jqff" path="res://ui/assets/icons/arrow_left.svg" id="1_cmhp2"] -[ext_resource type="Script" uid="uid://bd5c0hwweht6a" path="res://common/layouts/language_switcher/language_switcher.gd" id="2_mi5fl"] -[ext_resource type="PackedScene" uid="uid://x84tcb0t06mo" path="res://common/layouts/language_switcher/language_option.tscn" id="3_unc6p"] -[ext_resource type="Texture2D" uid="uid://hlck6y4i3l5q" path="res://ui/assets/icons/plus.svg" id="4_1p58k"] - -[node name="LanguageSwitcher" type="Button"] -custom_minimum_size = Vector2(200, 0) -offset_right = 200.0 -offset_bottom = 34.0 -text = "English" -icon = ExtResource("1_cmhp2") -alignment = 0 -icon_alignment = 2 -script = ExtResource("2_mi5fl") - -[node name="Dropdown" type="Control" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 - -[node name="PanelContainer" type="PanelContainer" parent="Dropdown"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -10.0 -offset_top = -220.0 -offset_right = 10.0 -offset_bottom = -60.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_type_variation = &"OuterPanel" - -[node name="VBox" type="VBoxContainer" parent="Dropdown/PanelContainer"] -layout_mode = 2 - -[node name="Scroll" type="ScrollContainer" parent="Dropdown/PanelContainer/VBox"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="Options" type="VBoxContainer" parent="Dropdown/PanelContainer/VBox/Scroll"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_type_variation = &"VBoxContainer_Medium" -alignment = 2 - -[node name="LanguageOption" parent="Dropdown/PanelContainer/VBox/Scroll/Options" instance=ExtResource("3_unc6p")] -layout_mode = 2 -script = null - -[node name="btnAdd" type="Button" parent="Dropdown/PanelContainer/VBox"] -custom_minimum_size = Vector2(0, 35) -layout_mode = 2 -theme_type_variation = &"FlatButton" -icon = ExtResource("4_1p58k") -icon_alignment = 1 -expand_icon = true - -[connection signal="pressed" from="." to="." method="_on_pressed"] -[connection signal="pressed" from="Dropdown/PanelContainer/VBox/btnAdd" to="." method="_on_btn_add_pressed"] diff --git a/common/layouts/search_bar/search_bar.gd b/common/layouts/search_bar/search_bar.gd deleted file mode 100644 index 62ff5b2e..00000000 --- a/common/layouts/search_bar/search_bar.gd +++ /dev/null @@ -1,27 +0,0 @@ -extends PanelContainer - -const SCROLL_CONTAINER_MAX_SIZE: int = 200 - -@export var graph_edit_switcher: GraphEditSwitcher - -@onready var line_edit: LineEdit = %LineEdit - - -func focus() -> void: - line_edit.grab_focus() - line_edit.select_all() - - _on_h_box_resized() - - -func _on_line_edit_text_changed(new_text: String) -> void: - var graph_edit: MonologueGraphEdit = graph_edit_switcher.current - var all_nodes: Array = graph_edit.get_nodes() - - for node: MonologueGraphNode in all_nodes: - if node.node_type.containsn(new_text): - continue - - -func _on_h_box_resized() -> void: - %ScrollContainer.custom_minimum_size.y = min(%HintVBox.size.y, SCROLL_CONTAINER_MAX_SIZE) diff --git a/common/layouts/search_bar/search_bar.gd.uid b/common/layouts/search_bar/search_bar.gd.uid deleted file mode 100644 index be5e8543..00000000 --- a/common/layouts/search_bar/search_bar.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhh7i0hnd3psj diff --git a/common/layouts/search_bar/search_bar.tscn b/common/layouts/search_bar/search_bar.tscn deleted file mode 100644 index d34904a8..00000000 --- a/common/layouts/search_bar/search_bar.tscn +++ /dev/null @@ -1,102 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://cvum3eaenloix"] - -[ext_resource type="Script" uid="uid://bhh7i0hnd3psj" path="res://common/layouts/search_bar/search_bar.gd" id="1_k0a4y"] -[ext_resource type="Texture2D" uid="uid://7cgdpf2fr6p6" path="res://ui/assets/icons/search.svg" id="2_ejrx2"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_iwyw6"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5dmbh"] - -[node name="SearchBar" type="PanelContainer"] -custom_minimum_size = Vector2(500, 0) -theme_type_variation = &"OuterPanel" -script = ExtResource("1_k0a4y") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 10 - -[node name="TextureRect" type="TextureRect" parent="VBoxContainer/HBoxContainer"] -custom_minimum_size = Vector2(20, 0) -layout_mode = 2 -texture = ExtResource("2_ejrx2") -expand_mode = 1 -stretch_mode = 5 - -[node name="LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(350, 0) -layout_mode = 2 -theme_override_styles/focus = SubResource("StyleBoxEmpty_iwyw6") -theme_override_styles/normal = SubResource("StyleBoxEmpty_5dmbh") -placeholder_text = "Start typing..." -flat = true - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] -layout_mode = 2 -theme_type_variation = &"HSeparatorGrow" - -[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(0, 200) -layout_mode = 2 - -[node name="HintVBox" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="Button" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Node ID" -alignment = 0 - -[node name="Button2" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Node type" -alignment = 0 - -[node name="Button3" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Contain" -alignment = 0 - -[node name="HSeparator" type="HSeparator" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -theme_type_variation = &"HDottedSeparator" - -[node name="Button4" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Add node" -alignment = 0 - -[node name="HSeparator2" type="HSeparator" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -theme_type_variation = &"HDottedSeparator" - -[node name="Button5" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Settings" -alignment = 0 - -[node name="Button7" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Character" -alignment = 0 - -[node name="Button6" type="Button" parent="VBoxContainer/ScrollContainer/HintVBox"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Variable" -alignment = 0 - -[connection signal="text_changed" from="VBoxContainer/HBoxContainer/LineEdit" to="." method="_on_line_edit_text_changed"] diff --git a/common/layouts/side_panel/side_panel.gd b/common/layouts/side_panel/side_panel.gd deleted file mode 100644 index 7e07ab0c..00000000 --- a/common/layouts/side_panel/side_panel.gd +++ /dev/null @@ -1,200 +0,0 @@ -## Side panel which displays graph node details. This panel should not contain -## references to MonologueControl or GraphEditSwitcher. -class_name SidePanel extends PanelContainer - -@onready var fields_container = %Fields -@onready var topbox = %TopBox -@onready var ribbon_scene = preload("res://common/ui/ribbon/ribbon.tscn") -@onready -var collapsible_field = preload("res://common/ui/fields/collapsible_field/collapsible_field.tscn") - -var collapsibles: Dictionary[String, CollapsibleField] -var id_field_container: Control -var selected_node: MonologueGraphNode - - -func _ready(): - GlobalSignal.add_listener("close_panel", _on_close_button_pressed) - hide() - - -func clear(): - for field in fields_container.get_children(): - field.free() - if is_instance_valid(id_field_container): - id_field_container.queue_free() - collapsibles.clear() - - -func on_graph_node_deselected(_node): - hide.call_deferred() - - -func on_graph_node_selected(node: MonologueGraphNode, bypass: bool = false): - if not bypass: - var graph_edit = node.get_parent() - await get_tree().create_timer(0.1).timeout - if ( - is_instance_valid(node) - and not graph_edit.moving_mode - and graph_edit.selected_nodes.size() == 1 - ): - graph_edit.active_graphnode = node - else: - graph_edit.active_graphnode = null - return - - # hack to preserve focus if the side panel contains the same node paths - var focus_owner = get_viewport().gui_get_focus_owner() - var refocus_path: NodePath = "" - var refocus_line: int = -1 - var refocus_column: int = -1 - if focus_owner: - refocus_path = get_path_to(focus_owner) - if focus_owner is TextEdit: - refocus_line = focus_owner.get_caret_line() - refocus_column = focus_owner.get_caret_column() - elif focus_owner is LineEdit: - refocus_column = focus_owner.get_caret_column() - var uncollapse_paths: Array[NodePath] = [] - if node == selected_node: - for collapsible: CollapsibleField in collapsibles.values(): - if collapsible.is_open(): - uncollapse_paths.append(get_path_to(collapsible)) - - clear() - selected_node = node - node._update() - - if not node.is_editable(): - return - - var items = node._get_field_groups() - var already_invoke := [] - var property_names = node.get_property_names() - - for item in items: - _load_groups(item, node, already_invoke) - - for property_name in property_names: - if property_name in already_invoke: - continue - - if property_name == "id": - var field = node.get(property_name) - field.show(topbox, 0, false) - id_field_container = field.field_container - else: - var field = node.get(property_name).show(fields_container) - field.set_label_text(property_name.capitalize()) - - show() - restore_collapsible_state(uncollapse_paths) - # if focus was preserved, restore it - restore_focus(refocus_path, refocus_line, refocus_column) - - -func _load_groups(item, graph_node: MonologueGraphNode, already_invoke) -> void: - if item is String: - var property = graph_node.get(item) - var field = property.show(fields_container) - - if property.custom_label != null: - field.set_label_text(property.custom_label) - else: - field.set_label_text(item.capitalize()) - - already_invoke.append(item) - else: - for group in item: - _recursive_build_collapsible_field( - fields_container, item, group, graph_node, already_invoke - ) - - -func _recursive_build_collapsible_field( - parent: Control, - item: Dictionary, - group: String, - graph_node: MonologueGraphNode, - already_invoke: Array -) -> CollapsibleField: - var fields = item[group] - var field_obj: CollapsibleField = collapsible_field.instantiate() - var field_margin = MarginContainer.new() - field_margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL - field_margin.add_theme_constant_override("margin_right", 0) - field_margin.add_theme_constant_override("margin_bottom", 0) - field_margin.add_child(field_obj) - if parent is CollapsibleField: - parent.add_item(field_margin) - else: - parent.add_child(field_margin) - field_obj.set_title(group) - - for field_name in fields: - if field_name is Dictionary: - for sub_group in field_name: - _recursive_build_collapsible_field( - field_obj, field_name, sub_group, graph_node, already_invoke - ) - continue - - var property: Property = graph_node.get(field_name) - var field = property.show(fields_container) - var field_container = property.field_container - - if property.custom_label != null: - field.set_label_text(property.custom_label) - else: - field.set_label_text(field_name.capitalize()) - - fields_container.remove_child(field_container) - field_obj.add_item(field_container) - already_invoke.append(field_name) - - field.collapsible_field = field_obj - if property.uncollapse: - field_obj.open() - property.uncollapse = false - return field_obj - - -## If the side panel for the node is visible, release the focus so that -## text controls trigger the focus_exited() signal to update. -func refocus(node: MonologueGraphNode) -> void: - if visible and selected_node == node: - var focus_owner = get_viewport().gui_get_focus_owner() - if focus_owner: - focus_owner.release_focus() - focus_owner.grab_focus() - - -## If any collapsible fields were opened before the side panel was refreshed, -## this method will reopen them via their node paths. -func restore_collapsible_state(uncollapse_paths: Array[NodePath]) -> void: - for path in uncollapse_paths: - var field = get_node_or_null(path) - if is_instance_valid(field) and field is CollapsibleField: - field.open() - - -## Hacky improvement for #52 to maintain focus on side panel refresh. -func restore_focus(node_path: NodePath, line: int, column: int) -> void: - if node_path: - var node = get_node_or_null(node_path) - if is_instance_valid(node) and node is Control: - node.grab_focus() - if line >= 0: - node.set_caret_line(line) - if column >= 0: - node.set_caret_column(column) - - -func _on_rfh_button_pressed() -> void: - GlobalSignal.emit("test_trigger", [selected_node.id.value]) - - -func _on_close_button_pressed(node: MonologueGraphNode = null) -> void: - if not node or selected_node == node: - selected_node.get_parent().set_selected(null) diff --git a/common/layouts/side_panel/side_panel.tscn b/common/layouts/side_panel/side_panel.tscn deleted file mode 100644 index b39ff815..00000000 --- a/common/layouts/side_panel/side_panel.tscn +++ /dev/null @@ -1,52 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://dgvhvxdrd58qp"] - -[ext_resource type="Script" uid="uid://dtf4ge38njewp" path="res://common/layouts/side_panel/side_panel.gd" id="1_haagr"] -[ext_resource type="Texture2D" uid="uid://b272tbdmvxj20" path="res://ui/assets/icons/play.svg" id="2_34x8o"] - -[node name="SidePanel" type="PanelContainer"] -offset_right = 158.0 -offset_bottom = 121.0 -theme_type_variation = &"EditorSidePanel" -script = ExtResource("1_haagr") - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="PanelContainer" type="PanelContainer" parent="VBox"] -z_index = 1 -layout_mode = 2 -theme_type_variation = &"EditorSidePanelTopBox" - -[node name="VBox" type="VBoxContainer" parent="VBox/PanelContainer"] -layout_mode = 2 - -[node name="TopBox" type="HBoxContainer" parent="VBox/PanelContainer/VBox"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="RFHButton" type="Button" parent="VBox/PanelContainer/VBox/TopBox"] -custom_minimum_size = Vector2(34, 29) -layout_mode = 2 -icon = ExtResource("2_34x8o") -icon_alignment = 1 -expand_icon = true - -[node name="HSeparator" type="HSeparator" parent="VBox/PanelContainer/VBox"] -layout_mode = 2 -theme_type_variation = &"HSeparatorGrow" - -[node name="Scroller" type="ScrollContainer" parent="VBox"] -clip_contents = false -layout_mode = 2 -size_flags_vertical = 3 - -[node name="Fields" type="VBoxContainer" parent="VBox/Scroller"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[connection signal="pressed" from="VBox/PanelContainer/VBox/TopBox/RFHButton" to="." method="_on_rfh_button_pressed"] diff --git a/common/monologue_graph_node.gd b/common/monologue_graph_node.gd deleted file mode 100644 index 57381a2c..00000000 --- a/common/monologue_graph_node.gd +++ /dev/null @@ -1,227 +0,0 @@ -## Abstract graph node class for Monologue dialogue nodes. This should not -## be used on its own, it should be overridden to replace [member node_type]. -class_name MonologueGraphNode extends GraphNode - -static var subclasses = [] - -@export_group("Appearance") -@export var titlebar_color: Color = Color("ff0000") -@export var show_close_button: bool = true -@export var show_titlebar: bool = true - -# field UI scene definitions that a graph node can have -const CHECKBOX = preload("res://common/ui/fields/check_box/monologue_check_box.tscn") -const DROPDOWN = preload("res://common/ui/fields/dropdown/monologue_dropdown.tscn") -const FILE = preload("res://common/ui/fields/file_picker/monologue_file_picker.tscn") -const LINE = preload("res://common/ui/fields/line_edit/monologue_line_edit.tscn") -const LIST = preload("res://common/ui/fields/list/monologue_list.tscn") -const SLIDER = preload("res://common/ui/fields/slider/monologue_slider.tscn") -const SPINBOX = preload("res://common/ui/fields/spin_box/monologue_spin_box.tscn") -const TIMELINE = preload("res://common/ui/fields/timeline/monologue_timeline.tscn") -const TEXT = preload("res://common/ui/fields/text/monologue_text.tscn") -const TOGGLE = preload("res://common/ui/fields/toggle/monologue_toggle.tscn") -const VECTOR = preload("res://common/ui/fields/vector/monologue_vector.tscn") - -const LEFT_SLOT = preload("res://ui/assets/icons/slot.svg") -const RIGHT_SLOT = preload("res://ui/assets/icons/slot.svg") - -var id := Property.new(LINE, {}, IDGen.generate()) -var editor_position := Property.new(VECTOR, {}, [0.0, 0.0]) -var node_type: String = "NodeUnknown" - - -func _ready() -> void: - set_anchors_preset(Control.PRESET_TOP_LEFT) - if show_titlebar: - _set_titlebar_color(titlebar_color) - - title = node_type - id.setters["copyable"] = true - id.setters["validator"] = _validate_id - id.callers["set_label_visible"] = [false] - editor_position.display.connect(_on_editor_position_change) - editor_position.visible = false - for property_name in get_property_names(): - get(property_name).connect("change", change.bind(property_name)) - get(property_name).connect("display", display) - - _update_slot_icons() - _harmonize_size.call_deferred() - - dragged.connect(_on_dragged) - - -func _on_dragged(_from: Vector2 = Vector2.ZERO, _to: Vector2 = Vector2.ZERO) -> void: - var new_editor_position: Array = [position_offset.x, position_offset.y] - editor_position.save_value(new_editor_position) - - -func _on_editor_position_change() -> void: - await get_tree().process_frame - position_offset.x = editor_position.value[0] - position_offset.y = editor_position.value[1] - - -func _update_slot_icons() -> void: - for slot_idx in get_child_count(): - if is_slot_enabled_left(slot_idx): - set_slot_custom_icon_left(slot_idx, LEFT_SLOT) - if is_slot_enabled_right(slot_idx): - set_slot_custom_icon_right(slot_idx, RIGHT_SLOT) - - -func _harmonize_size() -> void: - var min_size: Vector2 = get_combined_minimum_size() - size.x = ceil(min_size.x / 30) * 30 - size.y = min_size.y - - -func _set_titlebar_color(val: Color): - var is_dark = val.get_luminance() < 0.5 - var stylebox: StyleBoxFlat = get_theme_stylebox("titlebar", "GraphNode").duplicate() - stylebox.bg_color = val - stylebox.corner_radius_top_left = 5 - stylebox.corner_radius_top_right = 5 - - stylebox.border_color = Color("4d4d4d") - stylebox.set_border_width_all(1) - stylebox.border_width_bottom = 0 - - var stylebox_selected = get_theme_stylebox("titlebar_selected", "GraphNode").duplicate() - stylebox_selected.bg_color = val - - remove_theme_stylebox_override("titlebar") - remove_theme_stylebox_override("titlebar_selected") - add_theme_stylebox_override("titlebar", stylebox) - add_theme_stylebox_override("titlebar_selected", stylebox_selected) - - var titlebar: HBoxContainer = get_titlebar_hbox() - var title_label: Label = titlebar.get_children().filter(func(c) -> bool: return c is Label)[0] - title_label.add_theme_color_override("font_color", Color.WHITE if is_dark else Color.BLACK) - - -func add_to(graph: MonologueGraphEdit) -> Array[MonologueGraphNode]: - graph.add_child(self, true) - var all_ids := graph.get_nodes().map(func(n) -> String: return n.id.value) - id.setters["value"] = IDGen.generate(10, all_ids) - return [self] - - -## Commits a given property's value into undo/redo history. -## Order of parameters is important due to how bind() works. -func change(old_value: Variant, new_value: Variant, property: String) -> void: - var changes: Array[PropertyChange] = [] - changes.append(PropertyChange.new(property, old_value, new_value)) - - var graph = get_graph_edit() - var undo_redo = graph.undo_redo - undo_redo.create_action("%s: %s => %s" % [property, old_value, new_value]) - var history = PropertyHistory.new(graph, graph.get_path_to(self), changes) - undo_redo.add_prepared_history(history) - undo_redo.commit_action() - - -func display() -> void: - get_graph_edit().set_selected(self) - - -func get_graph_edit() -> MonologueGraphEdit: - return get_parent() - - -func get_property_names() -> PackedStringArray: - var names = PackedStringArray() - for property in get_property_list(): - if Constants.PROPERTY_CLASSES.has(property.class_name): - names.append(property.name) - return names - - -func is_editable() -> bool: - var ignorable := ["id"] - - for property in get_property_names(): - if property in ignorable: - continue - return true - return false - - -## Reload the preview text of the graph node, if any. -func reload_preview() -> void: - pass - - -func _from_dict(dict: Dictionary) -> void: - var editor_pos = dict.get("EditorPosition") - if editor_pos is Dictionary: - editor_pos = [editor_pos.get("x", 0), editor_pos.get("y", 0)] - dict["EditorPosition"] = editor_pos - - for key in dict.keys(): - var property = get(key.to_snake_case()) - if property is Property: - property.value = dict.get(key) - var private_property = get("_" + key.to_snake_case()) - if private_property is Property: - private_property.value = dict.get(key) - - _load_position(dict) - _update() # refresh node UI after loading properties - - -func _load_connections(data: Dictionary, key: String = "NextID") -> void: - var next_id = data.get(key) - if next_id is String: - var next_node = get_graph_edit().get_node_by_id(next_id) - if next_node: - get_graph_edit().connect_node(name, 0, next_node.name, 0) - - -func _load_position(data: Dictionary) -> void: - var editor_pos = data.get("EditorPosition") - if editor_pos and editor_pos is Dictionary: # Backward compatibility - position_offset.x = editor_pos.get("x", randi_range(-400, 400)) - position_offset.y = editor_pos.get("y", randi_range(-200, 200)) - elif editor_position and editor_pos is Array: - position_offset.x = editor_pos[0] - position_offset.y = editor_pos[1] - - -func _to_dict() -> Dictionary: - var base_dict = {"$type": node_type, "ID": id.value, "EditorPosition": editor_position.value} - _to_next(base_dict) - _to_fields(base_dict) - - #base_dict["EditorPosition"] = { - #"x": int(position_offset.x), - #"y": int(position_offset.y) - #} - return base_dict - - -func _to_fields(dict: Dictionary) -> void: - for property_name in get_property_names(): - var property = get(property_name) - var is_raw = property is Localizable - if property.visible: - var value = property.to_raw_value() if is_raw else property.value - dict[Util.to_key_name(property_name)] = value - - -func _to_next(dict: Dictionary, key: String = "NextID") -> void: - var next_id_node = get_graph_edit().get_all_connections_from_slot(name, 0) - dict[key] = next_id_node[0].id.value if next_id_node else -1 - - -func _update() -> void: - size.y = 0 - _harmonize_size() - - -func _validate_id(text: String) -> bool: - return get_graph_edit().get_node_by_id(text) == null - - -func _get_field_groups() -> Array: - return [] diff --git a/common/monologue_indexer.gd b/common/monologue_indexer.gd new file mode 100644 index 00000000..d0032b09 --- /dev/null +++ b/common/monologue_indexer.gd @@ -0,0 +1,8 @@ +@abstract class_name MonologueIndexer + +enum ObjectType { NODE, FIELD, COLLECTION } + +@abstract func get_scene() -> PackedScene + +# {"name": "", "type": ""} +@abstract func get_metadata() -> Dictionary diff --git a/common/monologue_indexer.gd.uid b/common/monologue_indexer.gd.uid new file mode 100644 index 00000000..40702262 --- /dev/null +++ b/common/monologue_indexer.gd.uid @@ -0,0 +1 @@ +uid://dc5qyu0n5oaqb diff --git a/common/name_generator.gd b/common/name_generator.gd new file mode 100644 index 00000000..0ef52fe7 --- /dev/null +++ b/common/name_generator.gd @@ -0,0 +1,29492 @@ +class_name NameGenerator + + +static func generate() -> String: + var first_name: String = [_female_first_names, _female_first_names].pick_random().pick_random() + var last_name: String = _last_names.pick_random() + + return "%s %s" % [first_name, last_name] + + +# Taken from https://cerol.itch.io/godot-name-generator-class +const _female_first_names: Array[String] = [ + "Aaliyah", + "Aarti", + "Abbe", + "Abbey", + "Abbie", + "Abby", + "Abena", + "Abigail", + "Abiola", + "Ada", + "Adalgisa", + "Adanna", + "Addie", + "Addy", + "Adela", + "Adelaida", + "Adelaide", + "Adele", + "Adelina", + "Adeline", + "Adena", + "Adeola", + "Adina", + "Aditi", + "Adjoa", + "Adria", + "Adriana", + "Adriane", + "Adrianna", + "Adrianne", + "Adrienne", + "Adwoa", + "Ady", + "Afiya", + "Afton", + "Aga", + "Agata", + "Agatha", + "Agathe", + "Aggie", + "Agi", + "Agnese", + "Agnieszka", + "Ahuva", + "Ai", + "Aicha", + "Aida", + "Aiesha", + "Aiko", + "Aileen", + "Aimee", + "Aisha", + "Aisling", + "Aislinn", + "Aixa", + "Aiysha", + "Aja", + "Akane", + "Akemi", + "Aki", + "Akia", + "Akiko", + "Akilah", + "Akosua", + "Akua", + "Alaina", + "Alana", + "Alane", + "Alanna", + "Alba", + "Albana", + "Albania", + "Albee", + "Alberta", + "Albina", + "Alea", + "Alecia", + "Aleida", + "Alejandra", + "Aleksandra", + "Alena", + "Alesha", + "Alesia", + "Alessandra", + "Alessia", + "Aleta", + "Alethea", + "Alexa", + "Alexandra", + "Alexandrea", + "Alexandria", + "Alexia", + "Alexis", + "Alexus", + "Alia", + "Alice", + "Alicia", + "Alicja", + "Alida", + "Alina", + "Aline", + "Alisa", + "Alise", + "Alisha", + "Alisia", + "Alison", + "Alissa", + "Alisson", + "Aliya", + "Aliyah", + "Aliza", + "Alka", + "Alla", + "Allana", + "Allegra", + "Alley", + "Alli", + "Allie", + "Allison", + "Ally", + "Allyson", + "Alma", + "Almira", + "Alona", + "Alondra", + "Altagracia", + "Althea", + "Alva", + "Alvina", + "Alyce", + "Alycia", + "Alyona", + "Alysa", + "Alyse", + "Alysha", + "Alysia", + "Alyson", + "Alyssa", + "Ama", + "Amada", + "Amal", + "Amalia", + "Amanda", + "Amandine", + "Amani", + "Amara", + "Amarilis", + "Amaris", + "Ambar", + "Amber", + "Ambika", + "Amelia", + "Amelie", + "Amena", + "Ami", + "Amie", + "Amina", + "Aminata", + "Amira", + "Amirah", + "Amisha", + "Amita", + "Amma", + "Amna", + "Amoy", + "Amparo", + "Amrita", + "Amy", + "Ana", + "Anabel", + "Anabella", + "Anabelle", + "Anais", + "Analia", + "Anam", + "Anamaria", + "Anamika", + "Ananda", + "Anastasia", + "Anastasiya", + "Anastassia", + "Anat", + "Anaya", + "Anca", + "Anda", + "Andi", + "Andie", + "Andra", + "Andrea", + "Andree", + "Andreea", + "Andreina", + "Andrene", + "Andria", + "Andriana", + "Anel", + "Anesha", + "Aneta", + "Anette", + "Angela", + "Angelia", + "Angelic", + "Angelica", + "Angelika", + "Angeliki", + "Angelina", + "Angeline", + "Angelique", + "Angelita", + "Angella", + "Angie", + "Ani", + "Ania", + "Anika", + "Anila", + "Anisa", + "Anisah", + "Anisha", + "Anissa", + "Anita", + "Anitha", + "Anitra", + "Anja", + "Anjali", + "Anjana", + "Anjelica", + "Anju", + "Anjuli", + "Ankita", + "Ann", + "Ann-Marie", + "Anna", + "Annabel", + "Annabelle", + "Annalisa", + "Annamaria", + "Annamarie", + "Anne", + "Anne-Marie", + "Annemarie", + "Annet", + "Annette", + "Anni", + "Annia", + "Annick", + "Annie", + "Annika", + "Annmarie", + "Anny", + "Anthea", + "Antionette", + "Antoinette", + "Antonella", + "Antonette", + "Antonia", + "Antonietta", + "Antonina", + "Anu", + "Anupama", + "Anusha", + "Anushka", + "Any", + "Anya", + "Aoife", + "Aparna", + "Apple", + "April", + "Aquila", + "Arabella", + "Araceli", + "Aracelis", + "Aracely", + "Archana", + "Arelis", + "Arely", + "Aretha", + "Aria", + "Ariadna", + "Ariana", + "Ariane", + "Arianna", + "Arianne", + "Ariela", + "Ariella", + "Arielle", + "Arisa", + "Arleen", + "Arlene", + "Arlette", + "Arline", + "Arlyn", + "Arlyne", + "Artemis", + "Arti", + "Aruna", + "Arzu", + "Asako", + "Asha", + "Ashante", + "Ashanti", + "Ashima", + "Ashlee", + "Ashleigh", + "Ashley", + "Ashli", + "Ashlie", + "Ashly", + "Ashwini", + "Asia", + "Asli", + "Asma", + "Asta", + "Astrid", + "Asuka", + "Asya", + "Atheña", + "Atiya", + "Atsuko", + "Aude", + "Audra", + "Audrey", + "Audrina", + "Audry", + "Augusta", + "Augustina", + "Aundrea", + "Aura", + "Aurea", + "Aurelia", + "Aurelie", + "Aurora", + "Autumn", + "Ava", + "Avalon", + "Avani", + "Ave", + "Avigail", + "Avion", + "Avis", + "Avital", + "Aviva", + "Avril", + "Awa", + "Awilda", + "Aya", + "Ayako", + "Ayana", + "Ayanna", + "Aye", + "Ayelet", + "Ayesha", + "Ayisha", + "Ayla", + "Aylin", + "Aysha", + "Ayumi", + "Ayça", + "Ayşe", + "Aziza", + "Azra", + "Azucena", + "Babette", + "Babs", + "Bahar", + "Bambi", + "Banu", + "Bara", + "Barb", + "Barbara", + "Barbie", + "Barbra", + "Bari", + "Barri", + "Barrie", + "Basia", + "Batsheva", + "Batya", + "Bea", + "Beata", + "Beatrice", + "Beatriz", + "Beba", + "Bebe", + "Becca", + "Becka", + "Beckie", + "Becky", + "Bee", + "Beena", + "Bei", + "Beka", + "Belen", + "Belinda", + "Belkis", + "Belkys", + "Bell", + "Bella", + "Belle", + "Benedetta", + "Benita", + "Berenice", + "Bernadette", + "Bernadine", + "Bernice", + "Berry", + "Berta", + "Bertha", + "Beryl", + "Bess", + "Bessie", + "Beth", + "Bethania", + "Bethann", + "Bethanne", + "Bethany", + "Betina", + "Betsey", + "Betsy", + "Bette", + "Betti", + "Bettie", + "Bettina", + "Betty", + "Bettyann", + "Betzaida", + "Bev", + "Beverley", + "Beverly", + "Biana", + "Bianca", + "Bianka", + "Bibi", + "Bibiana", + "Billie", + "Bina", + "Bindu", + "Birgit", + "Blair", + "Blaire", + "Blanca", + "Blanche", + "Blerta", + "Blossom", + "Blythe", + "Bobbi", + "Bobbie", + "Bonita", + "Bonnie", + "Bozena", + "Bracha", + "Brandee", + "Brandi", + "Brandie", + "Brandy", + "Bre", + "Breanna", + "Breanne", + "Bree", + "Brenda", + "Brenna", + "Bri", + "Bria", + "Briana", + "Brianna", + "Brianne", + "Bridget", + "Bridgett", + "Bridgette", + "Bridie", + "Brie", + "Brigette", + "Brigid", + "Brigida", + "Brigitta", + "Brigitte", + "Brina", + "Brit", + "Britney", + "Britt", + "Britta", + "Brittani", + "Brittany", + "Brittney", + "Brittni", + "Brittny", + "Britton", + "Bronwen", + "Bronwyn", + "Brooke", + "Bruna", + "Brunilda", + "Brynn", + "Bukola", + "Bunny", + "Burcu", + "Bushra", + "Cait", + "Caitlin", + "Caitlyn", + "Calandra", + "Cali", + "Callie", + "Camara", + "Camelia", + "Cami", + "Camila", + "Camilla", + "Camille", + "Cammy", + "Candace", + "Candi", + "Candice", + "Candida", + "Candie", + "Candis", + "Candy", + "Caprice", + "Cara", + "Caren", + "Carey", + "Cari", + "Caridad", + "Carin", + "Carina", + "Carine", + "Carissa", + "Carla", + "Carleen", + "Carlene", + "Carley", + "Carli", + "Carlie", + "Carlina", + "Carline", + "Carlotta", + "Carly", + "Carlyn", + "Carmel", + "Carmela", + "Carmelina", + "Carmelita", + "Carmella", + "Carmen", + "Carmencita", + "Caro", + "Carol", + "Carola", + "Carolann", + "Carole", + "Carolee", + "Carolin", + "Carolina", + "Caroline", + "Carolyn", + "Carolyne", + "Caron", + "Carrie", + "Carrol", + "Carroll", + "Caryl", + "Caryn", + "Casandra", + "Casie", + "Cass", + "Cassandra", + "Cassandre", + "Cassidy", + "Cassie", + "Cassy", + "Cat", + "Cata", + "Catalina", + "Catarina", + "Cate", + "Caterina", + "Catharine", + "Catherine", + "Cathi", + "Cathie", + "Cathleen", + "Cathrine", + "Cathryn", + "Cathy", + "Catia", + "Catie", + "Catrice", + "Catrina", + "Catriona", + "Caty", + "Cece", + "Cecelia", + "Ceci", + "Cecilia", + "Cecily", + "Celena", + "Celeste", + "Celestina", + "Celestine", + "Celia", + "Celina", + "Celine", + "Cerissa", + "Cha", + "Chana", + "Chanda", + "Chandra", + "Chanel", + "Chanell", + "Chanelle", + "Chani", + "Chanice", + "Chanie", + "Channel", + "Channing", + "Chantal", + "Chantay", + "Chante", + "Chantee", + "Chantel", + "Chantell", + "Chantelle", + "Char", + "Charis", + "Charise", + "Charisma", + "Charissa", + "Charisse", + "Charity", + "Charleen", + "Charlene", + "Charline", + "Charlotte", + "Charlyn", + "Charmaine", + "Charo", + "Charu", + "Chasity", + "Chastity", + "Chava", + "Chavi", + "Chaya", + "Chelle", + "Chelsea", + "Chelsey", + "Chelsie", + "Cher", + "Cherelle", + "Cheri", + "Cherie", + "Cherise", + "Cherish", + "Cherisse", + "Cherrie", + "Cherry", + "Cherryann", + "Cherryl", + "Cheryl", + "Cherylann", + "Chevonne", + "Cheyenne", + "Chi-Chi", + "Chiaki", + "Chiara", + "Chichi", + "Chickie", + "Chika", + "China", + "Chinyere", + "Chioma", + "Chitra", + "Chloe", + "Chrissie", + "Chrissy", + "Christa", + "Christabel", + "Christal", + "Christel", + "Christelle", + "Christen", + "Christi", + "Christiana", + "Christiane", + "Christianna", + "Christie", + "Christin", + "Christina", + "Christine", + "Christy", + "Chrystal", + "Chrystie", + "Chyna", + "Ciara", + "Cicely", + "Cici", + "Cielo", + "Ciera", + "Cierra", + "Cindi", + "Cindy", + "Cinnamon", + "Cinthia", + "Cinthya", + "Cintia", + "Cinzia", + "Clair", + "Claire", + "Clara", + "Clare", + "Claribel", + "Clarice", + "Clarisa", + "Clarissa", + "Clarisse", + "Claudette", + "Claudia", + "Claudina", + "Claudine", + "Cleo", + "Cleopatra", + "Clover", + "Coco", + "Cocoa", + "Coleen", + "Colette", + "Colleen", + "Collette", + "Concetta", + "Connie", + "Constance", + "Consuelo", + "Cora", + "Coral", + "Cordelia", + "Coreen", + "Coretta", + "Cori", + "Corie", + "Corina", + "Corine", + "Corinna", + "Corinne", + "Corinthia", + "Cornelia", + "Corrie", + "Corrine", + "Cortney", + "Courtenay", + "Courteney", + "Courtney", + "Cricket", + "Crissy", + "Crista", + "Cristal", + "Cristen", + "Cristiana", + "Cristin", + "Cristina", + "Cristine", + "Cristy", + "Crystal", + "Cui", + "Cyn", + "Cyndi", + "Cynthia", + "Cécile", + "Dacia", + "Dagmar", + "Dagmara", + "Dahiana", + "Dahlia", + "Daiana", + "Daina", + "Daisy", + "Dali", + "Dalia", + "Dalila", + "Damali", + "Damaris", + "Damla", + "Dana", + "Danae", + "Danelle", + "Danessa", + "Danette", + "Dani", + "Dania", + "Danica", + "Daniela", + "Daniella", + "Danielle", + "Danika", + "Danisha", + "Danit", + "Danita", + "Danna", + "Danni", + "Dannie", + "Dannielle", + "Danuta", + "Danya", + "Danyelle", + "Daphne", + "Daphnee", + "Daphney", + "Dara", + "Darcel", + "Darci", + "Darcy", + "Daria", + "Darla", + "Darleen", + "Darlene", + "Darline", + "Darling", + "Darya", + "Dasha", + "Davida", + "Davina", + "Dawn", + "Dawne", + "Dawnmarie", + "Dayana", + "Dayna", + "Daysi", + "Dea", + "Deana", + "Deandra", + "Deanna", + "Deanne", + "Deb", + "Debbi", + "Debbie", + "Debby", + "Debi", + "Debora", + "Deborah", + "Debra", + "Dede", + "Dedra", + "Deedee", + "Deena", + "Deepa", + "Deepali", + "Deepika", + "Deepthi", + "Deepti", + "Deidra", + "Deidre", + "Deirdra", + "Deirdre", + "Deisy", + "Deja", + "Delia", + "Delilah", + "Della", + "Delma", + "Delores", + "Deloris", + "Delphine", + "Demetra", + "Demetria", + "Demi", + "Dena", + "Deneen", + "Denice", + "Denise", + "Denisha", + "Denisse", + "Dennise", + "Denyse", + "Derin", + "Derya", + "Des", + "Deserie", + "Desirae", + "Desire", + "Desiree", + "Despina", + "Destiny", + "Deva", + "Devan", + "Devi", + "Devika", + "Devina", + "Devona", + "Devora", + "Devorah", + "Devra", + "Devyn", + "Deyanira", + "Dhara", + "Dia", + "Diamond", + "Dian", + "Diandra", + "Diane", + "Diann", + "Dianna", + "Dianne", + "Didi", + "Diedre", + "Digna", + "Dikla", + "Dilcia", + "Dimitra", + "Dimple", + "Dina", + "Dinah", + "Dinara", + "Dini", + "Dinorah", + "Diona", + "Dione", + "Dionne", + "Dior", + "Divina", + "Divya", + "Dixie", + "Dolly", + "Dolores", + "Domenica", + "Dominika", + "Dominique", + "Domonique", + "Dona", + "Donatella", + "Donette", + "Donielle", + "Donna", + "Donnamarie", + "Donnette", + "Dora", + "Dorcas", + "Doreen", + "Dorene", + "Dori", + "Dorie", + "Dorina", + "Doris", + "Dorit", + "Dorota", + "Dorothea", + "Dorothee", + "Dorothy", + "Dory", + "Dot", + "Dottie", + "Dotty", + "Dragana", + "Drea", + "Drita", + "Dulce", + "Dyan", + "Dyana", + "Eartha", + "Eboni", + "Ebonie", + "Ebony", + "Ebru", + "Ece", + "Eda", + "Eden", + "Edie", + "Edina", + "Edita", + "Edith", + "Edna", + "Edwina", + "Edyta", + "Effie", + "Efrat", + "Eileen", + "Ekaterina", + "Ekta", + "Ela", + "Elaina", + "Elaine", + "Elana", + "Elayne", + "Elba", + "Elda", + "Eleana", + "Eleanor", + "Elektra", + "Elen", + "Elena", + "Eleni", + "Eleonor", + "Eleonora", + "Eliana", + "Elicia", + "Elida", + "Elif", + "Elin", + "Elina", + "Elinor", + "Elisa", + "Elisabeth", + "Elisabetta", + "Elise", + "Elisha", + "Elisheva", + "Elissa", + "Eliza", + "Elizabeth", + "Elka", + "Elke", + "Ella", + "Elle", + "Ellen", + "Elli", + "Ellie", + "Elly", + "Ellyn", + "Elma", + "Elodie", + "Eloisa", + "Eloise", + "Elona", + "Elsa", + "Elsie", + "Elsy", + "Elva", + "Elvia", + "Elvira", + "Elyse", + "Elyssa", + "Elzbieta", + "Em", + "Ema", + "Eman", + "Emanuela", + "Emel", + "Emelia", + "Emely", + "Emerald", + "Emi", + "Emiko", + "Emilee", + "Emilia", + "Emilie", + "Emily", + "Emma", + "Emmanuella", + "Emmanuelle", + "Emmie", + "Emmy", + "Emy", + "Ena", + "Eneida", + "Enid", + "Enjoli", + "Enny", + "Enza", + "Era", + "Eri", + "Erica", + "Ericka", + "Erika", + "Eriko", + "Erin", + "Erina", + "Erinn", + "Erlinda", + "Erna", + "Ernestina", + "Ernestine", + "Eryka", + "Eryn", + "Esha", + "Esin", + "Esmeralda", + "Esperanza", + "Esra", + "Essence", + "Essie", + "Esta", + "Estee", + "Estefania", + "Estefany", + "Estela", + "Estella", + "Estelle", + "Ester", + "Esther", + "Esti", + "Estrella", + "Esty", + "Ethel", + "Eti", + "Etta", + "Eugenia", + "Eugenie", + "Eun", + "Eunhye", + "Eunice", + "Eunsun", + "Eva", + "Evana", + "Evangelia", + "Evangeline", + "Eve", + "Evelin", + "Evelina", + "Eveline", + "Evelyn", + "Evette", + "Evgenia", + "Evgeniya", + "Evi", + "Evie", + "Evita", + "Evon", + "Evonne", + "Evy", + "Ewa", + "Ewelina", + "Fabiana", + "Fabienne", + "Fabiola", + "Faigy", + "Faina", + "Faith", + "Fallon", + "Fancy", + "Fang", + "Fannie", + "Fanny", + "Fanta", + "Fara", + "Farah", + "Farhana", + "Farida", + "Farrah", + "Farzana", + "Fatema", + "Fatima", + "Fatimah", + "Fatou", + "Fatoumata", + "Fawn", + "Fay", + "Faye", + "Faïza", + "Fe", + "Federica", + "Felecia", + "Felice", + "Felicia", + "Felicita", + "Felicity", + "Felisa", + "Felise", + "Felisha", + "Fern", + "Fernanda", + "Fia", + "Fidelia", + "Fifi", + "Filiz", + "Filomena", + "Fiona", + "Fior", + "Fiordaliza", + "Fiorella", + "Flavia", + "Flawless", + "Fleur", + "Flo", + "Flor", + "Flora", + "Florence", + "Florencia", + "Florina", + "Fluffy", + "Fran", + "Franca", + "France", + "Frances", + "Francesca", + "Franchesca", + "Francheska", + "Francia", + "Francine", + "Francisca", + "Francoise", + "Francy", + "Frannie", + "Franny", + "Franziska", + "Freda", + "Fredda", + "Frederica", + "Freya", + "Frida", + "Frieda", + "Fumiko", + "Funmi", + "Gabby", + "Gabi", + "Gabriela", + "Gabriella", + "Gabrielle", + "Gaby", + "Gaelle", + "Gaia", + "Gail", + "Gale", + "Galia", + "Galina", + "Galit", + "Gamze", + "Gay", + "Gaye", + "Gayle", + "Geeta", + "Geisha", + "Gemma", + "Gena", + "Genesis", + "Geneva", + "Genevieve", + "Genia", + "Genie", + "Genine", + "Genise", + "Genna", + "Genny", + "Georgette", + "Georgia", + "Georgiana", + "Georgianna", + "Georgina", + "Geraldine", + "Geralyn", + "Geri", + "Germaine", + "Gerri", + "Gertrude", + "Ghislaine", + "Gi", + "Gia", + "Giana", + "Gianna", + "Gidget", + "Gigi", + "Gila", + "Gilda", + "Gillian", + "Gina", + "Ginamarie", + "Ginette", + "Ginger", + "Gini", + "Ginna", + "Ginny", + "Giorgia", + "Giovana", + "Giovanna", + "Gisela", + "Gisele", + "Gisella", + "Giselle", + "Gissel", + "Gissell", + "Gisselle", + "Gita", + "Gitty", + "Giulia", + "Giuliana", + "Giuseppina", + "Gizem", + "Gladys", + "Glenda", + "Glenna", + "Glenny", + "Gleny", + "Glenys", + "Gloria", + "Glory", + "Glynis", + "Golda", + "Goldie", + "Gordana", + "Gosia", + "Gozde", + "Grace", + "Graceanne", + "Gracie", + "Graciela", + "Graziella", + "Greer", + "Greta", + "Gretchen", + "Gricel", + "Grisel", + "Griselda", + "Guadalupe", + "Guerda", + "Gwen", + "Gwendolyn", + "Gwenn", + "Gwyneth", + "Ha", + "Habiba", + "Hadas", + "Hadassah", + "Hadley", + "Hailey", + "Hala", + "Haley", + "Halima", + "Halina", + "Halley", + "Hallie", + "Hana", + "Hanan", + "Hanna", + "Hannah", + "Hanny", + "Harmony", + "Harriet", + "Haruka", + "Hatice", + "Hattie", + "Haydee", + "Hayley", + "Hazel", + "Heather", + "Heba", + "Hedy", + "Heejin", + "Heide", + "Heidi", + "Heidy", + "Heike", + "Helaine", + "Helen", + "Helena", + "Helga", + "Hellen", + "Heloise", + "Hema", + "Henna", + "Henny", + "Henrietta", + "Hester", + "Hila", + "Hilary", + "Hilda", + "Hildy", + "Hillary", + "Hina", + "Hindy", + "Hira", + "Hiroko", + "Hiromi", + "Hitomi", + "Hollie", + "Holly", + "Honey", + "Honor", + "Hope", + "Hortensia", + "Hui", + "Hulya", + "Huma", + "Hyacinth", + "Hye", + "Hyunjoo", + "Hélène", + "Ida", + "Idalia", + "Iesha", + "Ife", + "Ifeoma", + "Ijeoma", + "Ilana", + "Ilaria", + "Ildiko", + "Ileana", + "Ilene", + "Iliana", + "Ilka", + "Ilona", + "Ilse", + "Ilyssa", + "Ima", + "Iman", + "Imani", + "Imelda", + "Imogen", + "Ina", + "Inbal", + "Indi", + "India", + "Indiana", + "Indira", + "Indra", + "Inessa", + "Inez", + "Inga", + "Inge", + "Inger", + "Ingrid", + "Inna", + "Inés", + "Ioana", + "Ioanna", + "Iona", + "Iraida", + "Irena", + "Irene", + "Irina", + "Iris", + "Irma", + "Iryna", + "Isabel", + "Isabela", + "Isabella", + "Isabelle", + "Isadora", + "Isamar", + "Isaura", + "Isela", + "Isha", + "Isis", + "Israt", + "Ita", + "Iva", + "Ivana", + "Ivanna", + "Ive", + "Ivelisse", + "Iveta", + "Ivette", + "Ivie", + "Ivon", + "Ivona", + "Ivonne", + "Ivory", + "Ivy", + "Iwona", + "Izabela", + "Izabella", + "Izumi", + "Jaci", + "Jacinda", + "Jacinta", + "Jacinth", + "Jackee", + "Jackeline", + "Jacki", + "Jackie", + "Jacklyn", + "Jaclyn", + "Jacque", + "Jacquelin", + "Jacqueline", + "Jacquelyn", + "Jacqui", + "Jacquie", + "Jacy", + "Jada", + "Jade", + "Jael", + "Jahaira", + "Jaimee", + "Jaimie", + "Jaleesa", + "Jalisa", + "Jalissa", + "Jameela", + "Jamela", + "Jamelia", + "Jamella", + "Jami", + "Jamie", + "Jamila", + "Jamilah", + "Jamillah", + "Jana", + "Janae", + "Janay", + "Jane", + "Janee", + "Janeen", + "Janel", + "Janell", + "Janelle", + "Janessa", + "Janet", + "Janeth", + "Janett", + "Janette", + "Janey", + "Janice", + "Janie", + "Janiece", + "Janina", + "Janine", + "Janis", + "Janise", + "Janna", + "Jannelle", + "Jannet", + "Jannette", + "Jannie", + "Jannine", + "Janny", + "January", + "Jany", + "Jaqueline", + "Jasleen", + "Jasmin", + "Jasmina", + "Jasmine", + "Jasmyn", + "Jaya", + "Jaye", + "Jayme", + "Jaymee", + "Jaymie", + "Jayne", + "Jazmin", + "Jazmine", + "Jeana", + "Jeanelle", + "Jeanette", + "Jeanie", + "Jeanine", + "Jeanmarie", + "Jeanna", + "Jeanne", + "Jeannette", + "Jeannie", + "Jeannine", + "Jee", + "Jeimy", + "Jelena", + "Jemima", + "Jemma", + "Jen", + "Jena", + "Jenee", + "Jenel", + "Jenelle", + "Jeni", + "Jenice", + "Jenifer", + "Jeniffer", + "Jenine", + "Jenise", + "Jenn", + "Jenna", + "Jennefer", + "Jennette", + "Jenni", + "Jennie", + "Jennifer", + "Jenniffer", + "Jennine", + "Jenny", + "Jennyfer", + "Jenya", + "Jeri", + "Jerilyn", + "Jerri", + "Jes", + "Jesenia", + "Jesica", + "Jess", + "Jessa", + "Jessenia", + "Jessi", + "Jessica", + "Jessie", + "Jessika", + "Jessy", + "Jessyca", + "Jewel", + "Jewell", + "Jewels", + "Jieun", + "Jihae", + "Jihan", + "Jihee", + "Jihye", + "Jihyun", + "Jill", + "Jillian", + "Jilly", + "Jina", + "Jinelle", + "Jing", + "Jingjing", + "Jinna", + "Jinnie", + "Jinny", + "Jiwon", + "Jiyeon", + "Jiyoung", + "Jo", + "Jo-Ann", + "Joan", + "Joana", + "Joane", + "Joanie", + "Joann", + "Joanna", + "Joanne", + "Joannie", + "Joanny", + "Jocelyn", + "Jocelyne", + "Jodee", + "Jodi", + "Jodi-Ann", + "Jodian", + "Jodie", + "Jody", + "Joelle", + "Joellen", + "Johana", + "Johanne", + "Johanny", + "Johnna", + "Joi", + "Joie", + "Jola", + "Jolanda", + "Jolanta", + "Joleen", + "Jolene", + "Jolie", + "Joline", + "Jonelle", + "Joni", + "Jonna", + "Joo", + "Jordana", + "Jordyn", + "Josefa", + "Josefina", + "Josefine", + "Joselin", + "Joselyn", + "Josephine", + "Josette", + "Josie", + "Joslyn", + "Josy", + "Jovanna", + "Jovita", + "Joy", + "Joyce", + "Joycelyn", + "Juana", + "Juanita", + "Judi", + "Judie", + "Judit", + "Judith", + "Judy", + "Juju", + "Julene", + "Juli", + "Juliana", + "Juliane", + "Juliann", + "Julianna", + "Julianne", + "Julie", + "Julieann", + "Juliet", + "Julieta", + "Juliette", + "Julisa", + "Julissa", + "Juliya", + "July", + "June", + "Junko", + "Justina", + "Justine", + "Justyna", + "Jyoti", + "Jóhanna", + "Júlia", + "Kacey", + "Kaci", + "Kacie", + "Kadian", + "Kady", + "Kae", + "Kafi", + "Kaila", + "Kaisha", + "Kait", + "Kaitlin", + "Kaitlyn", + "Kaitlynn", + "Kaity", + "Kali", + "Kalimah", + "Kalina", + "Kalisha", + "Kally", + "Kalpana", + "Kamala", + "Kamaria", + "Kami", + "Kamika", + "Kamila", + "Kamilah", + "Kamilla", + "Kamille", + "Kamini", + "Kamisha", + "Kana", + "Kanako", + "Kandi", + "Kandice", + "Kandis", + "Kanika", + "Kaori", + "Kara", + "Kareen", + "Karem", + "Karen", + "Karena", + "Karima", + "Karimah", + "Karin", + "Karina", + "Karine", + "Karisa", + "Karishma", + "Karissa", + "Karla", + "Karlene", + "Karli", + "Karmen", + "Karolina", + "Karoline", + "Karolyn", + "Karren", + "Karrie", + "Kary", + "Karyn", + "Kasandra", + "Kasey", + "Kasia", + "Kassandra", + "Kassie", + "Kat", + "Katalin", + "Katalina", + "Katarina", + "Katarzyna", + "Kate", + "Katelyn", + "Katerina", + "Katey", + "Katharina", + "Katharine", + "Katherin", + "Katherina", + "Katherine", + "Katheryn", + "Kathi", + "Kathia", + "Kathie", + "Kathleen", + "Kathrin", + "Kathrine", + "Kathryn", + "Kathy", + "Kathyann", + "Kati", + "Katia", + "Katiana", + "Katie", + "Katina", + "Katiria", + "Katja", + "Katlyn", + "Katrice", + "Katrin", + "Katrina", + "Katty", + "Katy", + "Katya", + "Kavita", + "Kay", + "Kaya", + "Kayan", + "Kaydian", + "Kaye", + "Kayla", + "Kaylee", + "Kayleigh", + "Kaylie", + "Kaylin", + "Kayo", + "Kayon", + "Keara", + "Kecia", + "Keely", + "Keesha", + "Keiko", + "Keila", + "Keira", + "Keisha", + "Keke", + "Keli", + "Kellee", + "Kelley", + "Kelli", + "Kellie", + "Kelly", + "Kelly-Ann", + "Kellyann", + "Kelsey", + "Kemba", + "Kemi", + "Kendra", + "Kenia", + "Kenisha", + "Kenya", + "Kenyetta", + "Kera", + "Keren", + "Keri", + "Keriann", + "Kerin", + "Kerisha", + "Kerri", + "Kerri-Ann", + "Kerrie", + "Kerrin", + "Kerry", + "Kerry-Ann", + "Kerryann", + "Kerstin", + "Kesha", + "Keshia", + "Ketsia", + "Ketty", + "Keturah", + "Keya", + "Keyanna", + "Keyla", + "Keyonna", + "Keysha", + "Kezia", + "Khadija", + "Khadijah", + "Khadine", + "Khalia", + "Khalilah", + "Kia", + "Kiana", + "Kiara", + "Kiera", + "Kierra", + "Kiesha", + "Kika", + "Kiki", + "Kiley", + "Kim", + "Kima", + "Kimara", + "Kimberlee", + "Kimberley", + "Kimberly", + "Kimesha", + "Kimmie", + "Kimmy", + "Kinga", + "Kira", + "Kiri", + "Kirsten", + "Kirstin", + "Kirsty", + "Kisha", + "Kitty", + "Kiya", + "Kizzy", + "Klara", + "Klaudia", + "Koko", + "Komal", + "Kori", + "Korin", + "Kortney", + "Kourtney", + "Krissie", + "Krissy", + "Krista", + "Kristal", + "Kristan", + "Kristel", + "Kristen", + "Kristi", + "Kristie", + "Kristin", + "Kristina", + "Kristine", + "Kristy", + "Kristyn", + "Krisztina", + "Krupa", + "Krysta", + "Krystal", + "Krystel", + "Krysten", + "Krystin", + "Krystina", + "Krystle", + "Krystyna", + "Ksenia", + "Kseniya", + "Kumiko", + "Kyla", + "Kylie", + "Kym", + "Kyoko", + "Kyra", + "Kári", + "La", + "Lacey", + "Lacy", + "Ladii", + "Ladonna", + "Lady", + "Laetitia", + "Laila", + "Laine", + "Lainie", + "Lakeisha", + "Lakesha", + "Lakeya", + "Lakia", + "Lakiesha", + "Lakisha", + "Lakshmi", + "Lala", + "Lale", + "Lali", + "Lalita", + "Lama", + "Lamia", + "Lan", + "Lana", + "Lani", + "Laquana", + "Laquanda", + "Lara", + "Laraine", + "Larisa", + "Larissa", + "Lariza", + "Larysa", + "Lashana", + "Lashanda", + "Lashanna", + "Lashaun", + "Lashawn", + "Lashawna", + "Lashon", + "Lashonda", + "Latanya", + "Latasha", + "Lateefah", + "Lateisha", + "Latesha", + "Latia", + "Laticia", + "Latifa", + "Latifah", + "Latisha", + "Latonia", + "Latonya", + "Latosha", + "Latoya", + "Latrice", + "Latricia", + "Laura", + "Lauralee", + "Laure", + "Laureen", + "Laurel", + "Lauren", + "Lauretta", + "Laurette", + "Lauri", + "Laurice", + "Laurie", + "Laury", + "Lauryn", + "Lavern", + "Laverne", + "Lavinia", + "Lavonne", + "Lawanda", + "Layla", + "Lea", + "Leah", + "Leandra", + "Leann", + "Leanna", + "Leanne", + "Leda", + "Leeann", + "Leeanne", + "Leela", + "Leena", + "Leesa", + "Leia", + "Leidy", + "Leigh", + "Leigha", + "Leighann", + "Leighanne", + "Leila", + "Leilani", + "Leisa", + "Lela", + "Lelia", + "Lena", + "Lenette", + "Leni", + "Lenka", + "Lenora", + "Lenore", + "Leona", + "Leonela", + "Leonie", + "Leonor", + "Leonora", + "Leora", + "Lesa", + "Lesia", + "Lesley", + "Leslie", + "Leslye", + "Leslyn", + "Leta", + "Leticia", + "Letisha", + "Letitia", + "Letizia", + "Letty", + "Lexi", + "Lexie", + "Lexy", + "Leydi", + "Leydy", + "Leyla", + "Leyna", + "Lia", + "Liana", + "Liane", + "Lianna", + "Lianne", + "Liat", + "Libby", + "Liberty", + "Libia", + "Lida", + "Lidia", + "Ligia", + "Lila", + "Lilach", + "Lili", + "Lilia", + "Lilian", + "Liliana", + "Liliane", + "Lilibeth", + "Lilith", + "Liliya", + "Lilla", + "Lilli", + "Lilliam", + "Lillian", + "Lilliana", + "Lillie", + "Lilly", + "Lily", + "Limor", + "Lina", + "Linda", + "Lindsay", + "Lindsey", + "Lindy", + "Linette", + "Ling", + "Linh", + "Linn", + "Linnea", + "Linnette", + "Lis", + "Lisa", + "Lisa-Marie", + "Lisamarie", + "Lisandra", + "Lisbeth", + "Lise", + "Lisette", + "Lisha", + "Lisi", + "Lissa", + "Lissett", + "Lissette", + "Lissy", + "Lita", + "Lital", + "Liv", + "Livia", + "Liya", + "Liz", + "Liza", + "Lizabeth", + "Lizbeth", + "Lizeth", + "Lizette", + "Lizz", + "Lizzette", + "Lizzie", + "Lizzy", + "Loida", + "Lois", + "Lola", + "Lolita", + "Loni", + "Lopez", + "Lora", + "Loraine", + "Lore", + "Loredana", + "Loreen", + "Lorelei", + "Loren", + "Lorena", + "Loreta", + "Loretta", + "Lori", + "Loriann", + "Lorie", + "Lorine", + "Lorna", + "Lorraine", + "Lorri", + "Lorrie", + "Lory", + "Lotus", + "Louann", + "Louella", + "Louisa", + "Louise", + "Lourdes", + "Lovely", + "Loyda", + "Luana", + "Luann", + "Luanne", + "Luba", + "Lucero", + "Luci", + "Lucia", + "Luciana", + "Lucie", + "Lucienne", + "Lucila", + "Lucille", + "Lucinda", + "Lucrecia", + "Lucretia", + "Lucy", + "Luda", + "Ludmila", + "Luisa", + "Luiza", + "Lula", + "Lulu", + "Luna", + "Lupita", + "Luz", + "Lydia", + "Lyn", + "Lynda", + "Lyndsay", + "Lyndsey", + "Lynette", + "Lynn", + "Lynne", + "Lynnette", + "Lynsey", + "Lysa", + "Lysette", + "Lyssa", + "Lystra", + "Lyubov", + "Lyudmila", + "Maame", + "Maayan", + "Mabel", + "Macarena", + "Mackenzie", + "Macy", + "Maddalena", + "Maddie", + "Maddy", + "Madelaine", + "Madeleine", + "Madelin", + "Madeline", + "Madelyn", + "Madhavi", + "Madi", + "Madina", + "Madison", + "Madonna", + "Mae", + "Maegan", + "Maeve", + "Magali", + "Magalie", + "Magaly", + "Magda", + "Magdalena", + "Magen", + "Maggi", + "Maggie", + "Maggy", + "Magnolia", + "Maha", + "Mahalia", + "Mahogany", + "Mai", + "Maia", + "Maida", + "Maiko", + "Maira", + "Mairead", + "Maisha", + "Maite", + "Maja", + "Makeba", + "Makeda", + "Maki", + "Makiko", + "Mala", + "Malaika", + "Malene", + "Malgorzata", + "Mali", + "Malia", + "Malika", + "Malin", + "Malina", + "Malinda", + "Malini", + "Malissa", + "Malka", + "Malkie", + "Malky", + "Mallory", + "Malorie", + "Malou", + "Malu", + "Malvina", + "Mami", + "Mamie", + "Mamta", + "Manal", + "Manda", + "Mandi", + "Mandie", + "Mandisa", + "Mandy", + "Manisha", + "Manju", + "Manon", + "Mansi", + "Manuela", + "Mara", + "Maralyn", + "Marcela", + "Marcelina", + "Marcella", + "Marcelle", + "Marci", + "Marcia", + "Marcie", + "Marcy", + "Maren", + "Margaret", + "Margarita", + "Margaux", + "Marge", + "Margery", + "Margherita", + "Margi", + "Margie", + "Margit", + "Margo", + "Margot", + "Margrét", + "Marguerite", + "Mari", + "Maria", + "Mariaelena", + "Mariah", + "Mariam", + "Mariama", + "Marian", + "Mariana", + "Marianela", + "Marianella", + "Mariangela", + "Mariann", + "Marianna", + "Marianne", + "Maribel", + "Maribeth", + "Maricel", + "Maricela", + "Marie", + "Mariel", + "Mariela", + "Mariella", + "Marielle", + "Mariely", + "Marietta", + "Marija", + "Marika", + "Mariko", + "Marilee", + "Marilena", + "Marilou", + "Marilu", + "Mariluz", + "Marilyn", + "Marilynn", + "Marina", + "Marinela", + "Mariné", + "Mariola", + "Marion", + "Maris", + "Marisa", + "Marisel", + "Marisela", + "Marisol", + "Marissa", + "Marita", + "Maritza", + "Mariuxi", + "Marivel", + "Marivic", + "Mariya", + "Marjan", + "Marjorie", + "Marjory", + "Marketa", + "Marla", + "Marleen", + "Marlena", + "Marlene", + "Marleny", + "Marlin", + "Marline", + "Marlo", + "Marly", + "Marlyn", + "Marni", + "Marnie", + "Marquita", + "Marsha", + "Marta", + "Martha", + "Marti", + "Martina", + "Martine", + "Martyna", + "Maru", + "Marva", + "Marwa", + "Mary", + "Mary-Jane", + "Marya", + "Maryam", + "Maryana", + "Maryann", + "Maryanne", + "Marybeth", + "Maryellen", + "Maryjane", + "Marylin", + "Marylou", + "Marylu", + "Maryrose", + "Maryse", + "Marzena", + "Masako", + "Masha", + "Massiel", + "Mathilde", + "Matilda", + "Matilde", + "Mattie", + "Maud", + "Maude", + "Maura", + "Maureen", + "Mavis", + "Maxie", + "Maxine", + "May", + "Maya", + "Maylin", + "Mayra", + "Maytal", + "Mayu", + "Mayuko", + "Mayumi", + "Mazal", + "Mckenzie", + "Meagan", + "Meaghan", + "Mecca", + "Medina", + "Meeka", + "Meena", + "Meera", + "Meg", + "Megan", + "Megha", + "Meghan", + "Meghann", + "Meghna", + "Megumi", + "Mehreen", + "Mei", + "Meiling", + "Meira", + "Meisha", + "Meital", + "Meka", + "Mela", + "Melaine", + "Melani", + "Melania", + "Melanie", + "Melany", + "Melba", + "Meli", + "Melida", + "Melina", + "Melinda", + "Melis", + "Melisa", + "Melissa", + "Mellisa", + "Mellissa", + "Melly", + "Melodie", + "Melody", + "Melonie", + "Melony", + "Meltem", + "Melva", + "Mely", + "Melyssa", + "Meme", + "Merav", + "Mercedes", + "Mercy", + "Meredith", + "Meri", + "Merideth", + "Meridith", + "Merle", + "Merlene", + "Merlyn", + "Merri", + "Merrie", + "Merry", + "Merryl", + "Merve", + "Mery", + "Meryl", + "Mesha", + "Mi", + "Mia", + "Miao", + "Mica", + "Micaela", + "Mich", + "Michaela", + "Michaelle", + "Michela", + "Michele", + "Michelina", + "Micheline", + "Michell", + "Michelle", + "Michi", + "Michiko", + "Micki", + "Mickie", + "Midge", + "Midori", + "Migdalia", + "Miguelina", + "Mihaela", + "Miho", + "Miisz", + "Mika", + "Mikaela", + "Mikki", + "Mila", + "Milagros", + "Milana", + "Mildred", + "Milena", + "Miley", + "Mili", + "Milica", + "Milli", + "Millicent", + "Millie", + "Milly", + "Mima", + "Mimi", + "Mimoza", + "Mina", + "Minal", + "Mindi", + "Mindy", + "Minerva", + "Mini", + "Minji", + "Minjung", + "Minna", + "Minnie", + "Minyoung", + "Mio", + "Miosotis", + "Mira", + "Miracle", + "Miranda", + "Mireille", + "Mirela", + "Mirella", + "Mireya", + "Miri", + "Miriam", + "Mirian", + "Mirjana", + "Mirna", + "Mirta", + "Mirtha", + "Miryam", + "Misa", + "Mischa", + "Miss", + "Missy", + "Misty", + "Mita", + "Mitra", + "Mitzi", + "Mitzie", + "Miya", + "Miyuki", + "Miz", + "Mocha", + "Moira", + "Mollie", + "Molly", + "Mona", + "Monae", + "Monalisa", + "Monet", + "Moni", + "Monie", + "Monifa", + "Monika", + "Monique", + "Monisha", + "Moon", + "Moran", + "Morgen", + "Moriah", + "Moya", + "Muna", + "Muriel", + "Murielle", + "Mya", + "Myisha", + "Myla", + "Mylene", + "Myra", + "Myriam", + "Myrlene", + "Myrna", + "Myrtle", + "Má", + "Mónica", + "Na", + "Naa", + "Naama", + "Nabila", + "Nada", + "Nadeen", + "Nadege", + "Nadia", + "Nadine", + "Nadira", + "Nadiyah", + "Nadja", + "Nadya", + "Naeemah", + "Nahid", + "Naida", + "Naila", + "Nailah", + "Naima", + "Naimah", + "Naisha", + "Naja", + "Najah", + "Najla", + "Nakia", + "Nakisha", + "Nala", + "Nalini", + "Nami", + "Namrata", + "Nan", + "Nanci", + "Nancie", + "Nancy", + "Nandi", + "Nandini", + "Nandita", + "Nanette", + "Nani", + "Nannette", + "Nao", + "Naoko", + "Naomi", + "Naomy", + "Narcisa", + "Narda", + "Narissa", + "Naseem", + "Nasha", + "Nasrin", + "Nastassia", + "Natacha", + "Natalee", + "Natali", + "Natalia", + "Natalie", + "Nataliya", + "Nataly", + "Natalya", + "Natasa", + "Natascha", + "Natasha", + "Natasia", + "Nathalia", + "Nathalie", + "Nathaly", + "Natia", + "Natisha", + "Natividad", + "Natoya", + "Naudia", + "Nava", + "Navi", + "Nay", + "Nayda", + "Naz", + "Nazia", + "Ndeye", + "Nechama", + "Neda", + "Neelam", + "Neena", + "Neeta", + "Neetu", + "Nef", + "Nefertiti", + "Neha", + "Neisha", + "Nekeisha", + "Nekia", + "Nelida", + "Nell", + "Nella", + "Nelle", + "Nellie", + "Nelly", + "Nelsi", + "Nelsy", + "Nena", + "Nereida", + "Nerissa", + "Nery", + "Nesha", + "Nessa", + "Neta", + "Netta", + "Nettie", + "Neva", + "Ngoc", + "Ngozi", + "Nia", + "Niamh", + "Niasia", + "Nicci", + "Niccole", + "Nichelle", + "Nichol", + "Nichola", + "Nichole", + "Nickesha", + "Nickey", + "Nicki", + "Nickie", + "Nicol", + "Nicole", + "Nicoletta", + "Nicolette", + "Nicolina", + "Nicolle", + "Nida", + "Nidhi", + "Nidia", + "Niecy", + "Niesha", + "Nieves", + "Nijah", + "Nika", + "Nikesha", + "Niki", + "Nikia", + "Nikisha", + "Nikki", + "Nikkia", + "Nikol", + "Nikole", + "Nila", + "Nilda", + "Nili", + "Nilka", + "Nilsa", + "Nina", + "Ninfa", + "Nini", + "Ninoska", + "Nira", + "Nirmala", + "Nisa", + "Nisha", + "Nita", + "Nitza", + "Niurka", + "Niya", + "Nneka", + "Noa", + "Noelia", + "Noelle", + "Noemi", + "Nola", + "Nomi", + "Nona", + "Noni", + "Nonna", + "Nora", + "Norah", + "Nordia", + "Noreen", + "Nori", + "Noriko", + "Norka", + "Norma", + "Noura", + "Novella", + "Nuala", + "Nubia", + "Nur", + "Nuria", + "Nurit", + "Nury", + "Nusrat", + "Nya", + "Nyasha", + "Nyasia", + "Nydia", + "Nyesha", + "Nyisha", + "Nykia", + "Nyla", + "Nyoka", + "Nyree", + "Oana", + "Octavia", + "Odalis", + "Odalys", + "Odessa", + "Odetta", + "Odette", + "Ofelia", + "Oksana", + "Ola", + "Olena", + "Olesya", + "Olga", + "Olimpia", + "Olive", + "Olya", + "Olympia", + "Oma", + "Omaira", + "Omayra", + "Omi", + "Omolara", + "Oneida", + "Onica", + "Onika", + "Opal", + "Ophelia", + "Ora", + "Oriana", + "Orit", + "Orla", + "Orlee", + "Orli", + "Orly", + "Orna", + "Ornella", + "Osnat", + "Oxana", + "Padma", + "Paige", + "Pallavi", + "Palma", + "Paloma", + "Pam", + "Pamela", + "Pamella", + "Pammy", + "Paola", + "Pari", + "Parul", + "Pascale", + "Patience", + "Patrice", + "Patrina", + "Patrizia", + "Patrycja", + "Patrícia", + "Patsy", + "Patt", + "Patti", + "Pattie", + "Patty", + "Pau", + "Paula", + "Paulette", + "Paulina", + "Pauline", + "Payal", + "Pearl", + "Pebbles", + "Peg", + "Peggy", + "Pei", + "Pelin", + "Pema", + "Penina", + "Penny", + "Penèlope", + "Pepi", + "Pepper", + "Peri", + "Perla", + "Perri", + "Petal", + "Petra", + "Petrina", + "Petula", + "Petunia", + "Phaedra", + "Philippa", + "Philomena", + "Phoebe", + "Phuong", + "Phylicia", + "Phyllis", + "Pia", + "Piedad", + "Piera", + "Pilar", + "Pina", + "Pinar", + "Ping", + "Pink", + "Pinky", + "Piper", + "Pixie", + "Polina", + "Polly", + "Pooja", + "Poonam", + "Popi", + "Poppy", + "Porsche", + "Porsha", + "Portia", + "Prachi", + "Pratima", + "Precious", + "Preeti", + "Pretty", + "Preya", + "Princess", + "Prisca", + "Priscila", + "Priscilla", + "Priti", + "Priya", + "Priyanka", + "Prudence", + "Pui", + "Puja", + "Pura", + "Purvi", + "Qian", + "Qiana", + "Qing", + "Quana", + "Queenie", + "Quiana", + "Quianna", + "Quynh", + "Rabia", + "Rachael", + "Racheal", + "Rachel", + "Rachele", + "Racheli", + "Rachelle", + "Rachna", + "Racquel", + "Rada", + "Radha", + "Radhika", + "Rae", + "Rafaela", + "Rafaelina", + "Raffaela", + "Raffaella", + "Rahel", + "Rai", + "Raina", + "Raine", + "Raisa", + "Raizy", + "Rakhee", + "Ramona", + "Ramya", + "Randee", + "Randi", + "Randie", + "Rani", + "Rania", + "Raquel", + "Rasa", + "Rasha", + "Rasheda", + "Rasheeda", + "Rasheedah", + "Rashel", + "Rashi", + "Rashida", + "Rashidah", + "Rashmi", + "Raven", + "Ravit", + "Raya", + "Rayna", + "Raysa", + "Rea", + "Reagan", + "Reba", + "Rebeca", + "Rebecca", + "Rebekah", + "Rebekka", + "Reem", + "Reema", + "Reena", + "Regina", + "Regine", + "Rehana", + "Reiko", + "Reina", + "Rekha", + "Rena", + "Renae", + "Renate", + "Renee", + "Renita", + "Renu", + "Renáta", + "Reshma", + "Reva", + "Revital", + "Reyna", + "Rhea", + "Rhiannon", + "Rhina", + "Rhoda", + "Rhona", + "Rhonda", + "Ria", + "Rica", + "Richa", + "Richelle", + "Ricki", + "Rie", + "Rika", + "Riki", + "Rikki", + "Rima", + "Rimma", + "Rina", + "Rinat", + "Risa", + "Rita", + "Ritu", + "Ritz", + "Riva", + "Rivka", + "Rivki", + "Rivky", + "Roberta", + "Robin", + "Robyn", + "Rochel", + "Rochelle", + "Rocío", + "Roe", + "Rohini", + "Rolanda", + "Roma", + "Romana", + "Romi", + "Romina", + "Romona", + "Romy", + "Rona", + "Ronda", + "Roni", + "Ronica", + "Ronit", + "Ronni", + "Rori", + "Rosa", + "Rosalba", + "Rosalee", + "Rosalia", + "Rosalie", + "Rosalina", + "Rosalind", + "Rosalinda", + "Rosaline", + "Rosalyn", + "Rosamaria", + "Rosana", + "Rosangela", + "Rosann", + "Rosanna", + "Rosanne", + "Rosaria", + "Rosary", + "Rosaura", + "Rose", + "Roseann", + "Roseanna", + "Roseanne", + "Roseline", + "Roselle", + "Rosely", + "Roselyn", + "Rosemarie", + "Rosemary", + "Rosetta", + "Rosette", + "Rosey", + "Roshni", + "Rosi", + "Rosie", + "Rosina", + "Rosita", + "Roslyn", + "Rossana", + "Rossi", + "Rossy", + "Rosy", + "Roula", + "Rowena", + "Roxana", + "Roxann", + "Roxanna", + "Roxanne", + "Roxie", + "Roxy", + "Roya", + "Roz", + "Roza", + "Rubi", + "Rubina", + "Ruby", + "Ruchi", + "Rupa", + "Rupi", + "Ruth", + "Ruthie", + "Ruthy", + "Ryann", + "Ryoko", + "Räñå", + "Saadia", + "Saba", + "Sabah", + "Sabina", + "Sabine", + "Sabrena", + "Sabrina", + "Sachi", + "Sadaf", + "Sade", + "Sadia", + "Sadie", + "Safia", + "Safiya", + "Sagrario", + "Sahar", + "Saida", + "Saima", + "Saira", + "Sakina", + "Sakinah", + "Sakura", + "Salima", + "Salina", + "Sallie", + "Sally", + "Sallyann", + "Salma", + "Salome", + "Samanta", + "Samantha", + "Samar", + "Samara", + "Sameera", + "Samia", + "Samina", + "Samira", + "Sammi", + "Sana", + "Sanam", + "Sandee", + "Sandhya", + "Sandi", + "Sandra", + "Sandrine", + "Sandy", + "Sanela", + "Sangeeta", + "Sania", + "Saniya", + "Sanja", + "Sanjana", + "Sanna", + "Santa", + "Sanya", + "Saori", + "Sapna", + "Sapphire", + "Sara", + "Sarah", + "Sarai", + "Saran", + "Saray", + "Sari", + "Sarika", + "Sarina", + "Sarit", + "Sarita", + "Sasha", + "Saskia", + "Satoko", + "Saundra", + "Savannah", + "Savita", + "Savitri", + "Sayaka", + "Scarlet", + "Scarlett", + "Scarlette", + "Seda", + "Seema", + "Seeta", + "Seiko", + "Sejal", + "Selena", + "Selene", + "Selin", + "Selina", + "Selma", + "Sena", + "Senada", + "Sera", + "Serena", + "Serene", + "Serenity", + "Serina", + "Serita", + "Serra", + "Shabana", + "Shabnam", + "Shade", + "Shadia", + "Shae", + "Shahida", + "Shaila", + "Shaina", + "Shakeema", + "Shakema", + "Shakia", + "Shakila", + "Shakima", + "Shakina", + "Shakira", + "Shala", + "Shaleen", + "Shalini", + "Shalonda", + "Shama", + "Shamara", + "Shamecca", + "Shameeka", + "Shameika", + "Shameka", + "Shamika", + "Shana", + "Shanae", + "Shanay", + "Shanaya", + "Shanaz", + "Shanda", + "Shanea", + "Shanee", + "Shaneka", + "Shanel", + "Shanell", + "Shanelle", + "Shanequa", + "Shanette", + "Shani", + "Shania", + "Shanice", + "Shaniece", + "Shanika", + "Shanikqua", + "Shaniqua", + "Shanique", + "Shanise", + "Shanita", + "Shanna", + "Shannan", + "Shannel", + "Shannon", + "Shanta", + "Shantae", + "Shantal", + "Shantay", + "Shante", + "Shantel", + "Shantell", + "Shantelle", + "Shanti", + "Shaquana", + "Shaquanna", + "Shar", + "Shara", + "Sharae", + "Sharay", + "Sharda", + "Sharday", + "Sharee", + "Shareen", + "Shareena", + "Shareese", + "Sharell", + "Sharene", + "Sharese", + "Shari", + "Sharice", + "Sharifa", + "Sharine", + "Sharise", + "Sharisse", + "Sharita", + "Sharla", + "Sharleen", + "Sharlene", + "Sharmaine", + "Sharmila", + "Sharmin", + "Sharon", + "Sharona", + "Sharonda", + "Sharron", + "Sharyn", + "Shasha", + "Shatima", + "Shatisha", + "Shauna", + "Shaunda", + "Shaunna", + "Shauntay", + "Shaunte", + "Shavon", + "Shavonne", + "Shawana", + "Shawanda", + "Shawanna", + "Shawna", + "Shawnee", + "Shawnette", + "Shawntay", + "Shayla", + "Shayna", + "Shazia", + "She", + "Sheba", + "Sheena", + "Sheetal", + "Shefali", + "Sheila", + "Sheilah", + "Shelby", + "Shelia", + "Shell", + "Shelley", + "Shelli", + "Shellie", + "Shelly", + "Shelly-Ann", + "Shellyann", + "Shenelle", + "Shenequa", + "Shenika", + "Sheniqua", + "Shera", + "Sheree", + "Shereen", + "Sherelle", + "Sherene", + "Sherese", + "Sheri", + "Sherica", + "Sherice", + "Sherika", + "Sherine", + "Sherise", + "Sherley", + "Sherly", + "Sherma", + "Sheron", + "Sherrell", + "Sherri", + "Sherrie", + "Sherry", + "Sherryann", + "Sheryl", + "Shifra", + "Shiho", + "Shikha", + "Shilpa", + "Shilpi", + "Shiny", + "Shira", + "Shiran", + "Shireen", + "Shiri", + "Shirin", + "Shirlene", + "Shirley", + "Shivani", + "Shivon", + "Shlomit", + "Shoko", + "Shona", + "Shonda", + "Shonette", + "Shonna", + "Shoshana", + "Shoshanna", + "Shoshi", + "Shpresa", + "Shreya", + "Shruti", + "Shuang", + "Shuchi", + "Shuli", + "Shweta", + "Shy", + "Sia", + "Sian", + "Sibel", + "Sibyl", + "Sidra", + "Sienna", + "Sierra", + "Sigal", + "Sigi", + "Sigrid", + "Silke", + "Silvana", + "Silvia", + "Silvina", + "Sima", + "Simi", + "Simmone", + "Simona", + "Simone", + "Simran", + "Sindy", + "Sinead", + "Siobhan", + "Sirena", + "Siri", + "Sisi", + "Sissy", + "Sita", + "Sivan", + "Skye", + "Smadar", + "Smiley", + "Smita", + "Smitha", + "Sneha", + "Socorro", + "Sofia", + "Sofie", + "Sofiya", + "Sofya", + "Solange", + "Sole", + "Soledad", + "Sona", + "Sonal", + "Sonali", + "Sondra", + "Soni", + "Sonia", + "Soniya", + "Sonja", + "Sonya", + "Soo", + "Soojin", + "Sophia", + "Sophie", + "Sora", + "Soraya", + "Sparkle", + "Spring", + "Stacey", + "Stacey-Ann", + "Staceyann", + "Staci", + "Stacia", + "Stacie", + "Stacy", + "Stacy-Ann", + "Stacyann", + "Star", + "Starr", + "Stasia", + "Stef", + "Stefani", + "Stefania", + "Stefanie", + "Stefany", + "Steffi", + "Stela", + "Stella", + "Steph", + "Stephani", + "Stephania", + "Stephanie", + "Stephany", + "Stephenie", + "Stephy", + "Stina", + "Stormy", + "Su", + "Subrina", + "Sudha", + "Sue", + "Sugar", + "Sui", + "Sujata", + "Sujin", + "Suki", + "Sultana", + "Suly", + "Summer", + "Sunita", + "Sunshine", + "Supriya", + "Suri", + "Susan", + "Susana", + "Susanna", + "Susannah", + "Susanne", + "Susi", + "Susie", + "Susy", + "Suzan", + "Suzana", + "Suzanna", + "Suzanne", + "Suzette", + "Suzi", + "Suzie", + "Suzy", + "Suzzette", + "Sveta", + "Svetlana", + "Swati", + "Sweet", + "Sweta", + "Swetha", + "Sybil", + "Sydelle", + "Sydney", + "Syeda", + "Sylvia", + "Sylvie", + "Sylwia", + "Symone", + "Synthia", + "Syreeta", + "Szilvia", + "Tabatha", + "Tabitha", + "Tahira", + "Tahirah", + "Tahisha", + "Taina", + "Taisha", + "Takako", + "Takia", + "Tali", + "Talia", + "Talisa", + "Talisha", + "Talya", + "Tamar", + "Tamara", + "Tameeka", + "Tameika", + "Tameka", + "Tamesha", + "Tami", + "Tamia", + "Tamica", + "Tamieka", + "Tamika", + "Tamiko", + "Tamisha", + "Tammi", + "Tammie", + "Tammy", + "Tamra", + "Tana", + "Tanaya", + "Taneisha", + "Tanesha", + "Tania", + "Taniesha", + "Tanika", + "Taniqua", + "Tanisha", + "Tanishia", + "Tanja", + "Tanvi", + "Tanya", + "Tara", + "Tarah", + "Tarika", + "Tarra", + "Tarsha", + "Taryn", + "Tash", + "Tasha", + "Tashana", + "Tashauna", + "Tashawna", + "Tashia", + "Tashika", + "Tasia", + "Tata", + "Tati", + "Tatiana", + "Tatjana", + "Tatyana", + "Tavia", + "Tawana", + "Tawanda", + "Tawanna", + "Tea", + "Teal", + "Teena", + "Tegan", + "Teisha", + "Temitope", + "Tene", + "Tenesha", + "Tenika", + "Tenisha", + "Tennille", + "Tera", + "Teresa", + "Terese", + "Teresita", + "Tereza", + "Teri", + "Terra", + "Terri", + "Terri-Ann", + "Terrie", + "Terryann", + "Tesha", + "Tess", + "Tessa", + "Tessie", + "Tetyana", + "Thais", + "Thalia", + "Thao", + "Thea", + "Theda", + "Thelma", + "Theodora", + "Theresa", + "Therese", + "Thomasina", + "Tia", + "Tiana", + "Tianna", + "Tiara", + "Tierney", + "Tierra", + "Tiesha", + "Tiff", + "Tiffani", + "Tiffanie", + "Tiffany", + "Tijuana", + "Tina", + "Tinamarie", + "Ting", + "Tingting", + "Tish", + "Tisha", + "Tita", + "Titi", + "Tiziana", + "Tobey", + "Toi", + "Tola", + "Tomoko", + "Toni", + "Toni-Ann", + "Tonia", + "Toniann", + "Tonisha", + "Tonya", + "Topaz", + "Tori", + "Toria", + "Tosha", + "Toula", + "Tova", + "Tovah", + "Towana", + "Toya", + "Toyin", + "Tracee", + "Tracey", + "Traci", + "Tracie", + "Tracy", + "Tran", + "Treasure", + "Tressa", + "Tricia", + "Trina", + "Trinidad", + "Trinity", + "Trish", + "Trisha", + "Trixie", + "Trudi", + "Trudy", + "Twana", + "Twanna", + "Tyeisha", + "Tyesha", + "Tyisha", + "Tynisha", + "Tyra", + "Tzippy", + "Tzivia", + "Ulrike", + "Uma", + "Una", + "Unique", + "Ursula", + "Urszula", + "Urvashi", + "Usha", + "Uzma", + "Valarie", + "Valbona", + "Valencia", + "Valentina", + "Valeria", + "Valerie", + "Valeriya", + "Valrie", + "Vanda", + "Vandana", + "Vanesa", + "Vanessa", + "Vania", + "Vanita", + "Vanity", + "Vanna", + "Vannessa", + "Varsha", + "Vashti", + "Vasiliki", + "Veda", + "Vee", + "Veena", + "Velma", + "Velvet", + "Venecia", + "Venera", + "Venessa", + "Venetia", + "Venice", + "Venus", + "Vera", + "Vered", + "Verena", + "Verna", + "Vernell", + "Vero", + "Verona", + "Veronika", + "Veronique", + "Verónica", + "Vesna", + "Vickey", + "Vicki", + "Vickie", + "Vicky", + "Victoria", + "Vida", + "Vidya", + "Vielka", + "Vienna", + "Viki", + "Vikki", + "Viktoria", + "Viktoriya", + "Vilma", + "Vina", + "Vincenza", + "Vinette", + "Vinita", + "Viola", + "Violet", + "Violeta", + "Violetta", + "Violette", + "Virginia", + "Virginie", + "Vita", + "Viv", + "Viva", + "Vivi", + "Vivian", + "Viviana", + "Viviane", + "Vivien", + "Vivienne", + "Vlada", + "Vonetta", + "Voula", + "Vy", + "Waleska", + "Wanda", + "Wendi", + "Wendie", + "Wendy", + "Whitney", + "Wilda", + "Wilhelmina", + "Willa", + "Willow", + "Wilma", + "Windy", + "Winifred", + "Winnie", + "Winsome", + "Wioletta", + "Xena", + "Xenia", + "Xia", + "Ximena", + "Xiomara", + "Xue", + "Yadira", + "Yael", + "Yaffa", + "Yafit", + "Yahaira", + "Yajaira", + "Yali", + "Yalitza", + "Yamileth", + "Yana", + "Yanet", + "Yani", + "Yanick", + "Yanina", + "Yanique", + "Yanira", + "Yaniris", + "Yanna", + "Yara", + "Yaritza", + "Yary", + "Yarí", + "Yasemin", + "Yashira", + "Yasmeen", + "Yasmin", + "Yasmina", + "Yasmine", + "Yaya", + "Yazmin", + "Yecenia", + "Yehudis", + "Yejin", + "Yekaterina", + "Yelena", + "Yelitza", + "Yen", + "Yeni", + "Yenifer", + "Yenny", + "Yesenia", + "Yessenia", + "Yessica", + "Yetunde", + "Yevgeniya", + "Yin", + "Ying", + "Yira", + "Yocasta", + "Yogita", + "Yohanna", + "Yohanny", + "Yokasta", + "Yoko", + "Yola", + "Yolanda", + "Yolande", + "Yoly", + "Yomaira", + "Yomayra", + "Yona", + "Yonit", + "Yoo", + "Yoojin", + "Yoon", + "Yoselin", + "Youjin", + "Yudelka", + "Yuen", + "Yuka", + "Yukari", + "Yuki", + "Yukiko", + "Yuko", + "Yulia", + "Yuliana", + "Yuliya", + "Yumi", + "Yumiko", + "Yun", + "Yuna", + "Yuni", + "Yvelisse", + "Yvette", + "Yvonne", + "Zahra", + "Zaida", + "Zainab", + "Zaira", + "Zakia", + "Zakiya", + "Zakiyyah", + "Zamira", + "Zana", + "Zandra", + "Zara", + "Zarina", + "Zehra", + "Zeina", + "Zelda", + "Zena", + "Zenaida", + "Zenia", + "Zenobia", + "Zeta", + "Zeynep", + "Zhanna", + "Zhenya", + "Zina", + "Zita", + "Zoe", + "Zoey", + "Zofia", + "Zoila", + "Zoraida", + "Zoraya", + "Zoya", + "Zsuzsanna", + "Zulay", + "Zuleika", + "Zulema", + "Zuleyka", + "Zulma", + "Zuzana", +] + +const _male_first_names: Array[String] = [ + "Aakash", + "Aamir", + "Aaron", + "Ab", + "Abba", + "Abbas", + "Abdel", + "Abdiel", + "Abdou", + "Abdoul", + "Abdoulaye", + "Abdul", + "Abdulla", + "Abdullah", + "Abe", + "Abel", + "Abey", + "Abhay", + "Abhi", + "Abhilash", + "Abhinav", + "Abhishek", + "Abid", + "Abie", + "Abir", + "Abner", + "Abraham", + "Abu", + "Aby", + "Ace", + "Achilles", + "Adalberto", + "Adam", + "Adams", + "Addis", + "Addison", + "Ade", + "Adebayo", + "Adeel", + "Adekunle", + "Adel", + "Adem", + "Ademola", + "Adewale", + "Adeyemi", + "Adham", + "Adiel", + "Adil", + "Adis", + "Aditya", + "Admir", + "Adnan", + "Adolfo", + "Adonis", + "Adriano", + "Adriel", + "Adrien", + "Ag", + "Agim", + "Agostino", + "Agron", + "Aharon", + "Ahmad", + "Ahmed", + "Ahmet", + "Ahsan", + "Aidan", + "Aiden", + "Ainsworth", + "Aj", + "Ajamu", + "Ajani", + "Ajay", + "Ajit", + "Ajith", + "Ak", + "Akash", + "Akbar", + "Akeem", + "Akhil", + "Akihiro", + "Akil", + "Akim", + "Akin", + "Akira", + "Akis", + "Akiva", + "Akram", + "Akshay", + "Akwasi", + "Al", + "Alain", + "Alan", + "Alaric", + "Alban", + "Albert", + "Alberto", + "Albi", + "Albin", + "Albion", + "Alcides", + "Alden", + "Aldo", + "Aldrin", + "Alec", + "Alejandro", + "Alek", + "Aleks", + "Aleksandar", + "Aleksander", + "Aleksandr", + "Aleksey", + "Alen", + "Alessandro", + "Alessio", + "Alex", + "Alexander", + "Alexandr", + "Alexandre", + "Alexandros", + "Alexandru", + "Alexei", + "Alexey", + "Alexi", + "Alexie", + "Alfie", + "Alfonso", + "Alfred", + "Alfredo", + "Algernon", + "Ali", + "Alioune", + "Alistair", + "Allan", + "Allen", + "Allister", + "Allon", + "Alok", + "Alon", + "Alonso", + "Alonzo", + "Alp", + "Alper", + "Alpesh", + "Alphonse", + "Alphonso", + "Alston", + "Altaf", + "Altin", + "Alton", + "Alvaro", + "Alvin", + "Alwyn", + "Amado", + "Amadou", + "Aman", + "Amandeep", + "Amar", + "Amauri", + "Amaury", + "Ambrose", + "Amedeo", + "Ameer", + "Ameet", + "Amer", + "Americo", + "Amilcar", + "Amin", + "Amine", + "Amir", + "Amish", + "Amit", + "Amjad", + "Ammar", + "Amnon", + "Amol", + "Amos", + "Amr", + "Amrit", + "Anand", + "Anant", + "Anas", + "Anastasios", + "Anatoliy", + "Anatoly", + "Ande", + "Anders", + "Anderson", + "Andrae", + "Andre", + "Andreas", + "Andrei", + "Andrew", + "Andrews", + "Andrey", + "Andri", + "Andriy", + "Andry", + "Andrzej", + "Andrés", + "Andy", + "Angel", + "Angelo", + "Angus", + "Anibal", + "Anil", + "Aniruddha", + "Anis", + "Anish", + "Anjum", + "Ankit", + "Ankur", + "Ankush", + "Anoop", + "Ansel", + "Anselm", + "Anselmo", + "Anshul", + "Anson", + "Ant", + "Ante", + "Anthony", + "Antoine", + "Anton", + "Antonino", + "Antonio", + "Antonis", + "Antony", + "Antwan", + "Antwon", + "Anuj", + "Anup", + "Anurag", + "Anwar", + "Apurva", + "Aqeel", + "Ar", + "Aram", + "Aramis", + "Arash", + "Aravind", + "Arben", + "Archie", + "Arda", + "Ardian", + "Ardit", + "Argenis", + "Ari", + "Arian", + "Aric", + "Arie", + "Ariel", + "Arif", + "Arik", + "Aris", + "Aristides", + "Aristotle", + "Arjun", + "Arkadiy", + "Arkady", + "Arlen", + "Arman", + "Armand", + "Armando", + "Armani", + "Armel", + "Armen", + "Armin", + "Arnaldo", + "Arnaud", + "Arnel", + "Arnie", + "Arno", + "Arnold", + "Arnulfo", + "Aron", + "Arron", + "Arsalan", + "Arsen", + "Arsenio", + "Arshad", + "Arslan", + "Art", + "Artan", + "Artem", + "Arthur", + "Artie", + "Arto", + "Artur", + "Arturo", + "Arty", + "Arun", + "Arvin", + "Arvind", + "Aryan", + "Aryeh", + "Asad", + "Asaf", + "Asher", + "Ashish", + "Ashok", + "Ashraf", + "Ashton", + "Ashutosh", + "Ashwin", + "Asi", + "Asif", + "Asim", + "Aslam", + "Assaf", + "Aston", + "Atakan", + "Atef", + "Athanasios", + "Atiba", + "Atif", + "Atsushi", + "Attila", + "Attilio", + "Atul", + "Aubyn", + "Augie", + "August", + "Augustine", + "Augusto", + "Augustus", + "Aung", + "Aurelio", + "Austen", + "Austin", + "Avery", + "Avi", + "Aviad", + "Avinash", + "Avishai", + "Aviv", + "Avner", + "Avraham", + "Avrohom", + "Axel", + "Aydin", + "Ayhan", + "Ayinde", + "Ayman", + "Ayo", + "Ayodele", + "Azad", + "Azeem", + "Azhar", + "Azim", + "Aziz", + "Baba", + "Babak", + "Babatunde", + "Babu", + "Bah", + "Bakari", + "Bala", + "Baldwin", + "Bam", + "Bang", + "Banks", + "Bao", + "Bar", + "Barak", + "Baran", + "Baris", + "Barnett", + "Barney", + "Baron", + "Barrett", + "Barrington", + "Barron", + "Barry", + "Bart", + "Barton", + "Baruch", + "Basem", + "Bashir", + "Basil", + "Bass", + "Bassam", + "Bassem", + "Bastien", + "Batuhan", + "Baxter", + "Bayo", + "Bear", + "Beat", + "Beau", + "Bekim", + "Ben", + "Bender", + "Benedict", + "Beni", + "Benito", + "Benj", + "Benjamin", + "Benji", + "Benjie", + "Benjy", + "Bennett", + "Bennie", + "Benny", + "Benoit", + "Benson", + "Bentley", + "Berk", + "Bernard", + "Bernardo", + "Bernd", + "Bernhard", + "Bernie", + "Bert", + "Berto", + "Bertram", + "Bertrand", + "Besim", + "Besnik", + "Beto", + "Bharat", + "Bhavin", + "Biagio", + "Bienvenido", + "Bigg", + "Biggie", + "Biju", + "Bikram", + "Bilal", + "Bill", + "Bin", + "Bing", + "Binod", + "Binu", + "Binyomin", + "Bishop", + "Björn", + "Black", + "Blaine", + "Blaise", + "Blake", + "Blanco", + "Blas", + "Blaze", + "Bledar", + "Blerim", + "Blu", + "Bo", + "Boaz", + "Bob", + "Bobby", + "Bobo", + "Bogdan", + "Bojan", + "Bolivar", + "Bong", + "Boo", + "Boogie", + "Bora", + "Boris", + "Boruch", + "Bosco", + "Boss", + "Boyce", + "Brad", + "Braden", + "Bradford", + "Bradley", + "Brady", + "Brahim", + "Bram", + "Brandan", + "Branden", + "Brando", + "Brandon", + "Branko", + "Braulio", + "Brayan", + "Brayden", + "Brenden", + "Brendon", + "Brennan", + "Brent", + "Brenton", + "Bret", + "Brett", + "Brian", + "Brice", + "Brien", + "Bright", + "Brion", + "Broadway", + "Brock", + "Brody", + "Bronx", + "Brooks", + "Bruce", + "Bruno", + "Bryan", + "Bryant", + "Bryce", + "Bryon", + "Brëndan", + "Bubba", + "Buck", + "Bud", + "Buddha", + "Buddy", + "Budi", + "Buffalo", + "Burak", + "Burhan", + "Burke", + "Burt", + "Burton", + "Buster", + "Butch", + "Buzz", + "Byron", + "Bïlly", + "Bülent", + "Caesar", + "Cal", + "Cale", + "Caleb", + "Callum", + "Calogero", + "Calvin", + "Cam", + "Camden", + "Cameron", + "Camilo", + "Campbell", + "Can", + "Candido", + "Carl", + "Carlin", + "Carlito", + "Carlitos", + "Carlo", + "Carlos", + "Carlton", + "Carlyle", + "Carmelo", + "Carmine", + "Carrington", + "Carson", + "Carter", + "Cary", + "Cas", + "Cash", + "Casimir", + "Casper", + "Cassius", + "Cecil", + "Cecilio", + "Cedric", + "Cedrick", + "Celso", + "Cem", + "Cengiz", + "Cenk", + "Ceo", + "Cesare", + "Cezar", + "Chad", + "Chadwick", + "Chaim", + "Chaitanya", + "Chaka", + "Champ", + "Chan", + "Chance", + "Chandler", + "Chang", + "Chao", + "Charles", + "Charley", + "Charlie", + "Charlton", + "Charly", + "Chas", + "Chase", + "Chauncey", + "Chavez", + "Chaz", + "Che", + "Cheng", + "Chester", + "Chet", + "Chetan", + "Chi", + "Chico", + "Chike", + "Chin", + "Chinedu", + "Chino", + "Chintan", + "Chip", + "Chirag", + "Chris", + "Christian", + "Christo", + "Christoph", + "Christophe", + "Christopher", + "Christos", + "Chuck", + "Chuckie", + "Chucky", + "Chun", + "Chung", + "Ciaran", + "Cid", + "Cihan", + "Ciprian", + "Ciro", + "Cisco", + "Clarence", + "Clark", + "Clarke", + "Classic", + "Claude", + "Claudio", + "Clay", + "Clayton", + "Clem", + "Clement", + "Clemente", + "Cleon", + "Cletus", + "Cleve", + "Cleveland", + "Clif", + "Cliff", + "Clifford", + "Clifton", + "Clint", + "Clinton", + "Clive", + "Clyde", + "Coby", + "Cody", + "Colby", + "Cole", + "Coleman", + "Colin", + "Collin", + "Collins", + "Colm", + "Colton", + "Connor", + "Cono", + "Conor", + "Conrad", + "Conroy", + "Constantin", + "Constantine", + "Constantinos", + "Cool", + "Cooper", + "Corbin", + "Cordell", + "Corey", + "Cornel", + "Cornelius", + "Cornell", + "Corrado", + "Cortez", + "Corwin", + "Cory", + "Cosimo", + "Cosmo", + "Costa", + "Costas", + "Court", + "Craig", + "Crispin", + "Cristhian", + "Cristian", + "Cristiano", + "Cristobal", + "Cullen", + "Culture", + "Curt", + "Curtis", + "Cy", + "Cyril", + "Cyrus", + "César", + "Da", + "Dada", + "Dae", + "Daily", + "Daisuke", + "Dale", + "Dalibor", + "Dallas", + "Dalton", + "Daman", + "Damani", + "Damian", + "Damiano", + "Damien", + "Damion", + "Damir", + "Damon", + "Damone", + "Dan", + "Dane", + "Daniel", + "Danijel", + "Danilo", + "Danish", + "Danny", + "Dante", + "Dany", + "Daquan", + "Darell", + "Daren", + "Darian", + "Dariel", + "Darien", + "Darin", + "Dario", + "Darion", + "Darius", + "Dariusz", + "Dark", + "Darnel", + "Darnell", + "Daron", + "Darrel", + "Darrell", + "Darren", + "Darrin", + "Darron", + "Darryl", + "Darshan", + "Darwin", + "Daryl", + "Daryll", + "Dash", + "Dashaun", + "Dashawn", + "Dave", + "Davey", + "David", + "Davide", + "Davidson", + "Davie", + "Davin", + "Davis", + "Davon", + "Davy", + "Dawid", + "Dawud", + "Dax", + "Day", + "Dayne", + "Dayo", + "De", + "Dean", + "Deandre", + "Declan", + "Deejay", + "Deep", + "Deepak", + "Deion", + "Dejan", + "Delano", + "Dele", + "Dell", + "Delon", + "Delroy", + "Demetri", + "Demetrios", + "Demetrius", + "Demian", + "Demir", + "Demitri", + "Den", + "Denis", + "Dennis", + "Denny", + "Denton", + "Denver", + "Denys", + "Denzel", + "Denzil", + "Deon", + "Dequan", + "Dereck", + "Derek", + "Derick", + "Derik", + "Dermot", + "Deron", + "Derrick", + "Derron", + "Derwin", + "Deryck", + "Deshaun", + "Deshawn", + "Deshon", + "Desmond", + "Deuce", + "Dev", + "Devaughn", + "Deven", + "Devendra", + "Devin", + "Devon", + "Devonte", + "Dewayne", + "Dewey", + "Dexter", + "Dez", + "Dhaval", + "Dheeraj", + "Dhiren", + "Dhruv", + "Diallo", + "Dice", + "Dick", + "Didier", + "Diego", + "Dieter", + "Dijon", + "Dil", + "Dilip", + "Dillon", + "Dima", + "Dimas", + "Dimitar", + "Dimitri", + "Dimitrios", + "Dimitris", + "Dimitry", + "Din", + "Dinesh", + "Ding", + "Dino", + "Dio", + "Diogenes", + "Dion", + "Dionis", + "Dionisio", + "Dipak", + "Dirk", + "Divine", + "Dixon", + "Django", + "Dmitri", + "Dmitriy", + "Dmitry", + "Dmytro", + "Do", + "Doc", + "Dollar", + "Dom", + "Domenic", + "Domenick", + "Domenico", + "Domingo", + "Dominic", + "Dominick", + "Dominik", + "Domo", + "Don", + "Donal", + "Donald", + "Donatello", + "Donato", + "Donavan", + "Donavin", + "Donell", + "Dong", + "Doni", + "Donn", + "Donnell", + "Donnie", + "Donny", + "Donovan", + "Donte", + "Doran", + "Doron", + "Dorsey", + "Doug", + "Dougie", + "Douglas", + "Dov", + "Dovid", + "Doğan", + "Dragan", + "Dragos", + "Drake", + "Drama", + "Dre", + "Drew", + "Dritan", + "Dru", + "Duane", + "Dudley", + "Dudu", + "Dujon", + "Duke", + "Duncan", + "Duran", + "Durell", + "Durrell", + "Dusan", + "Dustin", + "Dusty", + "Dutch", + "Duwayne", + "Dwain", + "Dwaine", + "Dwayne", + "Dwight", + "Dylan", + "Eamon", + "Eamonn", + "Ean", + "Earl", + "Earle", + "Eazy", + "Eben", + "Ebenezer", + "Ed", + "Edan", + "Edd", + "Eddie", + "Eddy", + "Eder", + "Edgar", + "Edgard", + "Edgardo", + "Edge", + "Edi", + "Edin", + "Edison", + "Edmond", + "Edmund", + "Edmundo", + "Edo", + "Edouard", + "Edson", + "Eduard", + "Eduardo", + "Edward", + "Edwards", + "Edwin", + "Efe", + "Efraim", + "Efrain", + "Efren", + "Ehab", + "Eitan", + "Ej", + "El", + "Elad", + "Eladio", + "Elan", + "Elbert", + "Eldad", + "Eldar", + "Elder", + "Eldon", + "Eli", + "Elias", + "Elie", + "Eliezer", + "Elijah", + "Elio", + "Eliot", + "Eliran", + "Elis", + "Eliseo", + "Elkin", + "Ellery", + "Elliot", + "Elliott", + "Ellis", + "Ellison", + "Elmer", + "Elmo", + "Elon", + "Eloy", + "Elton", + "Elvin", + "Elvis", + "Emad", + "Emanuel", + "Emanuele", + "Emeka", + "Emerson", + "Emil", + "Emile", + "Emiliano", + "Emilio", + "Emin", + "Emir", + "Emmanuel", + "Emmet", + "Emmett", + "Emory", + "Emrah", + "Emre", + "Ender", + "Enes", + "Engels", + "Engin", + "Enis", + "Enmanuel", + "Enoch", + "Enrico", + "Enrique", + "Enzo", + "Eon", + "Ephraim", + "Eran", + "Ercan", + "Erdal", + "Erdem", + "Eren", + "Erez", + "Erhan", + "Eric", + "Erich", + "Erick", + "Erik", + "Erkan", + "Ermal", + "Ernest", + "Ernesto", + "Ernie", + "Ernst", + "Erol", + "Errol", + "Ervin", + "Erwin", + "Espezy", + "Esteban", + "Etan", + "Ethan", + "Etienne", + "Eugene", + "Eugenio", + "Eusebio", + "Evan", + "Evangelos", + "Evans", + "Evens", + "Ever", + "Everett", + "Everton", + "Evgeny", + "Ewan", + "Extra", + "Eyal", + "Eytan", + "Ez", + "Ezekiel", + "Ezequiel", + "Ezra", + "Fabien", + "Fabio", + "Fabián", + "Fabrice", + "Fabricio", + "Fabrizio", + "Fadi", + "Fady", + "Fahad", + "Faheem", + "Fahim", + "Faisal", + "Faiz", + "Faraz", + "Fareed", + "Farhad", + "Farhan", + "Farid", + "Faris", + "Farrukh", + "Faruk", + "Farzad", + "Fatih", + "Fatmir", + "Fatos", + "Faustino", + "Fausto", + "Fazal", + "Federico", + "Felipe", + "Felix", + "Femi", + "Feng", + "Ferdinand", + "Ferdinando", + "Ferhat", + "Fermin", + "Fernando", + "Fidel", + "Filip", + "Filipe", + "Filippo", + "Finest", + "Finn", + "Fish", + "Fitz", + "Fitzgerald", + "Fitzroy", + "Flash", + "Flat", + "Flavio", + "Fletcher", + "Flip", + "Florian", + "Florin", + "Floyd", + "Fly", + "Flynn", + "Forbes", + "Ford", + "Forest", + "Forrest", + "Foster", + "Fouad", + "Fox", + "Francesco", + "Francis", + "Francisco", + "Franck", + "Franco", + "Francois", + "Frank", + "Frankie", + "Franklin", + "Franklyn", + "Franky", + "Frantz", + "Franz", + "Fraser", + "Fred", + "Freddie", + "Freddy", + "Frederic", + "Frederick", + "Frederik", + "Fredric", + "Fredrick", + "Fredrik", + "Fredy", + "Free", + "French", + "Fritz", + "Fu", + "Fuad", + "Fuat", + "Furkan", + "Gab", + "Gabe", + "Gabino", + "Gabor", + "Gabriel", + "Gaetano", + "Gagan", + "Gagandeep", + "Gal", + "Galen", + "Galo", + "Gamal", + "Games", + "Ganesh", + "Gar", + "Gardy", + "Gareth", + "Garfield", + "Garland", + "Garnet", + "Garnett", + "Garret", + "Garrett", + "Garrick", + "Garry", + "Garth", + "Garvey", + "Garvin", + "Gary", + "Gaspar", + "Gasper", + "Gaston", + "Gaurav", + "Gautam", + "Gavin", + "Gavriel", + "Gbenga", + "Gee", + "Gen", + "Genaro", + "Gene", + "Gennadiy", + "Gennady", + "Gennaro", + "Geno", + "Geo", + "Geoff", + "Geoffrey", + "Georg", + "George", + "Georges", + "Georgi", + "Georgio", + "Geovanni", + "Geovanny", + "Ger", + "Gerald", + "Geraldo", + "Gerard", + "Gerardo", + "Germain", + "German", + "Gerome", + "Geronimo", + "Gerry", + "Gershon", + "Gerson", + "Get", + "Gezim", + "Ghulam", + "Giacomo", + "Gian", + "Giancarlo", + "Giancarlos", + "Gianfranco", + "Gianluca", + "Gianni", + "Gibran", + "Gideon", + "Gil", + "Gilad", + "Gilbert", + "Gilberto", + "Giles", + "Gill", + "Gilles", + "Gino", + "Gio", + "Giorgio", + "Giovanni", + "Giovanny", + "Girish", + "Giuliano", + "Giulio", + "Giuseppe", + "Gleb", + "Glen", + "Glenn", + "Glenroy", + "Glyn", + "Go", + "Godfrey", + "Godwin", + "Gonzalo", + "Good", + "Gopal", + "Gopi", + "Goran", + "Gordon", + "Grady", + "Graeme", + "Graham", + "Graig", + "Grant", + "Granville", + "Gray", + "Grayson", + "Greg", + "Gregg", + "Gregor", + "Gregorio", + "Gregory", + "Griffin", + "Grzegorz", + "Gucci", + "Gui", + "Guido", + "Guilherme", + "Guillaume", + "Guillermo", + "Gunnar", + "Guo", + "Gurpreet", + "Gus", + "Gustav", + "Gustave", + "Gustavo", + "Guy", + "Gyasi", + "Gökhan", + "Habeeb", + "Habib", + "Hadi", + "Hai", + "Haider", + "Haim", + "Hakan", + "Hakeem", + "Hakim", + "Hal", + "Halil", + "Hamad", + "Hamid", + "Hamilton", + "Hamish", + "Hamlet", + "Hammad", + "Hamza", + "Han", + "Hanif", + "Hank", + "Hannibal", + "Hans", + "Hansel", + "Hanson", + "Hany", + "Hao", + "Hardik", + "Hardy", + "Hari", + "Haris", + "Harish", + "Harlan", + "Harlem", + "Harold", + "Haroon", + "Harpreet", + "Harri", + "Harris", + "Harrison", + "Harry", + "Harsh", + "Harun", + "Harvey", + "Hasan", + "Hasani", + "Hashim", + "Hassan", + "Hatem", + "Haywood", + "Hazem", + "He", + "Heath", + "Helio", + "Hemal", + "Hemant", + "Hendrik", + "Heng", + "Henning", + "Henri", + "Henrik", + "Henrique", + "Henry", + "Herb", + "Herbert", + "Herbie", + "Herby", + "Hercules", + "Heriberto", + "Herman", + "Hermes", + "Herminio", + "Hernando", + "Hernán", + "Hersh", + "Hershey", + "Herve", + "Hesham", + "Heshy", + "Hezekiah", + "Hicham", + "Hideki", + "Hillel", + "Hilton", + "Himanshu", + "Hin", + "Hing", + "Hiram", + "Hiren", + "Hiro", + "Hiroki", + "Hiroshi", + "Hiroyuki", + "Hitesh", + "Ho", + "Hoi", + "Holden", + "Holger", + "Homer", + "Hon", + "Hood", + "Hoon", + "Hopeton", + "Horace", + "Horacio", + "Howard", + "Howie", + "Hoàng", + "Hua", + "Hubert", + "Hudson", + "Hue", + "Huey", + "Hugh", + "Hugo", + "Humayun", + "Humberto", + "Humza", + "Hung", + "Hunter", + "Huseyin", + "Hussain", + "Hussein", + "Hy", + "Hymie", + "Hyung", + "Héctor", + "Iain", + "Iam", + "Ian", + "Ibrahima", + "Idan", + "Ido", + "Idris", + "Igal", + "Iggy", + "Ignacio", + "Ignatius", + "Ignazio", + "Igor", + "Ihab", + "Ike", + "Ikenna", + "Ikey", + "Il", + "Ilan", + "Ilhan", + "Ilir", + "Ill", + "Ilya", + "Ilyas", + "Imad", + "Imran", + "Imtiaz", + "Ioannis", + "Iqbal", + "Ira", + "Irfan", + "Irv", + "Irvin", + "Irving", + "Irwin", + "Isaac", + "Isaiah", + "Isaias", + "Ishmael", + "Isiah", + "Islam", + "Ismael", + "Ismet", + "Israel", + "Issac", + "Itai", + "Italo", + "Itamar", + "Itay", + "Itzik", + "Ivo", + "Ivor", + "Iván", + "Iwan", + "Izzy", + "Ja", + "Jabari", + "Jace", + "Jacek", + "Jack", + "Jackson", + "Jacky", + "Jacob", + "Jacopo", + "Jacques", + "Jad", + "Jaden", + "Jae", + "Jaewon", + "Jah", + "Jahid", + "Jahmal", + "Jahmel", + "Jai", + "Jair", + "Jairo", + "Jaison", + "Jaja", + "Jake", + "Jakob", + "Jakub", + "Jalen", + "Jalil", + "Jam", + "Jamaal", + "Jamal", + "Jamar", + "Jame", + "Jameel", + "Jamel", + "Jamell", + "James", + "Jameson", + "Jamey", + "Jamil", + "Jamin", + "Jamison", + "Janusz", + "Jaquan", + "Jarad", + "Jared", + "Jarek", + "Jarel", + "Jarell", + "Jarett", + "Jarod", + "Jaron", + "Jarred", + "Jarret", + "Jarrett", + "Jarrod", + "Jarvis", + "Jasen", + "Jason", + "Jasper", + "Jaspreet", + "Jatin", + "Javan", + "Javed", + "Javi", + "Javid", + "Javier", + "Javon", + "Jawad", + "Jay", + "Jayden", + "Jayquan", + "Jaysen", + "Jayson", + "Jean-Claude", + "Jean-Louis", + "Jean-Luc", + "Jean-Michel", + "Jean-Paul", + "Jean-Pierre", + "Jeb", + "Jed", + "Jeet", + "Jef", + "Jeff", + "Jefferson", + "Jeffery", + "Jeffrey", + "Jeffry", + "Jeffy", + "Jelani", + "Jemal", + "Jemel", + "Jens", + "Jensen", + "Jerald", + "Jerard", + "Jerel", + "Jerell", + "Jeremiah", + "Jeremias", + "Jeremy", + "Jermain", + "Jermaine", + "Jermel", + "Jerome", + "Jerrell", + "Jerson", + "Jesper", + "Jesse", + "Jesús", + "Jet", + "Jevon", + "Jey", + "Jhon", + "Jhonatan", + "Jhonathan", + "Jhonny", + "Jhony", + "Jian", + "Jiang", + "Jide", + "Jigar", + "Jignesh", + "Jim", + "Jimbo", + "Jimi", + "Jimmie", + "Jimmy", + "Jinal", + "Jitendra", + "Joaquim", + "Joaquín", + "Job", + "Joe", + "Joel", + "Joeseph", + "Joey", + "Joffre", + "Johan", + "Johann", + "Johannes", + "John", + "John-Paul", + "Johnathan", + "Johnathon", + "Johnnie", + "Johnny", + "Johnpaul", + "Johnson", + "Johny", + "Jomo", + "Jon", + "Jon-Paul", + "Jonah", + "Jonas", + "Jonatan", + "Jonathan", + "Jonathon", + "Jonel", + "Jong", + "Jonny", + "Jony", + "Joon", + "Jordan", + "Jordi", + "Jordon", + "Jorel", + "Jorge", + "Jos", + "Josef", + "Joselito", + "Joseph", + "Josh", + "Joshua", + "Josiah", + "Josias", + "Josip", + "Josuë", + "José", + "Jourdan", + "Jovan", + "Jovani", + "Jovanny", + "Jovany", + "Jozef", + "João", + "Juan", + "Juancarlos", + "Juanito", + "Jud", + "Judah", + "Judd", + "Jude", + "Juergen", + "Juice", + "Julian", + "Juliano", + "Julien", + "Julio", + "Julius", + "Jun", + "Junaid", + "Junior", + "Junito", + "Jurgen", + "Just", + "Justice", + "Justin", + "Justino", + "Justo", + "Justus", + "Jérry", + "Jérémie", + "Kaan", + "Kabir", + "Kadeem", + "Kadir", + "Kahlil", + "Kai", + "Kal", + "Kaleb", + "Kalvin", + "Kalyan", + "Kam", + "Kamal", + "Kamar", + "Kamau", + "Kamel", + "Kamen", + "Kameron", + "Kamil", + "Kamran", + "Kan", + "Kane", + "Kang", + "Kapil", + "Karan", + "Kareem", + "Karel", + "Karim", + "Karl", + "Karlo", + "Karlos", + "Karsten", + "Karthik", + "Kartik", + "Kaseem", + "Kash", + "Kashif", + "Kasper", + "Kaushal", + "Kaushik", + "Kayode", + "Kaz", + "Kazi", + "Keane", + "Kedar", + "Keegan", + "Keenan", + "Kei", + "Keir", + "Keith", + "Kelby", + "Kelechi", + "Kellan", + "Kellen", + "Kelvin", + "Kemal", + "Kemar", + "Ken", + "Kenan", + "Kendel", + "Kendell", + "Kendrick", + "Kenichi", + "Kenji", + "Kenn", + "Kennedy", + "Kenneth", + "Kenny", + "Kenrick", + "Kent", + "Kenta", + "Kentaro", + "Kenyatta", + "Kenyon", + "Keon", + "Kerby", + "Kerem", + "Kermit", + "Kern", + "Keron", + "Kerron", + "Kervin", + "Kerwin", + "Kester", + "Keston", + "Ketan", + "Kev", + "Kevan", + "Keven", + "Kevin", + "Kevon", + "Kevyn", + "Key", + "Keyon", + "Keyur", + "Kfir", + "Khaled", + "Khaleel", + "Khalid", + "Khalifa", + "Khalil", + "Khan", + "Khari", + "Khris", + "Khurram", + "Kiel", + "Kieran", + "Kike", + "Kiko", + "Kimani", + "Kin", + "King", + "Kingsley", + "Kip", + "Kirby", + "Kiril", + "Kirill", + "Kirk", + "Kirkland", + "Kirt", + "Kishan", + "Kishore", + "Klaus", + "Kleber", + "Klever", + "Knight", + "Ko", + "Kobe", + "Kobi", + "Koby", + "Kofi", + "Koji", + "Kojo", + "Kola", + "Kong", + "Konrad", + "Konstantin", + "Konstantinos", + "Kool", + "Koray", + "Korey", + "Kory", + "Kosta", + "Kostas", + "Kosuke", + "Kotaro", + "Kramer", + "Kreshnik", + "Kris", + "Krish", + "Krishna", + "Kristian", + "Kristof", + "Kristofer", + "Kristoffer", + "Kristopher", + "Krunal", + "Krystian", + "Krzysztof", + "Kubilay", + "Kujtim", + "Kumar", + "Kun", + "Kunal", + "Kunle", + "Kurt", + "Kurtis", + "Kush", + "Kushtrim", + "Kwabena", + "Kwadwo", + "Kwaku", + "Kwame", + "Kwan", + "Kwang", + "Kwasi", + "Kwesi", + "Kwok", + "Ky", + "Kyaw", + "Kyle", + "Kyron", + "Kyung", + "Labinot", + "Lam", + "Lamar", + "Lamarr", + "Lambert", + "Lamin", + "Lamont", + "Lance", + "Lancelot", + "Landon", + "Lane", + "Langston", + "Lanny", + "Lanre", + "Laquan", + "Laron", + "Larry", + "Lars", + "Laszlo", + "Lateef", + "Latif", + "Laurence", + "Laurent", + "Lauro", + "Lavon", + "Lawrence", + "Layton", + "Lazar", + "Lazaro", + "Leandro", + "Lee", + "Lefty", + "Leiby", + "Leif", + "Leighton", + "Lekan", + "Leland", + "Lemuel", + "Len", + "Lenard", + "Lenin", + "Lennie", + "Lennon", + "Lennox", + "Lenny", + "Lenworth", + "Leo", + "Leon", + "Leonardo", + "Leonel", + "Leonid", + "Leonidas", + "Leopold", + "Leopoldo", + "Leor", + "Leron", + "Leroy", + "Les", + "Lester", + "Lev", + "Levan", + "Levar", + "Levent", + "Levi", + "Levon", + "Levy", + "Lew", + "Lewis", + "Lex", + "Liam", + "Liang", + "Lincoln", + "Linden", + "Lino", + "Linton", + "Linus", + "Lionel", + "Lior", + "Liran", + "Lisandro", + "Lito", + "Livingston", + "Livio", + "Lloyd", + "Lobsang", + "Logan", + "Lon", + "Long", + "Lonnie", + "Lonny", + "Lord", + "Lorenzo", + "Lorne", + "Los", + "Lou", + "Louie", + "Louis", + "Lovell", + "Lowell", + "Luan", + "Luc", + "Luca", + "Lucas", + "Lucian", + "Luciano", + "Lucien", + "Lucio", + "Lucius", + "Lucky", + "Ludovic", + "Lui", + "Luigi", + "Luiz", + "Lukas", + "Lukasz", + "Luke", + "Luther", + "Luís", + "Lyle", + "Lyndon", + "Léonard", + "Maarten", + "Mac", + "Macho", + "Maciej", + "Maciek", + "Mack", + "Macky", + "Mad", + "Magdy", + "Maged", + "Magnus", + "Mahdi", + "Mahendra", + "Maher", + "Mahesh", + "Mahmood", + "Mahmoud", + "Mahmud", + "Mahmut", + "Maine", + "Majed", + "Majid", + "Major", + "Makoto", + "Maksim", + "Mal", + "Malachi", + "Malcolm", + "Malek", + "Malick", + "Malik", + "Malvin", + "Mamadou", + "Mamun", + "Man", + "Mandeep", + "Manfred", + "Manhattan", + "Manish", + "Manjit", + "Manny", + "Manoj", + "Manolo", + "Manpreet", + "Mansoor", + "Manu", + "Manuel", + "Maor", + "Marat", + "Marc", + "Marcel", + "Marcelino", + "Marcell", + "Marcello", + "Marcellus", + "Marcelo", + "Marcial", + "Marcin", + "Marcio", + "Marco", + "Marcos", + "Marcus", + "Marek", + "Mariano", + "Marino", + "Mario", + "Marios", + "Marius", + "Mariusz", + "Mark", + "Markie", + "Marko", + "Markos", + "Markus", + "Marlon", + "Marques", + "Marquis", + "Marquise", + "Marshall", + "Martin", + "Martinez", + "Marty", + "Marv", + "Marvelous", + "Marvin", + "Marwan", + "Masahiro", + "Masaki", + "Mason", + "Masood", + "Massimiliano", + "Massimo", + "Masud", + "Masum", + "Mat", + "Matan", + "Mateo", + "Mateusz", + "Mathew", + "Mathias", + "Mathieu", + "Matt", + "Matteo", + "Matthew", + "Matthias", + "Matthieu", + "Mattia", + "Matty", + "Matías", + "Maurice", + "Mauricio", + "Maurizio", + "Mauro", + "Maury", + "Maverick", + "Max", + "Maxime", + "Maximilian", + "Maximiliano", + "Maximillian", + "Maximo", + "Maximus", + "Maxwell", + "Maxx", + "Maxím", + "Mayank", + "Mayer", + "Mayur", + "Mazen", + "Mega", + "Mehdi", + "Mehmet", + "Mehul", + "Meir", + "Mel", + "Melvin", + "Menachem", + "Mendel", + "Mendy", + "Meng", + "Merlin", + "Mert", + "Merv", + "Mervin", + "Mervyn", + "Mesut", + "Mete", + "Metin", + "Meyer", + "Mic", + "Micah", + "Michael", + "Michaelangelo", + "Micheal", + "Michel", + "Michelangelo", + "Michiel", + "Mick", + "Mickael", + "Mickel", + "Mickey", + "Micky", + "Mihai", + "Mihir", + "Mikael", + "Mike", + "Mikel", + "Mikey", + "Mikhail", + "Miko", + "Milan", + "Milen", + "Miles", + "Miller", + "Milo", + "Milos", + "Milton", + "Minh", + "Minister", + "Mir", + "Mirko", + "Miroslav", + "Mirsad", + "Mirza", + "Misael", + "Mista", + "Mitch", + "Mitchel", + "Mitchell", + "Mitesh", + "Mo", + "Modesto", + "Moe", + "Mohamad", + "Mohamed", + "Mohammad", + "Mohammed", + "Mohan", + "Mohd", + "Mohit", + "Mohsin", + "Moises", + "Moishe", + "Mon", + "Monte", + "Montgomery", + "Monti", + "Monty", + "Moo", + "Moose", + "Mordecai", + "Mordechai", + "Mordy", + "Moreno", + "Moritz", + "Morris", + "Morton", + "Morty", + "Moses", + "Moshe", + "Mostafa", + "Moti", + "Mounir", + "Mourad", + "Moussa", + "Moustafa", + "Moustapha", + "Muhamed", + "Muhammad", + "Muhammed", + "Mukesh", + "Mukund", + "Munir", + "Murad", + "Murat", + "Murray", + "Murtaza", + "Musa", + "Mustafa", + "Mustapha", + "Myke", + "Myles", + "Myron", + "Mïgûél", + "Nabeel", + "Nabil", + "Nacho", + "Nadav", + "Nadeem", + "Nader", + "Nadim", + "Nadir", + "Naeem", + "Naftali", + "Nahum", + "Naim", + "Naj", + "Najee", + "Nalin", + "Nam", + "Naman", + "Nando", + "Naoki", + "Napoleon", + "Naquan", + "Narayan", + "Narciso", + "Narendra", + "Naresh", + "Nas", + "Nash", + "Nasir", + "Nasser", + "Nat", + "Natale", + "Natan", + "Nate", + "Nathan", + "Nathanael", + "Nathaniel", + "Nauman", + "Naveed", + "Naveen", + "Navin", + "Nazir", + "Nazmul", + "Neal", + "Ned", + "Neel", + "Neeraj", + "Neftali", + "Neil", + "Neill", + "Nelson", + "Nemo", + "Nenad", + "Neo", + "Ness", + "Nestor", + "Netanel", + "Neville", + "Nevin", + "New", + "Newton", + "Niall", + "Nic", + "Nicholas", + "Nick", + "Nickolas", + "Nico", + "Nicolai", + "Nicolo", + "Nicolás", + "Nigel", + "Nii", + "Nik", + "Nikhil", + "Nikko", + "Niklas", + "Niko", + "Nikola", + "Nikolai", + "Nikolaos", + "Nikolas", + "Nikolay", + "Nikos", + "Nile", + "Niles", + "Nilesh", + "Nils", + "Nimesh", + "Nimrod", + "Nino", + "Nir", + "Niraj", + "Nirav", + "Nishant", + "Nishit", + "Nissim", + "Nitin", + "Nitzan", + "Nixon", + "Nkosi", + "Nnamdi", + "No", + "Noah", + "Noam", + "Noble", + "Noe", + "Noel", + "Nolan", + "Noman", + "Norbert", + "Norberto", + "Norm", + "Norman", + "Norris", + "Nour", + "Nuno", + "Nunzio", + "Nuri", + "Ny", + "Nyc", + "Nyu", + "Obi", + "Obinna", + "Octavio", + "Odell", + "Ofer", + "Ofir", + "Oguz", + "Ohad", + "Okan", + "Oktay", + "Olaf", + "Olando", + "Ole", + "Oleg", + "Oli", + "Oliver", + "Olivier", + "Olu", + "Olufemi", + "Om", + "Omar", + "Omari", + "Omer", + "Omid", + "Omri", + "Oneal", + "Oneil", + "Onur", + "Or", + "Oral", + "Orel", + "Oren", + "Orestes", + "Orhan", + "Ori", + "Orin", + "Orion", + "Orlando", + "Orrin", + "Orson", + "Orville", + "Osama", + "Osei", + "Oshane", + "Osiris", + "Oskar", + "Osman", + "Osvaldo", + "Oswald", + "Oswaldo", + "Otis", + "Otto", + "Oumar", + "Ousmane", + "Owen", + "Oz", + "Ozan", + "Ozzie", + "Ozzy", + "Pa", + "Paa", + "Pablo", + "Paco", + "Paddy", + "Palmer", + "Pan", + "Panagiotis", + "Pankaj", + "Panos", + "Pantelis", + "Paolo", + "Papa", + "Pape", + "Papi", + "Papito", + "Papo", + "Parag", + "Paresh", + "Parker", + "Parth", + "Partha", + "Pasang", + "Pascal", + "Pascual", + "Pasha", + "Pasquale", + "Pastor", + "Patel", + "Patric", + "Patricio", + "Patrick", + "Patrik", + "Patryk", + "Paul", + "Paulie", + "Paulo", + "Pavan", + "Pavel", + "Pavlo", + "Pawan", + "Pawel", + "Pedro", + "Peng", + "Pepe", + "Percy", + "Pernell", + "Perry", + "Petar", + "Pete", + "Peter", + "Peterson", + "Petey", + "Petr", + "Petros", + "Phil", + "Philip", + "Philipp", + "Philippe", + "Phill", + "Phillip", + "Philly", + "Pierce", + "Piero", + "Pierre", + "Pieter", + "Pietro", + "Pinny", + "Piotr", + "Pito", + "Piyush", + "Polo", + "Pop", + "Porfirio", + "Power", + "Pradeep", + "Prakash", + "Pranav", + "Prasad", + "Prasanna", + "Prashant", + "Prashanth", + "Prateek", + "Pratik", + "Praveen", + "Pravin", + "Prem", + "Preston", + "Price", + "Prince", + "Przemyslaw", + "Puneet", + "Punit", + "Qamar", + "Qasim", + "Qiang", + "Qin", + "Quan", + "Quang", + "Que", + "Quentin", + "Quincy", + "Quintin", + "Quinton", + "Ra", + "Rabbi", + "Rachid", + "Rad", + "Radames", + "Radhames", + "Radu", + "Raf", + "Rafa", + "Rafael", + "Rafal", + "Raffaele", + "Raffi", + "Raffy", + "Rafi", + "Rafiq", + "Raghav", + "Raghu", + "Rah", + "Raheem", + "Rahiem", + "Rahim", + "Rahman", + "Rahmel", + "Rahsaan", + "Rahul", + "Rainer", + "Rainier", + "Raj", + "Raja", + "Rajan", + "Rajat", + "Rajeev", + "Rajendra", + "Rajesh", + "Rajib", + "Rajiv", + "Raju", + "Rakesh", + "Ralf", + "Ralph", + "Ralphie", + "Ralphy", + "Ralston", + "Ram", + "Raman", + "Ramazan", + "Ramel", + "Ramell", + "Ramesh", + "Ramez", + "Rami", + "Ramin", + "Ramiro", + "Ramon", + "Ramone", + "Ramsay", + "Ramses", + "Ramsey", + "Ramy", + "Ramzy", + "Ran", + "Rand", + "Randal", + "Randall", + "Randell", + "Randolph", + "Randy", + "Ranjit", + "Rany", + "Raoul", + "Raphael", + "Ras", + "Rasean", + "Rashaad", + "Rashaan", + "Rashad", + "Rashard", + "Rashaun", + "Rashawn", + "Rasheed", + "Rasheem", + "Rasheen", + "Rashid", + "Rashon", + "Ravi", + "Ravin", + "Ravinder", + "Ravindra", + "Rawle", + "Ray", + "Rayan", + "Raymon", + "Raymond", + "Raymundo", + "Raynard", + "Rayon", + "Rayshawn", + "Raz", + "Raza", + "Raúl", + "Real", + "Rebel", + "Red", + "Reda", + "Reece", + "Reed", + "Reg", + "Reggie", + "Reginald", + "Regis", + "Rehan", + "Reid", + "Reinaldo", + "Rell", + "Remi", + "Remon", + "Remy", + "Renaldo", + "Renan", + "Renard", + "Renato", + "Rene", + "Renny", + "Reno", + "Renzo", + "Reuben", + "Reuel", + "Reuven", + "Rex", + "Rey", + "Reyes", + "Reynaldo", + "Reynold", + "Reza", + "Rhys", + "Riad", + "Rian", + "Riaz", + "Ric", + "Ricardo", + "Riccardo", + "Rich", + "Richard", + "Richardson", + "Richie", + "Richmond", + "Richy", + "Rick", + "Rickey", + "Ricky", + "Rico", + "Ridwan", + "Rifat", + "Rigo", + "Rigoberto", + "Rik", + "Riley", + "Rino", + "Rip", + "Rishabh", + "Rishi", + "Ritchie", + "Rizwan", + "Rob", + "Robb", + "Robbie", + "Robby", + "Robert", + "Roberto", + "Robinson", + "Robson", + "Roby", + "Rocco", + "Rocko", + "Rocky", + "Rod", + "Roddy", + "Roderick", + "Rodger", + "Rodolfo", + "Rodrigo", + "Roee", + "Rogelio", + "Roger", + "Rohan", + "Rohit", + "Roi", + "Roland", + "Rolando", + "Rolf", + "Romain", + "Roman", + "Romano", + "Rome", + "Romel", + "Romeo", + "Romero", + "Rommel", + "Ron", + "Ronak", + "Ronald", + "Ronaldo", + "Ronan", + "Rondell", + "Ronen", + "Rong", + "Ronn", + "Ronnell", + "Ronnie", + "Ronny", + "Rony", + "Roody", + "Roosevelt", + "Roque", + "Rory", + "Roscoe", + "Rosendo", + "Roshan", + "Ross", + "Rostislav", + "Rowan", + "Roy", + "Royal", + "Royce", + "Rubens", + "Rubin", + "Rubén", + "Ruddy", + "Rudi", + "Rudolf", + "Rudolph", + "Rudy", + "Ruel", + "Rufus", + "Ruhul", + "Rui", + "Rupert", + "Rush", + "Ruslan", + "Russ", + "Russel", + "Russell", + "Rustam", + "Rusty", + "Ryan", + "Ryo", + "Ryota", + "Ryu", + "Ródney", + "Saad", + "Sabin", + "Sachin", + "Sadik", + "Saeed", + "Safet", + "Safi", + "Safraz", + "Sagar", + "Sahil", + "Said", + "Saif", + "Saiful", + "Saint", + "Sajal", + "Sajid", + "Sal", + "Salah", + "Saleem", + "Saleh", + "Salem", + "Salih", + "Salim", + "Salman", + "Salomon", + "Salvador", + "Salvatore", + "Salvo", + "Sam", + "Sameer", + "Sameh", + "Samer", + "Samet", + "Sami", + "Samir", + "Samit", + "Samm", + "Sammy", + "Sampath", + "Samson", + "Samuel", + "Samy", + "San", + "Sanchez", + "Sandeep", + "Sander", + "Sandip", + "Sandor", + "Sandro", + "Sanford", + "Sang", + "Sanjay", + "Sanjeev", + "Santhosh", + "Santi", + "Santiago", + "Santino", + "Santo", + "Santos", + "Santosh", + "Sapan", + "Sarwar", + "Sasa", + "Sascha", + "Sat", + "Satish", + "Satoshi", + "Saud", + "Saul", + "Saurabh", + "Saverio", + "Savvas", + "Sayed", + "Scooter", + "Scot", + "Scott", + "Scottie", + "Scotty", + "Sead", + "Seamus", + "Sean", + "Seb", + "Sebastian", + "Sebastiano", + "Sebastien", + "Sedat", + "Segun", + "Sekou", + "Selcuk", + "Selim", + "Selwyn", + "Semih", + "Sen", + "Senad", + "Seneca", + "Seon", + "Serdar", + "Serge", + "Sergei", + "Sergey", + "Sergio", + "Serkan", + "Seth", + "Seung", + "Seymour", + "Shaan", + "Shabazz", + "Shachar", + "Shad", + "Shadi", + "Shadrack", + "Shady", + "Shah", + "Shahar", + "Shahed", + "Shahid", + "Shahriar", + "Shahzad", + "Shai", + "Shailesh", + "Shain", + "Shaka", + "Shakeel", + "Shakeem", + "Shakil", + "Shakim", + "Shakir", + "Shalom", + "Shamar", + "Shamari", + "Shameek", + "Shameer", + "Shamel", + "Shamil", + "Shamim", + "Shamir", + "Shane", + "Shankar", + "Shantanu", + "Shao", + "Shaquille", + "Sharad", + "Sharath", + "Shareef", + "Sharif", + "Shariff", + "Sharod", + "Shashank", + "Shaul", + "Shaun", + "Shawn", + "Shayan", + "Sheik", + "Sheikh", + "Sheldon", + "Shelton", + "Shem", + "Sheng", + "Sherard", + "Sheraz", + "Sherif", + "Sherman", + "Sherwin", + "Shiloh", + "Shimi", + "Shimon", + "Shinya", + "Shiraz", + "Shiv", + "Shivam", + "Shlomi", + "Shlomo", + "Shmuel", + "Sho", + "Shoaib", + "Sholom", + "Shomari", + "Shon", + "Shotta", + "Showtime", + "Shun", + "Shyam", + "Si", + "Sid", + "Siddharth", + "Sidney", + "Sigmund", + "Silas", + "Silvestre", + "Silvio", + "Sim", + "Simba", + "Simcha", + "Simeon", + "Simo", + "Simón", + "Sinan", + "Sincere", + "Sion", + "Sixto", + "Skender", + "Skip", + "Sky", + "Skyler", + "Slava", + "Slawomir", + "Slick", + "Sly", + "Sneaker", + "Snoopy", + "Socrates", + "Sohail", + "Sohan", + "Sohel", + "Sokol", + "Sola", + "Solomon", + "Son", + "Soner", + "Sonny", + "Sonu", + "Soren", + "Sourav", + "Sparky", + "Spencer", + "Spike", + "Spiro", + "Spiros", + "Spyros", + "Sree", + "Sridhar", + "Srikanth", + "Srinivas", + "Sriram", + "Sruly", + "Stalin", + "Stan", + "Stanford", + "Stanislav", + "Stanley", + "Stanton", + "Stas", + "Stash", + "Stavros", + "Stay", + "Steeve", + "Stefan", + "Stefano", + "Stefanos", + "Steffan", + "Steffen", + "Steffon", + "Stefon", + "Stelios", + "Stephane", + "Stephen", + "Stephon", + "Sterling", + "Stevan", + "Steve", + "Steven", + "Stevens", + "Stevenson", + "Stewart", + "Stone", + "Storm", + "Street", + "Stu", + "Stuart", + "Stéphan", + "Sudesh", + "Sudhir", + "Sujan", + "Sujit", + "Suk", + "Sule", + "Sulejman", + "Suleyman", + "Sultan", + "Suman", + "Sumeet", + "Sumit", + "Sumon", + "Sundeep", + "Sungmin", + "Sunil", + "Sunny", + "Supa", + "Suraj", + "Surendra", + "Suresh", + "Sushil", + "Sven", + "Swapnil", + "Sy", + "Syd", + "Syed", + "Sylvain", + "Sylvester", + "Taco", + "Tad", + "Tae", + "Tafari", + "Tag", + "Tah", + "Taha", + "Tahir", + "Tahsin", + "Taimur", + "Taj", + "Taji", + "Taka", + "Takashi", + "Takeshi", + "Takuya", + "Tal", + "Talal", + "Talha", + "Tamas", + "Tamer", + "Tamir", + "Tan", + "Taner", + "Tang", + "Tanner", + "Tanveer", + "Tanvir", + "Tao", + "Tapan", + "Taras", + "Tarek", + "Tareq", + "Tarik", + "Tariq", + "Taro", + "Tarrell", + "Tarun", + "Tate", + "Taurean", + "Taye", + "Tayo", + "Taz", + "Ted", + "Tedd", + "Teddy", + "Teejay", + "Tejas", + "Telly", + "Tenzin", + "Teo", + "Terell", + "Terence", + "Teron", + "Terrance", + "Terrel", + "Terrell", + "Terrence", + "Terron", + "Tesfa", + "Tevin", + "Tevon", + "Tha", + "Thad", + "Thaddeus", + "Thai", + "Thanh", + "The", + "Theo", + "Theodore", + "Theophilus", + "Theron", + "Thiago", + "Thierry", + "Thom", + "Thomas", + "Thompson", + "Thor", + "Thupten", + "Tiago", + "Tibor", + "Tiger", + "Tim", + "Timmy", + "Timo", + "Timothy", + "Timur", + "Tino", + "Tito", + "Titus", + "Tobias", + "Tod", + "Todd", + "Tolga", + "Tom", + "Tomasz", + "Tomer", + "Tommaso", + "Tommy", + "Tomo", + "Tomás", + "Tone", + "Toney", + "Tong", + "Tonny", + "Tony", + "Topher", + "Tor", + "Torres", + "Torrey", + "Toshi", + "Trace", + "Travis", + "Travon", + "Tre", + "Tremaine", + "Tremayne", + "Trent", + "Trenton", + "Tres", + "Trevon", + "Trevor", + "Trey", + "Tri", + "Trip", + "Tripp", + "Tris", + "Tristan", + "Tristian", + "Troy", + "Truman", + "Trung", + "Tsering", + "Tu", + "Tuan", + "Tucker", + "Tudor", + "Tuna", + "Tunde", + "Tushar", + "Ty", + "Tye", + "Tyler", + "Tyquan", + "Tyree", + "Tyreek", + "Tyrel", + "Tyrell", + "Tyrese", + "Tyron", + "Tyrone", + "Tyshawn", + "Tyson", + "Tzvi", + "Uche", + "Uday", + "Udi", + "Ufuk", + "Ugo", + "Uli", + "Ulises", + "Ulrich", + "Ulysses", + "Umair", + "Umar", + "Umberto", + "Umer", + "Umesh", + "Umit", + "Umut", + "Uri", + "Uriah", + "Uriel", + "Usman", + "Utku", + "Uwe", + "Uzair", + "Uzi", + "Uğur", + "Vadim", + "Vaibhav", + "Valentin", + "Valentino", + "Valerio", + "Vamsi", + "Van", + "Vance", + "Varun", + "Vasili", + "Vasilios", + "Vasilis", + "Vasiliy", + "Vaughn", + "Vedat", + "Vega", + "Venu", + "Vern", + "Vernon", + "Vic", + "Vicente", + "Vick", + "Vidal", + "Vijay", + "Vik", + "Vikas", + "Vikash", + "Vikram", + "Viktor", + "Vimal", + "Vin", + "Vinay", + "Vince", + "Vincent", + "Vincenzo", + "Vineet", + "Vinh", + "Vinit", + "Vinnie", + "Vinny", + "Vinod", + "Vinson", + "Vipin", + "Vipul", + "Viraj", + "Virgil", + "Virgilio", + "Vishal", + "Vishnu", + "Vitaliy", + "Vitaly", + "Vito", + "Vittorio", + "Vivek", + "Vlad", + "Vladimir", + "Vladislav", + "Volkan", + "Volodymyr", + "Von", + "Vu", + "Víctor", + "Wade", + "Wael", + "Wagner", + "Wah", + "Wahid", + "Waldemar", + "Waldo", + "Waldy", + "Wale", + "Waleed", + "Walid", + "Walker", + "Wallace", + "Wally", + "Walt", + "Walter", + "Wang", + "Waqas", + "Ward", + "Warner", + "Warren", + "Waseem", + "Washington", + "Wayne", + "We", + "Webster", + "Wei", + "Wellington", + "Wendell", + "Werner", + "Wes", + "Wesley", + "Westley", + "Weston", + "Whit", + "White", + "Wil", + "Wilber", + "Wilbert", + "Wilberto", + "Wilbur", + "Wild", + "Wilder", + "Wiley", + "Wilfred", + "Wilfredo", + "Wilfrido", + "Wiliam", + "Wilkins", + "Will", + "Willam", + "Willard", + "Willem", + "William", + "Willie", + "Willis", + "Willy", + "Wilmer", + "Wilson", + "Wilton", + "Win", + "Winson", + "Winston", + "Wissam", + "Wojciech", + "Wojtek", + "Wolf", + "Wolfgang", + "Won", + "Wong", + "Woo", + "Woodley", + "Woodrow", + "Woody", + "Wu", + "Wyatt", + "Wylie", + "Wynn", + "Xander", + "Xavier", + "Xin", + "Xu", + "Yaakov", + "Yahya", + "Yair", + "Yakov", + "Yale", + "Yamil", + "Yamin", + "Yancy", + "Yaniv", + "Yanky", + "Yann", + "Yanni", + "Yannick", + "Yaron", + "Yaroslav", + "Yash", + "Yasha", + "Yasin", + "Yasir", + "Yasser", + "Yassin", + "Yassine", + "Yaw", + "Yaşar", + "Yechiel", + "Yefim", + "Yehuda", + "Yehudah", + "Yeon", + "Yevgeniy", + "Yianni", + "Yiannis", + "Yigal", + "Yilmaz", + "Yinka", + "Yisroel", + "Yitz", + "Yitzchok", + "Yitzy", + "Ylli", + "Yoann", + "Yoav", + "Yoel", + "Yoeli", + "Yoely", + "Yogesh", + "Yogi", + "Yohan", + "Yomi", + "Yonatan", + "Yong", + "Yoni", + "Yoram", + "York", + "Yosef", + "Yoshi", + "Yosi", + "Yossi", + "Yosuke", + "Yotam", + "You", + "Younes", + "Young", + "Yousef", + "Youssef", + "Yuji", + "Yung", + "Yunus", + "Yuriy", + "Yury", + "Yusef", + "Yusuf", + "Yusuke", + "Yuuki", + "Yuval", + "Yves", + "Zac", + "Zach", + "Zachariah", + "Zachary", + "Zack", + "Zafar", + "Zafer", + "Zaheer", + "Zahid", + "Zahir", + "Zaid", + "Zain", + "Zak", + "Zakaria", + "Zaki", + "Zakir", + "Zalman", + "Zane", + "Zap", + "Zee", + "Zeeshan", + "Zeke", + "Zeljko", + "Zeshan", + "Zeus", + "Zev", + "Zhe", + "Zhi", + "Zhuo", + "Ziad", + "Ziggy", + "Zion", + "Zohaib", + "Zohar", + "Zoltán", + "Zoran", + "Zsolt", + "Zubair", + "Zvi" +] + +const _last_names: Array[String] = [ + "Aaberg", + "Aalst", + "Aara", + "Aaren", + "Aarika", + "Aaron", + "Aaronson", + "Ab", + "Aba", + "Abad", + "Abagael", + "Abagail", + "Abana", + "Abate", + "Abba", + "Abbate", + "Abbe", + "Abbey", + "Abbi", + "Abbie", + "Abbot", + "Abbotsen", + "Abbotson", + "Abbotsun", + "Abbott", + "Abbottson", + "Abby", + "Abbye", + "Abdel", + "Abdella", + "Abdu", + "Abdul", + "Abdulla", + "Abe", + "Abebi", + "Abel", + "Abelard", + "Abell", + "Abercromby", + "Abernathy", + "Abernon", + "Abert", + "Abeu", + "Abey", + "Abie", + "Abigael", + "Abigail", + "Abigale", + "Abijah", + "Abisha", + "Abisia", + "Abixah", + "Abner", + "Aborn", + "Abott", + "Abra", + "Abraham", + "Abrahams", + "Abrahamsen", + "Abrahan", + "Abram", + "Abramo", + "Abrams", + "Abramson", + "Abran", + "Abroms", + "Absa", + "Absalom", + "Abshier", + "Acacia", + "Acalia", + "Accalia", + "Ace", + "Acey", + "Acherman", + "Achilles", + "Achorn", + "Acie", + "Acima", + "Acker", + "Ackerley", + "Ackerman", + "Ackler", + "Ackley", + "Acquah", + "Acus", + "Ad", + "Ada", + "Adabel", + "Adabelle", + "Adachi", + "Adah", + "Adaha", + "Adai", + "Adaiha", + "Adair", + "Adal", + "Adala", + "Adalai", + "Adalard", + "Adalbert", + "Adalheid", + "Adali", + "Adalia", + "Adaliah", + "Adalie", + "Adaline", + "Adall", + "Adallard", + "Adam", + "Adama", + "Adamec", + "Adamek", + "Adamik", + "Adamina", + "Adaminah", + "Adamis", + "Adamo", + "Adamok", + "Adams", + "Adamsen", + "Adamski", + "Adamson", + "Adamsun", + "Adan", + "Adao", + "Adar", + "Adara", + "Adaurd", + "Aday", + "Adda", + "Addam", + "Addi", + "Addia", + "Addie", + "Addiego", + "Addiel", + "Addis", + "Addison", + "Addy", + "Ade", + "Adebayo", + "Adel", + "Adela", + "Adelaida", + "Adelaide", + "Adelaja", + "Adelbert", + "Adele", + "Adelheid", + "Adelia", + "Adelice", + "Adelina", + "Adelind", + "Adeline", + "Adella", + "Adelle", + "Adelpho", + "Adelric", + "Adena", + "Ader", + "Adest", + "Adey", + "Adham", + "Adhamh", + "Adhern", + "Adi", + "Adiana", + "Adiel", + "Adiell", + "Adigun", + "Adila", + "Adim", + "Adin", + "Adina", + "Adine", + "Adis", + "Adkins", + "Adlai", + "Adlar", + "Adlare", + "Adlay", + "Adlee", + "Adlei", + "Adler", + "Adley", + "Adna", + "Adnah", + "Adne", + "Adnopoz", + "Ado", + "Adolf", + "Adolfo", + "Adolph", + "Adolphe", + "Adolpho", + "Adolphus", + "Adon", + "Adonis", + "Adora", + "Adore", + "Adoree", + "Adorl", + "Adorne", + "Adrea", + "Adrell", + "Adria", + "Adriaens", + "Adrial", + "Adrian", + "Adriana", + "Adriane", + "Adrianna", + "Adrianne", + "Adriano", + "Adriel", + "Adriell", + "Adrien", + "Adriena", + "Adriene", + "Adrienne", + "Adur", + "Aekerly", + "Aelber", + "Aenea", + "Aeneas", + "Aeneus", + "Aeniah", + "Aenneea", + "Aeriel", + "Aeriela", + "Aeriell", + "Affer", + "Affra", + "Affrica", + "Afra", + "Africa", + "Africah", + "Afrika", + "Afrikah", + "Afton", + "Ag", + "Agace", + "Agamemnon", + "Agan", + "Agata", + "Agate", + "Agatha", + "Agathe", + "Agathy", + "Agbogla", + "Agee", + "Aggappe", + "Aggappera", + "Aggappora", + "Aggarwal", + "Aggi", + "Aggie", + "Aggri", + "Aggy", + "Agle", + "Agler", + "Agna", + "Agnella", + "Agnes", + "Agnese", + "Agnesse", + "Agneta", + "Agnew", + "Agnola", + "Agostino", + "Agosto", + "Agretha", + "Agripina", + "Agrippina", + "Aguayo", + "Agueda", + "Aguie", + "Aguste", + "Agustin", + "Ahab", + "Aharon", + "Ahasuerus", + "Ahders", + "Ahearn", + "Ahern", + "Ahl", + "Ahlgren", + "Ahmad", + "Ahmar", + "Ahmed", + "Ahola", + "Aholah", + "Aholla", + "Ahoufe", + "Ahouh", + "Ahrendt", + "Ahrens", + "Ahron", + "Aia", + "Aida", + "Aidan", + "Aiden", + "Aiello", + "Aigneis", + "Aiken", + "Aila", + "Ailbert", + "Aile", + "Ailee", + "Aileen", + "Ailene", + "Ailey", + "Aili", + "Ailin", + "Ailina", + "Ailis", + "Ailsa", + "Ailssa", + "Ailsun", + "Ailyn", + "Aime", + "Aimee", + "Aimil", + "Aimo", + "Aindrea", + "Ainslee", + "Ainsley", + "Ainslie", + "Ainsworth", + "Airel", + "Aires", + "Airla", + "Airlee", + "Airlia", + "Airliah", + "Airlie", + "Aisha", + "Ajani", + "Ajax", + "Ajay", + "Ajit", + "Akanke", + "Akel", + "Akela", + "Aker", + "Akerboom", + "Akerley", + "Akers", + "Akeyla", + "Akeylah", + "Akili", + "Akim", + "Akin", + "Akins", + "Akira", + "Aklog", + "Aksel", + "Aksoyn", + "Al", + "Alabaster", + "Alage", + "Alain", + "Alaine", + "Alair", + "Alake", + "Alameda", + "Alan", + "Alana", + "Alanah", + "Aland", + "Alane", + "Alanna", + "Alano", + "Alansen", + "Alanson", + "Alard", + "Alaric", + "Alarice", + "Alarick", + "Alarise", + "Alasdair", + "Alastair", + "Alasteir", + "Alaster", + "Alatea", + "Alathia", + "Alayne", + "Alba", + "Alban", + "Albarran", + "Albemarle", + "Alben", + "Alber", + "Alberic", + "Alberik", + "Albers", + "Albert", + "Alberta", + "Albertina", + "Albertine", + "Alberto", + "Albertson", + "Albie", + "Albin", + "Albina", + "Albion", + "Alboran", + "Albrecht", + "Albric", + "Albright", + "Albur", + "Alburg", + "Alburga", + "Alby", + "Alcina", + "Alcine", + "Alcinia", + "Alcock", + "Alcot", + "Alcott", + "Alcus", + "Alda", + "Aldarcie", + "Aldarcy", + "Aldas", + "Alded", + "Alden", + "Aldercy", + "Alderman", + "Alderson", + "Aldin", + "Aldis", + "Aldo", + "Aldon", + "Aldora", + "Aldos", + "Aldous", + "Aldred", + "Aldredge", + "Aldric", + "Aldrich", + "Aldridge", + "Alduino", + "Aldus", + "Aldwin", + "Aldwon", + "Alec", + "Alecia", + "Aleck", + "Aleda", + "Aleece", + "Aleedis", + "Aleen", + "Aleetha", + "Alegre", + "Alejandra", + "Alejandrina", + "Alejandro", + "Alejo", + "Alejoa", + "Alek", + "Aleksandr", + "Alena", + "Alene", + "Alenson", + "Aleras", + "Aleris", + "Aleron", + "Alesandrini", + "Alessandra", + "Alessandro", + "Aleta", + "Aletha", + "Alethea", + "Alethia", + "Aletta", + "Alex", + "Alexa", + "Alexander", + "Alexandr", + "Alexandra", + "Alexandre", + "Alexandria", + "Alexandrina", + "Alexandro", + "Alexandros", + "Alexei", + "Alexi", + "Alexia", + "Alexina", + "Alexine", + "Alexio", + "Alexis", + "Aley", + "Aleydis", + "Alf", + "Alfeus", + "Alfi", + "Alfie", + "Alfons", + "Alfonse", + "Alfonso", + "Alfonzo", + "Alford", + "Alfred", + "Alfreda", + "Alfredo", + "Alfy", + "Algar", + "Alger", + "Algernon", + "Algie", + "Alguire", + "Algy", + "Ali", + "Alia", + "Aliber", + "Alic", + "Alica", + "Alice", + "Alicea", + "Alicia", + "Alick", + "Alida", + "Alidia", + "Alidis", + "Alidus", + "Alie", + "Alika", + "Alikee", + "Alina", + "Aline", + "Alinna", + "Alis", + "Alisa", + "Alisan", + "Alisander", + "Alisen", + "Alisha", + "Alisia", + "Alison", + "Alissa", + "Alistair", + "Alister", + "Alisun", + "Alita", + "Alitha", + "Alithea", + "Alithia", + "Alitta", + "Alius", + "Alix", + "Aliza", + "Alla", + "Allain", + "Allan", + "Allana", + "Allanson", + "Allard", + "Allare", + "Allayne", + "Allbee", + "Allcot", + "Alleen", + "Allegra", + "Allen", + "Allene", + "Alleras", + "Allerie", + "Alleris", + "Allerus", + "Alley", + "Alleyn", + "Alleyne", + "Alli", + "Allianora", + "Alliber", + "Allie", + "Allin", + "Allina", + "Allis", + "Allisan", + "Allison", + "Allissa", + "Allista", + "Allister", + "Allistir", + "Allix", + "Allmon", + "Allred", + "Allrud", + "Allsopp", + "Allsun", + "Allveta", + "Allwein", + "Allx", + "Ally", + "Allyce", + "Allyn", + "Allys", + "Allyson", + "Alma", + "Almallah", + "Almeda", + "Almeeta", + "Almeida", + "Almena", + "Almeria", + "Almeta", + "Almira", + "Almire", + "Almita", + "Almond", + "Almund", + "Alo", + "Alodee", + "Alodi", + "Alodie", + "Aloin", + "Aloise", + "Aloisia", + "Aloisius", + "Aloke", + "Alon", + "Alonso", + "Alonzo", + "Aloysia", + "Aloysius", + "Alper", + "Alpers", + "Alpert", + "Alphard", + "Alpheus", + "Alphonsa", + "Alphonse", + "Alphonsine", + "Alphonso", + "AlrZc", + "Alric", + "Alrich", + "Alrick", + "Alroi", + "Alroy", + "Also", + "Alston", + "Alsworth", + "Alta", + "Altaf", + "Alten", + "Althea", + "Althee", + "Altheta", + "Altis", + "Altman", + "Alton", + "Aluin", + "Aluino", + "Alurd", + "Alurta", + "Alva", + "Alvan", + "Alvar", + "Alvarez", + "Alver", + "Alvera", + "Alverson", + "Alverta", + "Alves", + "Alveta", + "Alviani", + "Alvie", + "Alvin", + "Alvina", + "Alvinia", + "Alvira", + "Alvis", + "Alvita", + "Alvord", + "Alvy", + "Alwin", + "Alwitt", + "Alwyn", + "Alyce", + "Alyda", + "Alyose", + "Alyosha", + "Alys", + "Alysa", + "Alyse", + "Alysia", + "Alyson", + "Alysoun", + "Alyss", + "Alyssa", + "Alyworth", + "Ama", + "Amabel", + "Amabelle", + "Amabil", + "Amadas", + "Amadeo", + "Amadeus", + "Amadis", + "Amado", + "Amador", + "Amadus", + "Amal", + "Amalbena", + "Amalberga", + "Amalbergas", + "Amalburga", + "Amalea", + "Amalee", + "Amaleta", + "Amalia", + "Amalie", + "Amalita", + "Amalle", + "Aman", + "Amand", + "Amanda", + "Amandi", + "Amandie", + "Amando", + "Amandy", + "Amann", + "Amar", + "Amara", + "Amaral", + "Amaras", + "Amarette", + "Amargo", + "Amari", + "Amarillas", + "Amarillis", + "Amaris", + "Amary", + "Amaryl", + "Amaryllis", + "Amasa", + "Amata", + "Amathist", + "Amathiste", + "Amati", + "Amato", + "Amatruda", + "Amaty", + "Amber", + "Amberly", + "Ambert", + "Ambie", + "Amble", + "Ambler", + "Ambrogino", + "Ambrogio", + "Ambros", + "Ambrosane", + "Ambrose", + "Ambrosi", + "Ambrosia", + "Ambrosine", + "Ambrosio", + "Ambrosius", + "Ambur", + "Amby", + "Ame", + "Amedeo", + "Amelia", + "Amelie", + "Amelina", + "Ameline", + "Amelita", + "Amena", + "Amend", + "Amerigo", + "Amero", + "Amersham", + "Amery", + "Ames", + "Amethist", + "Amethyst", + "Ami", + "Amias", + "Amice", + "Amick", + "Amie", + "Amiel", + "Amieva", + "Amii", + "Amil", + "Amin", + "Aminta", + "Amir", + "Amitie", + "Amity", + "Amling", + "Ammadas", + "Ammadis", + "Ammamaria", + "Ammann", + "Ammon", + "Amoakuh", + "Amor", + "Amora", + "Amoreta", + "Amorete", + "Amorette", + "Amorita", + "Amoritta", + "Amory", + "Amos", + "Amr", + "Amrita", + "Amsden", + "Amund", + "Amy", + "Amyas", + "Amye", + "Am�lie", + "An", + "Ana", + "Anabal", + "Anabel", + "Anabella", + "Anabelle", + "Anagnos", + "Analiese", + "Analise", + "Anallese", + "Anallise", + "Anana", + "Ananna", + "Anastas", + "Anastase", + "Anastasia", + "Anastasie", + "Anastasio", + "Anastasius", + "Anastassia", + "Anastatius", + "Anastice", + "Anastos", + "Anatol", + "Anatola", + "Anatole", + "Anatolio", + "Anatollo", + "Ancalin", + "Ancel", + "Ancelin", + "Anceline", + "Ancell", + "Anchie", + "Ancier", + "Ancilin", + "Andee", + "Andeee", + "Andel", + "Ander", + "Anderea", + "Anderegg", + "Anderer", + "Anders", + "Andersen", + "Anderson", + "Andert", + "Andi", + "Andie", + "Andonis", + "Andra", + "Andrade", + "Andras", + "Andre", + "Andrea", + "Andreana", + "Andreas", + "Andree", + "Andrei", + "Andrej", + "Andrel", + "Andres", + "Andrew", + "Andrews", + "Andrey", + "Andri", + "Andria", + "Andriana", + "Andrien", + "Andriette", + "Andris", + "Andromache", + "Andromada", + "Andromeda", + "Andromede", + "Andros", + "Androw", + "Andrus", + "Andryc", + "Andy", + "Anestassia", + "Anet", + "Anett", + "Anetta", + "Anette", + "Aney", + "Angadreme", + "Angadresma", + "Ange", + "Angel", + "Angela", + "Angele", + "Angeli", + "Angelia", + "Angelica", + "Angelico", + "Angelika", + "Angelina", + "Angeline", + "Angelique", + "Angelis", + "Angelita", + "Angell", + "Angelle", + "Angelo", + "Angi", + "Angie", + "Angil", + "Angle", + "Anglim", + "Anglo", + "Angrist", + "Angus", + "Angy", + "Anh", + "Ania", + "Aniakudo", + "Anica", + "Aniela", + "Anil", + "Anis", + "Anissa", + "Anita", + "Anitra", + "Aniweta", + "Anjali", + "Anjanette", + "Anjela", + "Ankeny", + "Ankney", + "Ann", + "Ann-Marie", + "Anna", + "Anna-Diana", + "Anna-Diane", + "Anna-Maria", + "Annabal", + "Annabel", + "Annabela", + "Annabell", + "Annabella", + "Annabelle", + "Annadiana", + "Annadiane", + "Annalee", + "Annaliese", + "Annalise", + "Annamaria", + "Annamarie", + "Anne", + "Anne-Corinne", + "Anne-Marie", + "Annecorinne", + "Anneliese", + "Annelise", + "Annemarie", + "Annetta", + "Annette", + "Anni", + "Annia", + "Annice", + "Annie", + "Anniken", + "Annis", + "Annissa", + "Annmaria", + "Annmarie", + "Annnora", + "Annora", + "Annorah", + "Annunciata", + "Anny", + "Anora", + "Anse", + "Ansel", + "Ansela", + "Ansell", + "Anselm", + "Anselma", + "Anselme", + "Anselmi", + "Anselmo", + "Ansilma", + "Ansilme", + "Ansley", + "Anson", + "Anstice", + "Anstus", + "Antebi", + "Anthe", + "Anthea", + "Anthia", + "Anthiathia", + "Anthony", + "Antin", + "Antipas", + "Antipus", + "Antoine", + "Antoinetta", + "Antoinette", + "Anton", + "Antone", + "Antonella", + "Antonetta", + "Antoni", + "Antonia", + "Antonie", + "Antonietta", + "Antonin", + "Antonina", + "Antonino", + "Antonio", + "Antonius", + "Antons", + "Antony", + "Antrim", + "Anurag", + "Anuska", + "Any", + "Anya", + "Anyah", + "Anzovin", + "Apfel", + "Apfelstadt", + "Apgar", + "Aphra", + "Aphrodite", + "Apicella", + "Apollo", + "Apollus", + "Apostles", + "Appel", + "Apple", + "Appleby", + "Appledorf", + "Applegate", + "Appleton", + "Appolonia", + "Apps", + "April", + "Aprile", + "Aprilette", + "Apthorp", + "Apul", + "Ara", + "Arabeila", + "Arabel", + "Arabela", + "Arabele", + "Arabella", + "Arabelle", + "Arad", + "Arakawa", + "Araldo", + "Aramanta", + "Aramen", + "Aramenta", + "Araminta", + "Aran", + "Arand", + "Arathorn", + "Arbe", + "Arber", + "Arbuckle", + "Arch", + "Archaimbaud", + "Archambault", + "Archangel", + "Archer", + "Archibald", + "Archibaldo", + "Archibold", + "Archie", + "Archle", + "Archy", + "Ard", + "Arda", + "Ardath", + "Arde", + "Ardeen", + "Ardeha", + "Ardehs", + "Ardel", + "Ardelia", + "Ardelis", + "Ardell", + "Ardella", + "Ardelle", + "Arden", + "Ardene", + "Ardenia", + "Ardeth", + "Ardie", + "Ardin", + "Ardine", + "Ardis", + "Ardisj", + "Ardith", + "Ardme", + "Ardolino", + "Ardra", + "Ardrey", + "Ardussi", + "Ardy", + "Ardyce", + "Ardys", + "Ardyth", + "Arel", + "Arela", + "Arella", + "Arelus", + "Aret", + "Areta", + "Aretha", + "Aretina", + "Aretta", + "Arette", + "Arezzini", + "Argent", + "Argile", + "Argus", + "Argyle", + "Argyres", + "Arhna", + "Ari", + "Aria", + "Ariadne", + "Ariana", + "Ariane", + "Arianie", + "Arianna", + "Arianne", + "Aribold", + "Aric", + "Arica", + "Arick", + "Aridatha", + "Arie", + "Ariel", + "Ariela", + "Ariella", + "Arielle", + "Ariew", + "Arin", + "Ario", + "Arissa", + "Aristotle", + "Arita", + "Arjan", + "Arjun", + "Ark", + "Arlan", + "Arlana", + "Arlee", + "Arleen", + "Arlen", + "Arlena", + "Arlene", + "Arleta", + "Arlette", + "Arley", + "Arleyne", + "Arlie", + "Arliene", + "Arlin", + "Arlina", + "Arlinda", + "Arline", + "Arlo", + "Arlon", + "Arluene", + "Arly", + "Arlyn", + "Arlyne", + "Arlynne", + "Armalda", + "Armalla", + "Armallas", + "Arman", + "Armand", + "Armanda", + "Armando", + "Armbrecht", + "Armbruster", + "Armelda", + "Armil", + "Armilda", + "Armilla", + "Armillas", + "Armillda", + "Armillia", + "Armin", + "Armington", + "Armitage", + "Armond", + "Armstrong", + "Armyn", + "Arnaldo", + "Arnaud", + "Arndt", + "Arne", + "Arnelle", + "Arney", + "Arni", + "Arnie", + "Arno", + "Arnold", + "Arnoldo", + "Arnon", + "Arnst", + "Arnuad", + "Arnulfo", + "Arny", + "Arola", + "Aron", + "Arondel", + "Arondell", + "Aronoff", + "Aronow", + "Aronson", + "Arquit", + "Arratoon", + "Arri", + "Arria", + "Arrio", + "Arron", + "Arst", + "Art", + "Arta", + "Artair", + "Artamas", + "Arte", + "Artema", + "Artemas", + "Artemis", + "Artemisa", + "Artemisia", + "Artemus", + "Arther", + "Arthur", + "Artie", + "Artima", + "Artimas", + "Artina", + "Artur", + "Arturo", + "Artus", + "Arty", + "Aruabea", + "Arun", + "Arundel", + "Arundell", + "Arv", + "Arva", + "Arvad", + "Arvell", + "Arvid", + "Arvie", + "Arvin", + "Arvind", + "Arvo", + "Arvonio", + "Arvy", + "Ary", + "Aryn", + "As", + "Asa", + "Asabi", + "Asante", + "Asaph", + "Asare", + "Aschim", + "Ase", + "Asel", + "Ash", + "Asha", + "Ashbaugh", + "Ashbey", + "Ashby", + "Ashelman", + "Ashely", + "Asher", + "Ashford", + "Ashia", + "Ashien", + "Ashil", + "Ashjian", + "Ashla", + "Ashlan", + "Ashlee", + "Ashleigh", + "Ashlen", + "Ashley", + "Ashli", + "Ashlie", + "Ashlin", + "Ashling", + "Ashly", + "Ashman", + "Ashmead", + "Ashok", + "Ashraf", + "Ashti", + "Ashton", + "Ashwell", + "Ashwin", + "Asia", + "Askari", + "Askwith", + "Aslam", + "Asp", + "Aspa", + "Aspasia", + "Aspia", + "Asquith", + "Assisi", + "Asta", + "Astera", + "Asteria", + "Astor", + "Astra", + "Astraea", + "Astrahan", + "Astrea", + "Astred", + "Astri", + "Astrid", + "Astrix", + "Astto", + "Asuncion", + "Atal", + "Atalanta", + "Atalante", + "Atalanti", + "Atalaya", + "Atalayah", + "Atalee", + "Ataliah", + "Atalie", + "Atalya", + "Atcliffe", + "Athal", + "Athalee", + "Athalia", + "Athalie", + "Athalla", + "Athallia", + "Athelstan", + "Athena", + "Athene", + "Athenian", + "Athey", + "Athiste", + "Atiana", + "Atkins", + "Atkinson", + "Atlanta", + "Atlante", + "Atlas", + "Atlee", + "Atonsah", + "Atrice", + "Atronna", + "Attah", + "Attalanta", + "Attalie", + "Attenborough", + "Attenweiler", + "Atterbury", + "Atthia", + "Attlee", + "Attwood", + "Atul", + "Atwater", + "Atwekk", + "Atwood", + "Atworth", + "Au", + "Aubarta", + "Aube", + "Auberbach", + "Auberon", + "Aubert", + "Auberta", + "Aubigny", + "Aubin", + "Aubine", + "Aubree", + "Aubreir", + "Aubrette", + "Aubrey", + "Aubrie", + "Aubry", + "Auburn", + "Auburta", + "Aubyn", + "Audette", + "Audi", + "Audie", + "Audley", + "Audly", + "Audra", + "Audras", + "Audre", + "Audres", + "Audrey", + "Audri", + "Audrie", + "Audris", + "Audrit", + "Audry", + "Audrye", + "Audsley", + "Audun", + "Audwen", + "Audwin", + "Audy", + "Auerbach", + "Aufmann", + "Augie", + "August", + "Augusta", + "Auguste", + "Augustin", + "Augustina", + "Augustine", + "Augusto", + "Augustus", + "Augy", + "Aulea", + "Auliffe", + "Aun", + "Aundrea", + "Aunson", + "Aura", + "Aurea", + "Aurel", + "Aurelea", + "Aurelia", + "Aurelie", + "Aurelio", + "Aurelius", + "Auria", + "Auric", + "Aurie", + "Aurilia", + "Aurita", + "Aurlie", + "Auroora", + "Aurora", + "Aurore", + "Aurthur", + "Ause", + "Austen", + "Austin", + "Austina", + "Austine", + "Auston", + "Australia", + "Austreng", + "Autrey", + "Autry", + "Autum", + "Autumn", + "Auvil", + "Av", + "Ava", + "Avan", + "Avaria", + "Ave", + "Avelin", + "Aveline", + "Avera", + "Averell", + "Averi", + "Averil", + "Averill", + "Averir", + "Avery", + "Averyl", + "Avi", + "Avictor", + "Avie", + "Avigdor", + "Avilla", + "Avis", + "Avitzur", + "Aviv", + "Aviva", + "Avivah", + "Avner", + "Avra", + "Avraham", + "Avram", + "Avril", + "Avrit", + "Avrom", + "Avron", + "Avruch", + "Awad", + "Ax", + "Axe", + "Axel", + "Aylmar", + "Aylmer", + "Aylsworth", + "Aylward", + "Aymer", + "Ayn", + "Aynat", + "Ayo", + "Ayres", + "Azal", + "Azalea", + "Azaleah", + "Azar", + "Azarcon", + "Azaria", + "Azarria", + "Azelea", + "Azeria", + "Aziza", + "Azpurua", + "Azral", + "Azriel", + "Baal", + "Baalbeer", + "Baalman", + "Bab", + "Babara", + "Babb", + "Babbette", + "Babbie", + "Babby", + "Babcock", + "Babette", + "Babita", + "Babs", + "Bac", + "Bacchus", + "Bach", + "Bachman", + "Backer", + "Backler", + "Bacon", + "Badger", + "Badr", + "Baecher", + "Bael", + "Baelbeer", + "Baer", + "Baerl", + "Baerman", + "Baese", + "Bagger", + "Baggett", + "Baggott", + "Baggs", + "Bagley", + "Bahner", + "Bahr", + "Baiel", + "Bail", + "Bailar", + "Bailey", + "Bailie", + "Baillie", + "Baillieu", + "Baily", + "Bain", + "Bainbridge", + "Bainbrudge", + "Bainter", + "Baird", + "Baiss", + "Bajaj", + "Bak", + "Bakeman", + "Bakemeier", + "Baker", + "Bakerman", + "Bakki", + "Bal", + "Bala", + "Balas", + "Balbinder", + "Balbur", + "Balcer", + "Balch", + "Balcke", + "Bald", + "Baldridge", + "Balduin", + "Baldwin", + "Bale", + "Baler", + "Balf", + "Balfore", + "Balfour", + "Balkin", + "Ball", + "Ballard", + "Balliett", + "Balling", + "Ballinger", + "Balliol", + "Ballman", + "Ballou", + "Balmuth", + "Balough", + "Balsam", + "Balthasar", + "Balthazar", + "Bamberger", + "Bambi", + "Bambie", + "Bamby", + "Bamford", + "Ban", + "Bancroft", + "Bandeen", + "Bander", + "Bandler", + "Bandur", + "Banebrudge", + "Banerjee", + "Bang", + "Bank", + "Banks", + "Banky", + "Banna", + "Bannasch", + "Bannerman", + "Bannister", + "Bannon", + "Banquer", + "Banwell", + "Baptist", + "Baptista", + "Baptiste", + "Baptlsta", + "Bar", + "Bara", + "Barabas", + "Barabbas", + "Baram", + "Baras", + "Barayon", + "Barb", + "Barbabas", + "Barbabra", + "Barbara", + "Barbara-Anne", + "Barbaraanne", + "Barbarese", + "Barbaresi", + "Barbe", + "Barbee", + "Barber", + "Barbette", + "Barbey", + "Barbi", + "Barbie", + "Barbour", + "Barboza", + "Barbra", + "Barbur", + "Barbuto", + "Barby", + "Barcellona", + "Barclay", + "Barcot", + "Barcroft", + "Barcus", + "Bard", + "Barde", + "Barden", + "Bardo", + "Barfuss", + "Barger", + "Bari", + "Barimah", + "Barina", + "Barker", + "Barkley", + "Barling", + "Barlow", + "Barmen", + "Barn", + "Barna", + "Barnaba", + "Barnabas", + "Barnabe", + "Barnaby", + "Barnard", + "Barncard", + "Barnebas", + "Barnes", + "Barnet", + "Barnett", + "Barney", + "Barnie", + "Barnum", + "Barny", + "Barolet", + "Baron", + "Barr", + "Barra", + "Barrada", + "Barram", + "Barraza", + "Barren", + "Barret", + "Barrett", + "Barri", + "Barrie", + "Barrington", + "Barris", + "Barron", + "Barrow", + "Barrus", + "Barry", + "Barsky", + "Barstow", + "Bart", + "Barta", + "Bartel", + "Barth", + "Barthel", + "Barthelemy", + "Barthol", + "Barthold", + "Bartholemy", + "Bartholomeo", + "Bartholomeus", + "Bartholomew", + "Bartie", + "Bartko", + "Bartle", + "Bartlet", + "Bartlett", + "Bartley", + "Bartolemo", + "Bartolome", + "Bartolomeo", + "Barton", + "Bartosch", + "Bartram", + "Barty", + "Baruch", + "Barvick", + "Bary", + "Baryram", + "Bascio", + "Bascomb", + "Base", + "Baseler", + "Basham", + "Bashee", + "Bashemath", + "Bashemeth", + "Bashuk", + "Basia", + "Basil", + "Basile", + "Basilio", + "Basilius", + "Basir", + "Baskett", + "Bass", + "Basset", + "Bassett", + "Basso", + "Bast", + "Bastian", + "Bastien", + "Bat", + "Batchelor", + "Bate", + "Baten", + "Bates", + "Batha", + "Bathelda", + "Bathesda", + "Bathilda", + "Batholomew", + "Bathsheb", + "Bathsheba", + "Bathsheeb", + "Bathulda", + "Batish", + "Batista", + "Batory", + "Batruk", + "Batsheva", + "Battat", + "Battista", + "Battiste", + "Batty", + "Baudelaire", + "Baudin", + "Baudoin", + "Bauer", + "Baugh", + "Baum", + "Baumann", + "Baumbaugh", + "Baun", + "Bausch", + "Bauske", + "Bautista", + "Bautram", + "Bax", + "Baxie", + "Baxter", + "Baxy", + "Bay", + "Bayard", + "Bayer", + "Bayless", + "Baylor", + "Bayly", + "Baynebridge", + "Bazar", + "Bazil", + "Bazluke", + "Bea", + "Beach", + "Beacham", + "Beal", + "Beale", + "Beall", + "Bealle", + "Bean", + "Beane", + "Beaner", + "Bear", + "Bearce", + "Beard", + "Beare", + "Bearnard", + "Beasley", + "Beaston", + "Beata", + "Beatrice", + "Beatrisa", + "Beatrix", + "Beatriz", + "Beattie", + "Beatty", + "Beau", + "Beauchamp", + "Beaudoin", + "Beaufert", + "Beaufort", + "Beaulieu", + "Beaumont", + "Beauregard", + "Beauvais", + "Beaver", + "Bebe", + "Beberg", + "Becca", + "Bechler", + "Becht", + "Beck", + "Becka", + "Becker", + "Beckerman", + "Becket", + "Beckett", + "Becki", + "Beckie", + "Beckman", + "Becky", + "Bedad", + "Bedelia", + "Bedell", + "Bedwell", + "Bee", + "Beebe", + "Beeck", + "Beedon", + "Beekman", + "Beera", + "Beesley", + "Beeson", + "Beetner", + "Beffrey", + "Bega", + "Begga", + "Beghtol", + "Behah", + "Behka", + "Behl", + "Behlau", + "Behlke", + "Behm", + "Behn", + "Behnken", + "Behre", + "Behrens", + "Beichner", + "Beilul", + "Bein", + "Beisel", + "Beitch", + "Beitnes", + "Beitris", + "Beitz", + "Beka", + "Bekah", + "Bekelja", + "Beker", + "Bekha", + "Bekki", + "Bel", + "Bela", + "Belak", + "Belamy", + "Belanger", + "Belayneh", + "Belcher", + "Belda", + "Belden", + "Belding", + "Belen", + "Belford", + "Belia", + "Belicia", + "Belier", + "Belinda", + "Belita", + "Bell", + "Bella", + "Bellamy", + "Bellanca", + "Bellaude", + "Bellda", + "Belldame", + "Belldas", + "Belle", + "Beller", + "Bellew", + "Bellina", + "Bellis", + "Bello", + "Belloir", + "Belmonte", + "Belshin", + "Belsky", + "Belter", + "Beltran", + "Belva", + "Belvia", + "Ben", + "Bena", + "Bencion", + "Benco", + "Bender", + "Bendick", + "Bendicta", + "Bendicty", + "Bendite", + "Bendix", + "Benedetta", + "Benedetto", + "Benedic", + "Benedick", + "Benedict", + "Benedicta", + "Benedicto", + "Benedikt", + "Benedikta", + "Benedix", + "Benenson", + "Benetta", + "Benge", + "Bengt", + "Benia", + "Beniamino", + "Benil", + "Benilda", + "Benildas", + "Benildis", + "Benioff", + "Benis", + "Benisch", + "Benita", + "Benito", + "Benjamen", + "Benjamin", + "Benji", + "Benjie", + "Benjy", + "Benkley", + "Benn", + "Bennet", + "Bennett", + "Benni", + "Bennie", + "Bennink", + "Bennion", + "Bennir", + "Benny", + "Benoit", + "Benoite", + "Bensen", + "Bensky", + "Benson", + "Bent", + "Bentlee", + "Bentley", + "Bently", + "Benton", + "Benyamin", + "Benzel", + "Beora", + "Beore", + "Ber", + "Berard", + "Berardo", + "Berck", + "Berenice", + "Beret", + "Berey", + "Berfield", + "Berg", + "Berga", + "Bergeman", + "Bergen", + "Berger", + "Bergerac", + "Bergeron", + "Bergess", + "Berget", + "Bergh", + "Berghoff", + "Bergin", + "Berglund", + "Bergman", + "Bergmann", + "Bergmans", + "Bergquist", + "Bergren", + "Bergstein", + "Bergstrom", + "Bergwall", + "Berhley", + "Berk", + "Berke", + "Berkeley", + "Berkie", + "Berkin", + "Berkley", + "Berkly", + "Berkman", + "Berkow", + "Berkshire", + "Berky", + "Berl", + "Berlauda", + "Berlin", + "Berlinda", + "Berliner", + "Berlyn", + "Berman", + "Bern", + "Berna", + "Bernadene", + "Bernadette", + "Bernadina", + "Bernadine", + "Bernard", + "Bernardi", + "Bernardina", + "Bernardine", + "Bernardo", + "Bernarr", + "Bernat", + "Berne", + "Bernelle", + "Berner", + "Berners", + "Berneta", + "Bernete", + "Bernetta", + "Bernette", + "Bernhard", + "Berni", + "Bernice", + "Bernie", + "Bernita", + "Bernj", + "Berns", + "Bernstein", + "Bernt", + "Berny", + "Berri", + "Berrie", + "Berriman", + "Berry", + "Berstine", + "Bert", + "Berta", + "Bertasi", + "Berte", + "Bertelli", + "Bertero", + "Bertha", + "Berthe", + "Berthold", + "Berthoud", + "Berti", + "Bertie", + "Bertila", + "Bertilla", + "Bertina", + "Bertine", + "Bertle", + "Bertold", + "Bertolde", + "Berton", + "Bertram", + "Bertrand", + "Bertrando", + "Bertsche", + "Berty", + "Berwick", + "Beryl", + "Beryle", + "Beshore", + "Besnard", + "Bess", + "Besse", + "Bessie", + "Bessy", + "Best", + "Beth", + "Bethanne", + "Bethany", + "Bethel", + "Bethena", + "Bethesda", + "Bethesde", + "Bethezel", + "Bethina", + "Betsey", + "Betsy", + "Betta", + "Bette", + "Bette-Ann", + "Betteann", + "Betteanne", + "Bettencourt", + "Betthel", + "Betthezel", + "Betthezul", + "Betti", + "Bettina", + "Bettine", + "Betty", + "Bettye", + "Bettzel", + "Betz", + "Beulah", + "Beuthel", + "Beutler", + "Beutner", + "Bev", + "Bevan", + "Bevash", + "Bever", + "Beverie", + "Beverle", + "Beverlee", + "Beverley", + "Beverlie", + "Beverly", + "Bevers", + "Bevin", + "Bevis", + "Bevon", + "Bevus", + "Bevvy", + "Beyer", + "Bezanson", + "Bhatt", + "Bhayani", + "Biagi", + "Biagio", + "Biamonte", + "Bianca", + "Biancha", + "Bianchi", + "Bianka", + "Bibbie", + "Bibby", + "Bibbye", + "Bibeau", + "Bibi", + "Bible", + "Bick", + "Bickart", + "Bicknell", + "Biddick", + "Biddie", + "Biddle", + "Biddy", + "Bidget", + "Bidle", + "Biebel", + "Biegel", + "Bierman", + "Biernat", + "Bigelow", + "Bigford", + "Bigg", + "Biggs", + "Bigler", + "Bigner", + "Bigod", + "Bigot", + "Bik", + "Bikales", + "Bil", + "Bilbe", + "Bilek", + "Biles", + "Bili", + "Bilicki", + "Bill", + "Billat", + "Bille", + "Billen", + "Billi", + "Billie", + "Billmyre", + "Bills", + "Billy", + "Billye", + "Bilow", + "Bilski", + "Bina", + "Binah", + "Bindman", + "Binetta", + "Binette", + "Bing", + "Bink", + "Binky", + "Binni", + "Binnie", + "Binnings", + "Binny", + "Biondo", + "Birch", + "Birchard", + "Birck", + "Bird", + "Birdella", + "Birdie", + "Birdt", + "Birecree", + "Birgit", + "Birgitta", + "Birk", + "Birkett", + "Birkle", + "Birkner", + "Birmingham", + "Biron", + "Bish", + "Bishop", + "Bissell", + "Bisset", + "Bithia", + "Bittencourt", + "Bitthia", + "Bittner", + "Bivins", + "Bixby", + "Bixler", + "Bjork", + "Bjorn", + "Black", + "Blackburn", + "Blackington", + "Blackman", + "Blackmore", + "Blackmun", + "Blackstock", + "Blackwell", + "Blader", + "Blain", + "Blaine", + "Blainey", + "Blair", + "Blaire", + "Blaise", + "Blake", + "Blakelee", + "Blakeley", + "Blakely", + "Blalock", + "Blanc", + "Blanca", + "Blanch", + "Blancha", + "Blanchard", + "Blanche", + "Blanchette", + "Bland", + "Blandina", + "Blanding", + "Blane", + "Blank", + "Blanka", + "Blankenship", + "Blas", + "Blase", + "Blaseio", + "Blasien", + "Blasius", + "Blatman", + "Blatt", + "Blau", + "Blayne", + "Blayze", + "Blaze", + "Bledsoe", + "Bleier", + "Blen", + "Blessington", + "Blight", + "Blim", + "Blinni", + "Blinnie", + "Blinny", + "Bliss", + "Blisse", + "Blithe", + "Bloch", + "Block", + "Blockus", + "Blodget", + "Blodgett", + "Bloem", + "Blondell", + "Blondelle", + "Blondie", + "Blondy", + "Blood", + "Bloom", + "Bloomer", + "Blossom", + "Blount", + "Bloxberg", + "Bluefarb", + "Bluefield", + "Bluh", + "Bluhm", + "Blum", + "Bluma", + "Blumenfeld", + "Blumenthal", + "Blunk", + "Blunt", + "Blus", + "Blynn", + "Blythe", + "Bo", + "Boak", + "Boar", + "Boardman", + "Boarer", + "Boaten", + "Boatwright", + "Bob", + "Bobbe", + "Bobbee", + "Bobbette", + "Bobbi", + "Bobbie", + "Bobby", + "Bobbye", + "Bobette", + "Bobina", + "Bobine", + "Bobinette", + "Bobker", + "Bobseine", + "Bock", + "Bocock", + "Bodi", + "Bodkin", + "Bodnar", + "Bodrogi", + "Bodwell", + "Body", + "Boehike", + "Boehmer", + "Boeke", + "Boelter", + "Boesch", + "Boeschen", + "Boff", + "Boffa", + "Bogart", + "Bogey", + "Boggers", + "Boggs", + "Bogie", + "Bogoch", + "Bogosian", + "Bogusz", + "Bohannon", + "Bohaty", + "Bohi", + "Bohlen", + "Bohlin", + "Bohman", + "Bohner", + "Bohon", + "Bohrer", + "Bohs", + "Bohun", + "Boice", + "Boigie", + "Boiney", + "Bois", + "Bolan", + "Boland", + "Bolanger", + "Bolen", + "Boles", + "Boleslaw", + "Boleyn", + "Bolger", + "Bolitho", + "Bollay", + "Bollen", + "Bolling", + "Bollinger", + "Bolme", + "Bolt", + "Bolte", + "Bolten", + "Bolton", + "Bomke", + "Bonacci", + "Bonaparte", + "Bonar", + "Bond", + "Bondie", + "Bondon", + "Bondy", + "Bone", + "Boni", + "Boniface", + "Bonilla", + "Bonina", + "Bonine", + "Bonis", + "Bonita", + "Bonn", + "Bonne", + "Bonneau", + "Bonnee", + "Bonnell", + "Bonner", + "Bonnes", + "Bonnette", + "Bonney", + "Bonni", + "Bonnibelle", + "Bonnice", + "Bonnie", + "Bonns", + "Bonny", + "Bonucci", + "Booker", + "Booma", + "Boone", + "Boonie", + "Boony", + "Boor", + "Boorer", + "Boorman", + "Boot", + "Boote", + "Booth", + "Boothe", + "Boothman", + "Booze", + "Bopp", + "Bor", + "Bora", + "Borchers", + "Borchert", + "Bord", + "Borden", + "Bordie", + "Bordiuk", + "Bordy", + "Bore", + "Borek", + "Borer", + "Bores", + "Borg", + "Borgeson", + "Boris", + "Bork", + "Borlase", + "Borlow", + "Borman", + "Born", + "Bornie", + "Bornstein", + "Borras", + "Borrell", + "Borreri", + "Borries", + "Borroff", + "Borszcz", + "Bortman", + "Bortz", + "Boru", + "Bosch", + "Bose", + "Boser", + "Bosson", + "Bostow", + "Boswall", + "Boswell", + "Botnick", + "Botsford", + "Bottali", + "Botti", + "Botzow", + "Bouchard", + "Boucher", + "Bouchier", + "Boudreaux", + "Bough", + "Boulanger", + "Bouldon", + "Bouley", + "Bound", + "Bounds", + "Bourgeois", + "Bourke", + "Bourn", + "Bourne", + "Bourque", + "Boutis", + "Bouton", + "Bouzoun", + "Bove", + "Bovill", + "Bow", + "Bowden", + "Bowe", + "Bowen", + "Bower", + "Bowerman", + "Bowers", + "Bowes", + "Bowie", + "Bowlds", + "Bowler", + "Bowles", + "Bowman", + "Bowne", + "Bowra", + "Bowrah", + "Bowyer", + "Box", + "Boy", + "Boyce", + "Boycey", + "Boycie", + "Boyd", + "Boyden", + "Boyer", + "Boyes", + "Boykins", + "Boylan", + "Boylston", + "Boynton", + "Boys", + "Boyse", + "Boyt", + "Bozovich", + "Bozuwa", + "Braasch", + "Brabazon", + "Braca", + "Bracci", + "Brace", + "Brackely", + "Brackett", + "Brad", + "Bradan", + "Brade", + "Braden", + "Bradeord", + "Brader", + "Bradford", + "Bradlee", + "Bradleigh", + "Bradley", + "Bradly", + "Bradman", + "Bradney", + "Bradshaw", + "Bradski", + "Bradstreet", + "Bradway", + "Bradwell", + "Brady", + "Braeunig", + "Brag", + "Brahear", + "Brainard", + "Bram", + "Bramwell", + "Bran", + "Brana", + "Branca", + "Branch", + "Brand", + "Brandais", + "Brande", + "Brandea", + "Branden", + "Brandenburg", + "Brander", + "Brandes", + "Brandi", + "Brandice", + "Brandie", + "Brandise", + "Brandon", + "Brandt", + "Brandtr", + "Brandwein", + "Brandy", + "Brandyn", + "Branen", + "Branham", + "Brannon", + "Branscum", + "Brant", + "Brantley", + "Brasca", + "Brass", + "Braswell", + "Brathwaite", + "Bratton", + "Braun", + "Braunstein", + "Brause", + "Bravar", + "Bravin", + "Brawley", + "Brawner", + "Bray", + "Braynard", + "Brazee", + "Breana", + "Breanne", + "Brear", + "Breban", + "Brebner", + "Brecher", + "Brechtel", + "Bred", + "Bree", + "Breech", + "Breed", + "Breen", + "Breena", + "Breeze", + "Breger", + "Brelje", + "Bremble", + "Bremen", + "Bremer", + "Bremser", + "Bren", + "Brena", + "Brenan", + "Brenda", + "Brendan", + "Brenden", + "Brendin", + "Brendis", + "Brendon", + "Brenk", + "Brenn", + "Brenna", + "Brennan", + "Brennen", + "Brenner", + "Brent", + "Brenton", + "Brentt", + "Brenza", + "Bresee", + "Breskin", + "Brest", + "Bret", + "Brett", + "Brew", + "Brewer", + "Brewster", + "Brey", + "Brezin", + "Bria", + "Brian", + "Briana", + "Brianna", + "Brianne", + "Briano", + "Briant", + "Brice", + "Brick", + "Bricker", + "Bride", + "Bridge", + "Bridges", + "Bridget", + "Bridgette", + "Bridgid", + "Bridie", + "Bridwell", + "Brie", + "Brien", + "Brier", + "Brieta", + "Brietta", + "Brig", + "Brigette", + "Brigg", + "Briggs", + "Brigham", + "Bright", + "Brightman", + "Brighton", + "Brigid", + "Brigida", + "Brigit", + "Brigitta", + "Brigitte", + "Brill", + "Brina", + "Brindell", + "Brindle", + "Brine", + "Briney", + "Bringhurst", + "Brink", + "Brinkema", + "Brinn", + "Brinna", + "Brinson", + "Briny", + "Brion", + "Briscoe", + "Bristow", + "Brit", + "Brita", + "Britney", + "Britni", + "Britt", + "Britta", + "Brittain", + "Brittan", + "Brittaney", + "Brittani", + "Brittany", + "Britte", + "Britteny", + "Brittne", + "Brittnee", + "Brittney", + "Brittni", + "Britton", + "Brnaba", + "Brnaby", + "Broadbent", + "Brock", + "Brockie", + "Brocklin", + "Brockwell", + "Brocky", + "Brod", + "Broddie", + "Broddy", + "Brodench", + "Broder", + "Broderic", + "Broderick", + "Brodeur", + "Brodie", + "Brodsky", + "Brody", + "Broeder", + "Broek", + "Broeker", + "Brogle", + "Broida", + "Brok", + "Brom", + "Bromleigh", + "Bromley", + "Bron", + "Bronder", + "Bronez", + "Bronk", + "Bronnie", + "Bronny", + "Bronson", + "Bronwen", + "Bronwyn", + "Brook", + "Brooke", + "Brookes", + "Brookhouse", + "Brooking", + "Brookner", + "Brooks", + "Broome", + "Brose", + "Brosine", + "Brost", + "Brosy", + "Brote", + "Brothers", + "Brotherson", + "Brott", + "Brottman", + "Broucek", + "Brout", + "Brouwer", + "Brower", + "Brown", + "Browne", + "Browning", + "Brownley", + "Brownson", + "Brozak", + "Brubaker", + "Bruce", + "Brucie", + "Bruckner", + "Bruell", + "Brufsky", + "Bruis", + "Brunell", + "Brunella", + "Brunelle", + "Bruner", + "Brunhild", + "Brunhilda", + "Brunhilde", + "Bruni", + "Bruning", + "Brunk", + "Brunn", + "Bruno", + "Bruns", + "Bruyn", + "Bryan", + "Bryana", + "Bryant", + "Bryanty", + "Bryce", + "Bryn", + "Bryna", + "Bryner", + "Brynn", + "Brynna", + "Brynne", + "Bryon", + "Buatti", + "Bubalo", + "Bubb", + "Bucella", + "Buchalter", + "Buchanan", + "Buchbinder", + "Bucher", + "Buchheim", + "Buck", + "Buckden", + "Buckels", + "Buckie", + "Buckingham", + "Buckler", + "Buckley", + "Bucky", + "Bud", + "Budd", + "Budde", + "Buddie", + "Budding", + "Buddy", + "Buderus", + "Budge", + "Budwig", + "Budworth", + "Buehler", + "Buehrer", + "Buell", + "Buerger", + "Bueschel", + "Buff", + "Buffo", + "Buffum", + "Buffy", + "Buford", + "Bugbee", + "Buhler", + "Bui", + "Buine", + "Buiron", + "Buke", + "Bull", + "Bullard", + "Bullen", + "Buller", + "Bulley", + "Bullion", + "Bullis", + "Bullivant", + "Bullock", + "Bullough", + "Bully", + "Bultman", + "Bum", + "Bumgardner", + "Buna", + "Bunce", + "Bunch", + "Bunde", + "Bunder", + "Bundy", + "Bunker", + "Bunni", + "Bunnie", + "Bunns", + "Bunny", + "Bunow", + "Bunting", + "Buonomo", + "Buote", + "Burack", + "Burbank", + "Burch", + "Burchett", + "Burck", + "Burd", + "Burdelle", + "Burdett", + "Burford", + "Burg", + "Burgener", + "Burger", + "Burgess", + "Burget", + "Burgwell", + "Burhans", + "Burk", + "Burke", + "Burkhard", + "Burkhardt", + "Burkhart", + "Burkitt", + "Burkle", + "Burkley", + "Burl", + "Burleigh", + "Burley", + "Burlie", + "Burman", + "Burn", + "Burnaby", + "Burnard", + "Burne", + "Burner", + "Burnett", + "Burney", + "Burnham", + "Burnie", + "Burnight", + "Burnley", + "Burns", + "Burnsed", + "Burnside", + "Burny", + "Buroker", + "Burr", + "Burra", + "Burrell", + "Burrill", + "Burris", + "Burroughs", + "Burrow", + "Burrows", + "Burrton", + "Burrus", + "Burt", + "Burta", + "Burtie", + "Burtis", + "Burton", + "Burty", + "Burwell", + "Bury", + "Busby", + "Busch", + "Buschi", + "Buseck", + "Busey", + "Bush", + "Bushey", + "Bushore", + "Bushweller", + "Busiek", + "Buskirk", + "Buskus", + "Bussey", + "Bussy", + "Bust", + "Butch", + "Butcher", + "Butler", + "Butta", + "Buttaro", + "Butte", + "Butterfield", + "Butterworth", + "Button", + "Buxton", + "Buyer", + "Buyers", + "Buyse", + "Buzz", + "Buzzell", + "Byers", + "Byler", + "Byram", + "Byran", + "Byrann", + "Byrd", + "Byrdie", + "Byrle", + "Byrn", + "Byrne", + "Byrom", + "Byron", + "Bysshe", + "Bywaters", + "Bywoods", + "Cacia", + "Cacie", + "Cacilia", + "Cacilie", + "Cacka", + "Cad", + "Cadal", + "Caddaric", + "Caddric", + "Cade", + "Cadel", + "Cadell", + "Cadman", + "Cadmann", + "Cadmar", + "Cadmarr", + "Caesar", + "Caesaria", + "Caffrey", + "Cagle", + "Cahan", + "Cahilly", + "Cahn", + "Cahra", + "Cai", + "Caia", + "Caiaphas", + "Cailean", + "Cailly", + "Cain", + "Caine", + "Caines", + "Cairistiona", + "Cairns", + "Caitlin", + "Caitrin", + "Cal", + "Calabrese", + "Calabresi", + "Calan", + "Calandra", + "Calandria", + "Calbert", + "Caldeira", + "Calder", + "Caldera", + "Calderon", + "Caldwell", + "Cale", + "Caleb", + "Calen", + "Calendra", + "Calendre", + "Calesta", + "Calhoun", + "Calia", + "Calica", + "Calida", + "Calie", + "Calisa", + "Calise", + "Calista", + "Call", + "Calla", + "Callahan", + "Callan", + "Callas", + "Calle", + "Callean", + "Callery", + "Calley", + "Calli", + "Callida", + "Callie", + "Callista", + "Calloway", + "Callum", + "Cally", + "Calmas", + "Calondra", + "Calore", + "Calv", + "Calva", + "Calvano", + "Calvert", + "Calvin", + "Calvina", + "Calvinna", + "Calvo", + "Calypso", + "Calysta", + "Cam", + "Camala", + "Camarata", + "Camden", + "Camel", + "Camella", + "Camellia", + "Cameron", + "Camey", + "Camfort", + "Cami", + "Camila", + "Camile", + "Camilia", + "Camilla", + "Camille", + "Camilo", + "Camm", + "Cammi", + "Cammie", + "Cammy", + "Camp", + "Campagna", + "Campball", + "Campbell", + "Campman", + "Campney", + "Campos", + "Campy", + "Camus", + "Can", + "Canada", + "Canale", + "Cand", + "Candace", + "Candi", + "Candice", + "Candida", + "Candide", + "Candie", + "Candis", + "Candless", + "Candra", + "Candy", + "Candyce", + "Caneghem", + "Canfield", + "Canica", + "Canice", + "Caniff", + "Cann", + "Cannell", + "Cannice", + "Canning", + "Cannon", + "Canon", + "Canotas", + "Canter", + "Cantlon", + "Cantone", + "Cantu", + "Canty", + "Canute", + "Capello", + "Caplan", + "Capon", + "Capone", + "Capp", + "Cappella", + "Cappello", + "Capps", + "Caprice", + "Capriola", + "Caputo", + "Caputto", + "Capwell", + "Car", + "Cara", + "Caralie", + "Caras", + "Caravette", + "Caraviello", + "Carberry", + "Carbo", + "Carbone", + "Carboni", + "Carbrey", + "Carce", + "Card", + "Carder", + "Cardew", + "Cardie", + "Cardinal", + "Cardon", + "Cardwell", + "Care", + "Careaga", + "Caren", + "Carena", + "Caresa", + "Caressa", + "Caresse", + "Carew", + "Carey", + "Cargian", + "Carhart", + "Cari", + "Caria", + "Carie", + "Caril", + "Carilla", + "Carilyn", + "Carin", + "Carina", + "Carine", + "Cariotta", + "Carisa", + "Carissa", + "Carita", + "Caritta", + "Carl", + "Carla", + "Carlee", + "Carleen", + "Carlen", + "Carlene", + "Carleton", + "Carley", + "Carli", + "Carlick", + "Carlie", + "Carlile", + "Carlin", + "Carlina", + "Carline", + "Carling", + "Carlisle", + "Carlita", + "Carlo", + "Carlock", + "Carlos", + "Carlota", + "Carlotta", + "Carlson", + "Carlstrom", + "Carlton", + "Carly", + "Carlye", + "Carlyle", + "Carlyn", + "Carlynn", + "Carlynne", + "Carma", + "Carman", + "Carmel", + "Carmela", + "Carmelia", + "Carmelina", + "Carmelita", + "Carmella", + "Carmelle", + "Carmelo", + "Carmen", + "Carmena", + "Carmencita", + "Carmina", + "Carmine", + "Carmita", + "Carmon", + "Carn", + "Carnahan", + "Carnay", + "Carnes", + "Carney", + "Carny", + "Caro", + "Carol", + "Carol-Jean", + "Carola", + "Carolan", + "Carolann", + "Carole", + "Carolee", + "Carolin", + "Carolina", + "Caroline", + "Carolle", + "Carolus", + "Carolyn", + "Carolyne", + "Carolynn", + "Carolynne", + "Caron", + "Carothers", + "Carpenter", + "Carper", + "Carpet", + "Carpio", + "Carr", + "Carree", + "Carrel", + "Carrelli", + "Carrew", + "Carri", + "Carrick", + "Carrie", + "Carrillo", + "Carrington", + "Carrissa", + "Carrnan", + "Carrol", + "Carroll", + "Carry", + "Carson", + "Cart", + "Cartan", + "Carter", + "Carthy", + "Cartie", + "Cartwell", + "Cartwright", + "Caruso", + "Carver", + "Carvey", + "Cary", + "Caryl", + "Caryn", + "Cas", + "Casabonne", + "Casady", + "Casaleggio", + "Casandra", + "Casanova", + "Casar", + "Casavant", + "Case", + "Casey", + "Cash", + "Casi", + "Casia", + "Casie", + "Casilda", + "Casilde", + "Casimir", + "Casimire", + "Casmey", + "Caspar", + "Casper", + "Cass", + "Cassady", + "Cassandra", + "Cassandre", + "Cassandry", + "Cassaundra", + "Cassell", + "Cassella", + "Cassey", + "Cassi", + "Cassiani", + "Cassidy", + "Cassie", + "Cassil", + "Cassilda", + "Cassius", + "Cassondra", + "Cassy", + "Casta", + "Castara", + "Casteel", + "Castera", + "Castillo", + "Castle", + "Castor", + "Castora", + "Castorina", + "Castra", + "Castro", + "Caswell", + "Cataldo", + "Catarina", + "Cate", + "Caterina", + "Cates", + "Cath", + "Catha", + "Catharina", + "Catharine", + "Cathe", + "Cathee", + "Catherin", + "Catherina", + "Catherine", + "Cathey", + "Cathi", + "Cathie", + "Cathleen", + "Cathlene", + "Cathrin", + "Cathrine", + "Cathryn", + "Cathy", + "Cathyleen", + "Cati", + "Catie", + "Catima", + "Catina", + "Catlaina", + "Catlee", + "Catlin", + "Cato", + "Caton", + "Catrina", + "Catriona", + "Catt", + "Cattan", + "Cattier", + "Cattima", + "Catto", + "Catton", + "Caty", + "Caughey", + "Caundra", + "Cavallaro", + "Cavan", + "Cavanagh", + "Cavanaugh", + "Cave", + "Caves", + "Cavil", + "Cavill", + "Cavit", + "Cavuoto", + "Cawley", + "Caye", + "Cayla", + "Caylor", + "Cayser", + "Caz", + "Cazzie", + "Cchaddie", + "Cece", + "Cecelia", + "Cecil", + "Cecile", + "Ceciley", + "Cecilia", + "Cecilio", + "Cecilius", + "Cecilla", + "Cecily", + "Ced", + "Cedar", + "Cedell", + "Cedric", + "Ceevah", + "Ceil", + "Cele", + "Celene", + "Celeski", + "Celesta", + "Celeste", + "Celestia", + "Celestina", + "Celestine", + "Celestyn", + "Celestyna", + "Celia", + "Celie", + "Celik", + "Celin", + "Celina", + "Celinda", + "Celine", + "Celinka", + "Celio", + "Celisse", + "Celka", + "Celle", + "Cello", + "Celtic", + "Cenac", + "Cence", + "Centeno", + "Center", + "Centonze", + "Ceporah", + "Cerallua", + "Cerelia", + "Cerell", + "Cerellia", + "Cerelly", + "Cerf", + "Cerracchio", + "Certie", + "Cerveny", + "Cerys", + "Cesar", + "Cesare", + "Cesaria", + "Cesaro", + "Cestar", + "Cesya", + "Cha", + "Chabot", + "Chace", + "Chad", + "Chadabe", + "Chadbourne", + "Chadburn", + "Chadd", + "Chaddie", + "Chaddy", + "Chader", + "Chadwick", + "Chae", + "Chafee", + "Chaffee", + "Chaffin", + "Chaffinch", + "Chaiken", + "Chaille", + "Chaim", + "Chainey", + "Chaing", + "Chak", + "Chaker", + "Chally", + "Chalmer", + "Chalmers", + "Chamberlain", + "Chamberlin", + "Chambers", + "Chamkis", + "Champ", + "Champagne", + "Champaigne", + "Chan", + "Chance", + "Chancellor", + "Chancelor", + "Chancey", + "Chanda", + "Chandal", + "Chandler", + "Chandless", + "Chandos", + "Chandra", + "Chane", + "Chaney", + "Chang", + "Changaris", + "Channa", + "Channing", + "Chansoo", + "Chantal", + "Chantalle", + "Chao", + "Chap", + "Chapa", + "Chapel", + "Chapell", + "Chapen", + "Chapin", + "Chapland", + "Chapman", + "Chapnick", + "Chappelka", + "Chappell", + "Chappie", + "Chappy", + "Chara", + "Charbonneau", + "Charbonnier", + "Chard", + "Chari", + "Charie", + "Charil", + "Charin", + "Chariot", + "Charis", + "Charissa", + "Charisse", + "Charita", + "Charity", + "Charla", + "Charlean", + "Charleen", + "Charlena", + "Charlene", + "Charles", + "Charlet", + "Charleton", + "Charley", + "Charlie", + "Charline", + "Charlot", + "Charlotta", + "Charlotte", + "Charlton", + "Charmain", + "Charmaine", + "Charmane", + "Charmian", + "Charmine", + "Charmion", + "Charo", + "Charpentier", + "Charron", + "Charry", + "Charteris", + "Charters", + "Charyl", + "Chas", + "Chase", + "Chasse", + "Chassin", + "Chastain", + "Chastity", + "Chatav", + "Chatterjee", + "Chatwin", + "Chaudoin", + "Chaunce", + "Chauncey", + "Chavaree", + "Chaves", + "Chavey", + "Chavez", + "Chaworth", + "Che", + "Cheadle", + "Cheatham", + "Checani", + "Chee", + "Cheffetz", + "Cheke", + "Chellman", + "Chelsae", + "Chelsea", + "Chelsey", + "Chelsie", + "Chelsy", + "Chelton", + "Chem", + "Chema", + "Chemar", + "Chemaram", + "Chemarin", + "Chemash", + "Chemesh", + "Chemosh", + "Chemush", + "Chen", + "Chenay", + "Chenee", + "Cheney", + "Cheng", + "Cher", + "Chere", + "Cherey", + "Cheri", + "Cheria", + "Cherian", + "Cherianne", + "Cherice", + "Cherida", + "Cherie", + "Cherilyn", + "Cherilynn", + "Cherin", + "Cherise", + "Cherish", + "Cherlyn", + "Chernow", + "Cherri", + "Cherrita", + "Cherry", + "Chery", + "Cherye", + "Cheryl", + "Ches", + "Cheshire", + "Cheslie", + "Chesna", + "Chesney", + "Chesnut", + "Chessa", + "Chessy", + "Chester", + "Cheston", + "Chet", + "Cheung", + "Chev", + "Chevalier", + "Chevy", + "Chew", + "Cheyne", + "Cheyney", + "Chi", + "Chiaki", + "Chiang", + "Chiarra", + "Chic", + "Chick", + "Chickie", + "Chicky", + "Chico", + "Chicoine", + "Chien", + "Chil", + "Chilcote", + "Child", + "Childers", + "Childs", + "Chiles", + "Chill", + "Chilson", + "Chilt", + "Chilton", + "Chimene", + "Chin", + "China", + "Ching", + "Chinua", + "Chiou", + "Chip", + "Chipman", + "Chiquia", + "Chiquita", + "Chirlin", + "Chisholm", + "Chita", + "Chitkara", + "Chivers", + "Chladek", + "Chlo", + "Chloe", + "Chloette", + "Chloras", + "Chlores", + "Chlori", + "Chloris", + "Cho", + "Chobot", + "Chon", + "Chong", + "Choo", + "Choong", + "Chor", + "Chouest", + "Chow", + "Chretien", + "Chris", + "Chrisman", + "Chrisoula", + "Chrissa", + "Chrisse", + "Chrissie", + "Chrissy", + "Christa", + "Christabel", + "Christabella", + "Christabelle", + "Christal", + "Christalle", + "Christan", + "Christean", + "Christel", + "Christen", + "Christensen", + "Christenson", + "Christi", + "Christian", + "Christiana", + "Christiane", + "Christianity", + "Christianna", + "Christiano", + "Christiansen", + "Christianson", + "Christie", + "Christin", + "Christina", + "Christine", + "Christis", + "Christmann", + "Christmas", + "Christoffer", + "Christoforo", + "Christoper", + "Christoph", + "Christophe", + "Christopher", + "Christos", + "Christy", + "Christye", + "Christyna", + "Chrisy", + "Chrotoem", + "Chrysa", + "Chrysler", + "Chrystal", + "Chryste", + "Chrystel", + "Chu", + "Chuah", + "Chubb", + "Chuch", + "Chucho", + "Chuck", + "Chud", + "Chui", + "Chuipek", + "Chun", + "Chung", + "Chura", + "Church", + "Churchill", + "Chute", + "Chuu", + "Chyou", + "Cia", + "Cianca", + "Ciapas", + "Ciapha", + "Ciaphus", + "Cibis", + "Ciccia", + "Cicely", + "Cicenia", + "Cicero", + "Cichocki", + "Cicily", + "Cid", + "Cida", + "Ciel", + "Cila", + "Cilka", + "Cilla", + "Cilo", + "Cilurzo", + "Cima", + "Cimah", + "Cimbura", + "Cinda", + "Cindee", + "Cindelyn", + "Cinderella", + "Cindi", + "Cindie", + "Cindra", + "Cindy", + "Cinelli", + "Cini", + "Cinnamon", + "Cioban", + "Cioffred", + "Ciprian", + "Circosta", + "Ciri", + "Cirilla", + "Cirillo", + "Cirilo", + "Ciro", + "Cirone", + "Cirri", + "Cis", + "Cissie", + "Cissiee", + "Cissy", + "Cita", + "Citarella", + "Citron", + "Clabo", + "Claiborn", + "Claiborne", + "Clair", + "Claire", + "Claman", + "Clance", + "Clancy", + "Clapp", + "Clapper", + "Clara", + "Clarabelle", + "Clarance", + "Clardy", + "Clare", + "Clarence", + "Claresta", + "Clareta", + "Claretta", + "Clarette", + "Clarey", + "Clarhe", + "Clari", + "Claribel", + "Clarice", + "Clarie", + "Clarinda", + "Clarine", + "Clarisa", + "Clarise", + "Clarissa", + "Clarisse", + "Clarita", + "Clark", + "Clarke", + "Clarkin", + "Clarkson", + "Clary", + "Claud", + "Clauddetta", + "Claude", + "Claudell", + "Claudelle", + "Claudetta", + "Claudette", + "Claudia", + "Claudian", + "Claudianus", + "Claudie", + "Claudina", + "Claudine", + "Claudio", + "Claudius", + "Claudy", + "Claus", + "Clausen", + "Clava", + "Clawson", + "Clay", + "Clayberg", + "Clayborn", + "Clayborne", + "Claybourne", + "Clayson", + "Clayton", + "Clea", + "Cleary", + "Cleasta", + "Cleave", + "Cleaves", + "Cleavland", + "Clein", + "Cleland", + "Clellan", + "Clem", + "Clemen", + "Clemence", + "Clemens", + "Clement", + "Clementas", + "Clemente", + "Clementi", + "Clementia", + "Clementina", + "Clementine", + "Clementis", + "Clementius", + "Clements", + "Clemmie", + "Clemmy", + "Cleo", + "Cleodal", + "Cleodel", + "Cleodell", + "Cleon", + "Cleopatra", + "Cleopatre", + "Clerc", + "Clercq", + "Clere", + "Cleres", + "Clerissa", + "Clerk", + "Cleti", + "Cletis", + "Cletus", + "Cleve", + "Cleveland", + "Clevey", + "Clevie", + "Clie", + "Cliff", + "Cliffes", + "Clifford", + "Clift", + "Clifton", + "Clim", + "Cline", + "Clint", + "Clintock", + "Clinton", + "Clio", + "Clippard", + "Clite", + "Clive", + "Clo", + "Cloe", + "Cloots", + "Clorinda", + "Clorinde", + "Cloris", + "Close", + "Clothilde", + "Clotilda", + "Clotilde", + "Clough", + "Clougher", + "Cloutman", + "Clova", + "Clovah", + "Clover", + "Clovis", + "Clower", + "Clute", + "Cly", + "Clyde", + "Clymer", + "Clynes", + "Clyte", + "Clyve", + "Clywd", + "Cnut", + "Coad", + "Coady", + "Coates", + "Coats", + "Cob", + "Cobb", + "Cobbie", + "Cobby", + "Coben", + "Cochard", + "Cochran", + "Cochrane", + "Cock", + "Cockburn", + "Cocke", + "Cocks", + "Coco", + "Codd", + "Codding", + "Codee", + "Codel", + "Codi", + "Codie", + "Cody", + "Coe", + "Coffee", + "Coffeng", + "Coffey", + "Coffin", + "Cofsky", + "Cogan", + "Cogen", + "Cogswell", + "Coh", + "Cohbath", + "Cohberg", + "Cohbert", + "Cohby", + "Cohdwell", + "Cohe", + "Coheman", + "Cohen", + "Cohette", + "Cohin", + "Cohl", + "Cohla", + "Cohleen", + "Cohlette", + "Cohlier", + "Cohligan", + "Cohn", + "Cointon", + "Coit", + "Coke", + "Col", + "Colan", + "Colas", + "Colb", + "Colbert", + "Colburn", + "Colby", + "Colbye", + "Cole", + "Coleen", + "Coleman", + "Colene", + "Colet", + "Coletta", + "Colette", + "Coleville", + "Colfin", + "Colier", + "Colin", + "Colinson", + "Colis", + "Collar", + "Collayer", + "Collbaith", + "Colleen", + "Collen", + "Collete", + "Collette", + "Colley", + "Collie", + "Collier", + "Colligan", + "Collimore", + "Collin", + "Colline", + "Collins", + "Collis", + "Collum", + "Colly", + "Collyer", + "Colman", + "Colner", + "Colombi", + "Colon", + "Colp", + "Colpin", + "Colson", + "Colston", + "Colt", + "Coltin", + "Colton", + "Coltson", + "Coltun", + "Columba", + "Columbine", + "Columbus", + "Columbyne", + "Colver", + "Colvert", + "Colville", + "Colvin", + "Colwell", + "Colwen", + "Colwin", + "Colyer", + "Combe", + "Combes", + "Combs", + "Comfort", + "Compte", + "Comptom", + "Compton", + "Comras", + "Comstock", + "Comyns", + "Con", + "Conah", + "Conal", + "Conall", + "Conan", + "Conant", + "Conard", + "Concepcion", + "Concettina", + "Concha", + "Conchita", + "Concoff", + "Concordia", + "Condon", + "Coney", + "Congdon", + "Conger", + "Coniah", + "Conias", + "Conlan", + "Conlee", + "Conlen", + "Conley", + "Conlin", + "Conlon", + "Conn", + "Connel", + "Connell", + "Connelley", + "Connelly", + "Conner", + "Conners", + "Connett", + "Conney", + "Conni", + "Connie", + "Connolly", + "Connor", + "Connors", + "Conny", + "Conover", + "Conrad", + "Conrade", + "Conrado", + "Conroy", + "Consalve", + "Consolata", + "Constance", + "Constancia", + "Constancy", + "Constant", + "Constanta", + "Constantia", + "Constantin", + "Constantina", + "Constantine", + "Constantino", + "Consuela", + "Consuelo", + "Conte", + "Conti", + "Converse", + "Convery", + "Conway", + "Cony", + "Conyers", + "Cooe", + "Cook", + "Cooke", + "Cookie", + "Cooley", + "Coombs", + "Coonan", + "Coop", + "Cooper", + "Cooperman", + "Coopersmith", + "Cooperstein", + "Cope", + "Copeland", + "Copland", + "Coplin", + "Copp", + "Coppinger", + "Coppins", + "Coppock", + "Coppola", + "Cora", + "Corabel", + "Corabella", + "Corabelle", + "Coral", + "Coralie", + "Coraline", + "Coralyn", + "Coray", + "Corbet", + "Corbett", + "Corbie", + "Corbin", + "Corby", + "Cord", + "Cordalia", + "Cordeelia", + "Cordelia", + "Cordelie", + "Cordell", + "Corder", + "Cordey", + "Cordi", + "Cordie", + "Cordier", + "Cordle", + "Cordova", + "Cordula", + "Cordy", + "Coreen", + "Corel", + "Corell", + "Corella", + "Corena", + "Corenda", + "Corene", + "Coretta", + "Corette", + "Corey", + "Cori", + "Coridon", + "Corie", + "Corilla", + "Corin", + "Corina", + "Corine", + "Corinna", + "Corinne", + "Coriss", + "Corissa", + "Corkhill", + "Corley", + "Corliss", + "Corly", + "Cormac", + "Cormack", + "Cormick", + "Cormier", + "Cornall", + "Corneille", + "Cornel", + "Cornela", + "Cornelia", + "Cornelie", + "Cornelius", + "Cornell", + "Cornelle", + "Cornew", + "Corney", + "Cornia", + "Cornie", + "Cornish", + "Cornwall", + "Cornwell", + "Corny", + "Corotto", + "Correna", + "Correy", + "Corri", + "Corrianne", + "Corrie", + "Corrina", + "Corrine", + "Corrinne", + "Corron", + "Corry", + "Corsetti", + "Corsiglia", + "Corso", + "Corson", + "Cort", + "Cortie", + "Cortney", + "Corty", + "Corvese", + "Corvin", + "Corwin", + "Corwun", + "Cory", + "Coryden", + "Corydon", + "Cos", + "Cosenza", + "Cosetta", + "Cosette", + "Coshow", + "Cosimo", + "Cosma", + "Cosme", + "Cosmo", + "Cost", + "Costa", + "Costanza", + "Costanzia", + "Costello", + "Coster", + "Costin", + "Cote", + "Cotsen", + "Cott", + "Cotter", + "Cotterell", + "Cottle", + "Cottrell", + "Coucher", + "Couchman", + "Coughlin", + "Coulombe", + "Coulson", + "Coulter", + "Coumas", + "Countess", + "Courcy", + "Court", + "Courtenay", + "Courtland", + "Courtnay", + "Courtney", + "Courtund", + "Cousin", + "Cousins", + "Coussoule", + "Couture", + "Covell", + "Coveney", + "Cowan", + "Coward", + "Cowden", + "Cowen", + "Cower", + "Cowey", + "Cowie", + "Cowles", + "Cowley", + "Cown", + "Cox", + "Coy", + "Coyle", + "Cozmo", + "Cozza", + "Crabb", + "Craddock", + "Craggie", + "Craggy", + "Craig", + "Crain", + "Cralg", + "Cram", + "Cramer", + "Cran", + "Crandale", + "Crandall", + "Crandell", + "Crane", + "Craner", + "Cranford", + "Cranston", + "Crary", + "Craven", + "Craw", + "Crawford", + "Crawley", + "Creamer", + "Crean", + "Creath", + "Creedon", + "Creigh", + "Creight", + "Creighton", + "Crelin", + "Crellen", + "Crenshaw", + "Cresa", + "Crescantia", + "Crescen", + "Crescentia", + "Crescin", + "Crescint", + "Cresida", + "Crespi", + "Crespo", + "Cressi", + "Cressida", + "Cressler", + "Cressy", + "Crichton", + "Crifasi", + "Crim", + "Crin", + "Cris", + "Crisey", + "Crispa", + "Crispas", + "Crispen", + "Crispin", + "Crissie", + "Crissy", + "Crist", + "Crista", + "Cristabel", + "Cristal", + "Cristen", + "Cristi", + "Cristian", + "Cristiano", + "Cristie", + "Cristin", + "Cristina", + "Cristine", + "Cristiona", + "Cristionna", + "Cristobal", + "Cristoforo", + "Cristy", + "Criswell", + "Critchfield", + "Critta", + "Crocker", + "Crockett", + "Crofoot", + "Croft", + "Crofton", + "Croix", + "Crompton", + "Cromwell", + "Croner", + "Cronin", + "Crooks", + "Croom", + "Crosby", + "Crosley", + "Cross", + "Crosse", + "Croteau", + "Crotty", + "Crow", + "Crowe", + "Crowell", + "Crowley", + "Crowns", + "Croydon", + "Cruce", + "Crudden", + "Cruickshank", + "Crutcher", + "Cruz", + "Cryan", + "Crysta", + "Crystal", + "Crystie", + "Cthrine", + "Cuda", + "Cudlip", + "Culberson", + "Culbert", + "Culbertson", + "Culhert", + "Cull", + "Cullan", + "Cullen", + "Culley", + "Cullie", + "Cullin", + "Culliton", + "Cully", + "Culosio", + "Culver", + "Cumine", + "Cumings", + "Cummine", + "Cummings", + "Cummins", + "Cung", + "Cunningham", + "Cupo", + "Curcio", + "Curhan", + "Curkell", + "Curley", + "Curnin", + "Curr", + "Curran", + "Curren", + "Currey", + "Currie", + "Currier", + "Curry", + "Curson", + "Curt", + "Curtice", + "Curtis", + "Curzon", + "Cusack", + "Cusick", + "Custer", + "Cut", + "Cutcheon", + "Cutcliffe", + "Cuthbert", + "Cuthbertson", + "Cuthburt", + "Cutler", + "Cutlerr", + "Cutlip", + "Cutlor", + "Cutter", + "Cuttie", + "Cuttler", + "Cutty", + "Cuyler", + "Cy", + "Cyb", + "Cybil", + "Cybill", + "Cychosz", + "Cyd", + "Cykana", + "Cyler", + "Cyma", + "Cymbre", + "Cyn", + "Cyna", + "Cynar", + "Cynara", + "Cynarra", + "Cynde", + "Cyndi", + "Cyndia", + "Cyndie", + "Cyndy", + "Cynera", + "Cynth", + "Cynthea", + "Cynthia", + "Cynthie", + "Cynthla", + "Cynthy", + "Cyprian", + "Cyprio", + "Cypro", + "Cyprus", + "Cyrano", + "Cyrie", + "Cyril", + "Cyrill", + "Cyrilla", + "Cyrille", + "Cyrillus", + "Cyrus", + "Czarra", + "D'Arcy", + "Dabbs", + "Daberath", + "Dabney", + "Dace", + "Dacey", + "Dachi", + "Dachia", + "Dachy", + "Dacia", + "Dacie", + "Dacy", + "Daegal", + "Dael", + "Daffi", + "Daffie", + "Daffodil", + "Daffy", + "Dafna", + "Dafodil", + "Dag", + "Dagall", + "Daggett", + "Daggna", + "Dagley", + "Dagmar", + "Dagna", + "Dagnah", + "Dagney", + "Dagny", + "Dahl", + "Dahle", + "Dahlia", + "Dahlstrom", + "Daigle", + "Dail", + "Daile", + "Dailey", + "Daisey", + "Daisi", + "Daisie", + "Daisy", + "Daitzman", + "Dal", + "Dale", + "Dalenna", + "Daley", + "Dalia", + "Dalila", + "Dalis", + "Dall", + "Dallas", + "Dalli", + "Dallis", + "Dallman", + "Dallon", + "Daloris", + "Dalpe", + "Dalston", + "Dalt", + "Dalton", + "Dalury", + "Daly", + "Dam", + "Damal", + "Damalas", + "Damales", + "Damali", + "Damalis", + "Damalus", + "Damara", + "Damaris", + "Damarra", + "Dambro", + "Dame", + "Damek", + "Damian", + "Damiani", + "Damiano", + "Damick", + "Damicke", + "Damien", + "Damita", + "Damle", + "Damon", + "Damour", + "Dan", + "Dana", + "Danae", + "Danaher", + "Danais", + "Danas", + "Danby", + "Danczyk", + "Dane", + "Danell", + "Danella", + "Danelle", + "Danete", + "Danette", + "Daney", + "Danforth", + "Dang", + "Dani", + "Dania", + "Daniala", + "Danialah", + "Danica", + "Danice", + "Danie", + "Daniel", + "Daniela", + "Daniele", + "Daniell", + "Daniella", + "Danielle", + "Daniels", + "Danielson", + "Danieu", + "Danika", + "Danila", + "Danit", + "Danita", + "Daniyal", + "Dann", + "Danna", + "Dannel", + "Danni", + "Dannica", + "Dannie", + "Dannon", + "Danny", + "Dannye", + "Dante", + "Danuloff", + "Danya", + "Danyelle", + "Danyette", + "Danyluk", + "Danzig", + "Danziger", + "Dao", + "Daph", + "Daphene", + "Daphie", + "Daphna", + "Daphne", + "Dar", + "Dara", + "Darach", + "Darb", + "Darbee", + "Darbie", + "Darby", + "Darce", + "Darcee", + "Darcey", + "Darci", + "Darcia", + "Darcie", + "Darcy", + "Darda", + "Dardani", + "Dare", + "Dareece", + "Dareen", + "Darees", + "Darell", + "Darelle", + "Daren", + "Dari", + "Daria", + "Darian", + "Darice", + "Darill", + "Darin", + "Dario", + "Darius", + "Darken", + "Darla", + "Darleen", + "Darlene", + "Darline", + "Darlleen", + "Darmit", + "Darn", + "Darnall", + "Darnell", + "Daron", + "Darooge", + "Darra", + "Darrel", + "Darrell", + "Darrelle", + "Darren", + "Darrey", + "Darrick", + "Darrill", + "Darrin", + "Darrow", + "Darryl", + "Darryn", + "Darsey", + "Darsie", + "Dart", + "Darton", + "Darwen", + "Darwin", + "Darya", + "Daryl", + "Daryle", + "Daryn", + "Dash", + "Dasha", + "Dasi", + "Dasie", + "Dasteel", + "Dasya", + "Datha", + "Datnow", + "Daub", + "Daugherty", + "Daughtry", + "Daukas", + "Daune", + "Dav", + "Dave", + "Daveda", + "Daveen", + "Daven", + "Davena", + "Davenport", + "Daveta", + "Davey", + "David", + "Davida", + "Davidde", + "Davide", + "Davidoff", + "Davidson", + "Davie", + "Davies", + "Davilman", + "Davin", + "Davina", + "Davine", + "Davis", + "Davison", + "Davita", + "Davon", + "Davy", + "Dawes", + "Dawkins", + "Dawn", + "Dawna", + "Dawson", + "Day", + "Daye", + "Dayle", + "Dayna", + "Ddene", + "De", + "De Witt", + "Deach", + "Deacon", + "Deadman", + "Dean", + "Deana", + "Deane", + "Deaner", + "Deanna", + "Deanne", + "Dearborn", + "Dearden", + "Dearman", + "Dearr", + "Deb", + "Debarath", + "Debbee", + "Debbi", + "Debbie", + "Debbra", + "Debby", + "Debee", + "Debera", + "Debi", + "Debor", + "Debora", + "Deborah", + "Deborath", + "Debra", + "Decamp", + "Decato", + "Decca", + "December", + "Decima", + "Deck", + "Decker", + "Deckert", + "Declan", + "Dede", + "Deden", + "Dedie", + "Dedra", + "Dedric", + "Dedrick", + "Dee", + "Dee Dee", + "DeeAnn", + "Deeann", + "Deeanne", + "Deedee", + "Deegan", + "Deena", + "Deenya", + "Deer", + "Deerdre", + "Deering", + "Deery", + "Deeyn", + "Defant", + "Dehlia", + "Dehnel", + "Deibel", + "Deidre", + "Deina", + "Deirdra", + "Deirdre", + "Dekeles", + "Dekow", + "Del", + "Dela", + "Delacourt", + "Delaine", + "Delainey", + "Delamare", + "Deland", + "Delaney", + "Delanie", + "Delano", + "Delanos", + "Delanty", + "Delaryd", + "Delastre", + "Delbert", + "Delcina", + "Delcine", + "Delfeena", + "Delfine", + "Delgado", + "Delia", + "Delija", + "Delila", + "Delilah", + "Delinda", + "Delisle", + "Dell", + "Della", + "Delle", + "Dellora", + "Delly", + "Delmar", + "Delmer", + "Delmor", + "Delmore", + "Delogu", + "Delora", + "Delorenzo", + "Delores", + "Deloria", + "Deloris", + "Delos", + "Delp", + "Delphina", + "Delphine", + "Delphinia", + "Delsman", + "Delwin", + "Delwyn", + "Demaggio", + "Demakis", + "Demaria", + "Demb", + "Demeter", + "Demetra", + "Demetre", + "Demetri", + "Demetria", + "Demetris", + "Demetrius", + "Demeyer", + "Deming", + "Demitria", + "Demmer", + "Demmy", + "Demodena", + "Demona", + "Demott", + "Demp", + "Dempsey", + "Dempster", + "Dempstor", + "Demy", + "Den", + "Dena", + "Denae", + "Denbrook", + "Denby", + "Dene", + "Deni", + "Denice", + "Denie", + "Denis", + "Denise", + "Denison", + "Denman", + "Denn", + "Denna", + "Dennard", + "Dennet", + "Dennett", + "Denney", + "Denni", + "Dennie", + "Dennis", + "Dennison", + "Denny", + "Denoting", + "Dent", + "Denten", + "Denton", + "Denver", + "Deny", + "Denys", + "Denyse", + "Denzil", + "Deonne", + "Depoliti", + "Deppy", + "Der", + "Deragon", + "Derayne", + "Derby", + "Dercy", + "Derek", + "Derian", + "Derick", + "Derina", + "Derinna", + "Derk", + "Derman", + "Dermot", + "Dermott", + "Derna", + "Deron", + "Deroo", + "Derr", + "Derrek", + "Derrick", + "Derriey", + "Derrik", + "Derril", + "Derron", + "Derry", + "Derte", + "Derward", + "Derwin", + "Derwon", + "Derwood", + "Deryl", + "Derzon", + "Des", + "Desai", + "Desberg", + "Descombes", + "Desdamona", + "Desdamonna", + "Desdee", + "Desdemona", + "Desi", + "Desimone", + "Desirae", + "Desirea", + "Desireah", + "Desiree", + "Desiri", + "Desma", + "Desmond", + "Desmund", + "Dessma", + "Desta", + "Deste", + "Destinee", + "Deth", + "Dett", + "Detta", + "Dettmer", + "Deuno", + "Deutsch", + "Dev", + "Deva", + "Devan", + "Devaney", + "Dever", + "Devi", + "Devin", + "Devina", + "Devine", + "Devinna", + "Devinne", + "Devitt", + "Devland", + "Devlen", + "Devlin", + "Devol", + "Devon", + "Devona", + "Devondra", + "Devonna", + "Devonne", + "Devora", + "Devy", + "Dew", + "Dewain", + "Dewar", + "Dewayne", + "Dewees", + "Dewey", + "Dewhirst", + "Dewhurst", + "Dewie", + "Dewitt", + "Dex", + "Dexter", + "Dey", + "Dhar", + "Dhiman", + "Dhiren", + "Dhruv", + "Dhu", + "Dhumma", + "Di", + "Diahann", + "Diamante", + "Diamond", + "Dian", + "Diana", + "Diandra", + "Diandre", + "Diane", + "Diane-Marie", + "Dianemarie", + "Diann", + "Dianna", + "Dianne", + "Diannne", + "Diantha", + "Dianthe", + "Diao", + "Diarmid", + "Diarmit", + "Diarmuid", + "Diaz", + "Dib", + "Diba", + "Dibb", + "Dibbell", + "Dibbrun", + "Dibri", + "Dibrin", + "Dibru", + "Dich", + "Dichy", + "Dick", + "Dickens", + "Dickenson", + "Dickerson", + "Dickey", + "Dickie", + "Dickinson", + "Dickman", + "Dicks", + "Dickson", + "Dicky", + "Didi", + "Didier", + "Dido", + "Dieball", + "Diego", + "Diehl", + "Diella", + "Dielle", + "Dielu", + "Diena", + "Dierdre", + "Dierolf", + "Diet", + "Dieter", + "Dieterich", + "Dietrich", + "Dietsche", + "Dietz", + "Dikmen", + "Dilan", + "Diley", + "Dilisio", + "Dilks", + "Dill", + "Dillie", + "Dillon", + "Dilly", + "Dimitri", + "Dimitris", + "Dimitry", + "Dimmick", + "Dimond", + "Dimphia", + "Dina", + "Dinah", + "Dinan", + "Dincolo", + "Dine", + "Dinerman", + "Dinesh", + "Dinin", + "Dinnage", + "Dinnie", + "Dinny", + "Dino", + "Dinsdale", + "Dinse", + "Dinsmore", + "Diogenes", + "Dion", + "Dione", + "Dionis", + "Dionisio", + "Dionne", + "Dionysus", + "Dippold", + "Dira", + "Dirk", + "Disario", + "Disharoon", + "Disini", + "Diskin", + "Diskson", + "Disraeli", + "Dita", + "Ditmore", + "Ditter", + "Dittman", + "Dituri", + "Ditzel", + "Diver", + "Divine", + "Dix", + "Dixie", + "Dixil", + "Dixon", + "Dmitri", + "Dniren", + "Doak", + "Doane", + "Dobb", + "Dobbins", + "Doble", + "Dobrinsky", + "Dobson", + "Docia", + "Docila", + "Docile", + "Docilla", + "Docilu", + "Dodd", + "Dodds", + "Dode", + "Dodge", + "Dodi", + "Dodie", + "Dodson", + "Dodwell", + "Dody", + "Doe", + "Doehne", + "Doelling", + "Doerrer", + "Doersten", + "Doggett", + "Dogs", + "Doherty", + "Doi", + "Doig", + "Dola", + "Dolan", + "Dole", + "Doley", + "Dolf", + "Dolhenty", + "Doll", + "Dollar", + "Dolley", + "Dolli", + "Dollie", + "Dolloff", + "Dolly", + "Dolora", + "Dolores", + "Dolorita", + "Doloritas", + "Dolph", + "Dolphin", + "Dom", + "Domash", + "Dombrowski", + "Domel", + "Domela", + "Domella", + "Domenech", + "Domenic", + "Domenico", + "Domeniga", + "Domineca", + "Dominga", + "Domingo", + "Domini", + "Dominic", + "Dominica", + "Dominick", + "Dominik", + "Dominique", + "Dominus", + "Dominy", + "Domonic", + "Domph", + "Don", + "Dona", + "Donadee", + "Donaghue", + "Donahoe", + "Donahue", + "Donal", + "Donald", + "Donaldson", + "Donall", + "Donalt", + "Donata", + "Donatelli", + "Donaugh", + "Donavon", + "Donegan", + "Donela", + "Donell", + "Donella", + "Donelle", + "Donelson", + "Donelu", + "Doner", + "Donetta", + "Dong", + "Donia", + "Donica", + "Donielle", + "Donn", + "Donna", + "Donnamarie", + "Donnell", + "Donnelly", + "Donnenfeld", + "Donni", + "Donnie", + "Donny", + "Donoghue", + "Donoho", + "Donohue", + "Donough", + "Donovan", + "Doolittle", + "Doone", + "Dopp", + "Dora", + "Doralia", + "Doralin", + "Doralyn", + "Doralynn", + "Doralynne", + "Doran", + "Dorca", + "Dorcas", + "Dorcea", + "Dorcia", + "Dorcus", + "Dorcy", + "Dore", + "Doreen", + "Dorelia", + "Dorella", + "Dorelle", + "Dorena", + "Dorene", + "Doretta", + "Dorette", + "Dorey", + "Dorfman", + "Dori", + "Doria", + "Dorian", + "Dorice", + "Dorie", + "Dorin", + "Dorina", + "Dorinda", + "Dorine", + "Dorion", + "Doris", + "Dorisa", + "Dorise", + "Dorison", + "Dorita", + "Dorkas", + "Dorkus", + "Dorlisa", + "Dorman", + "Dorn", + "Doro", + "Dorolice", + "Dorolisa", + "Dorotea", + "Doroteya", + "Dorothea", + "Dorothee", + "Dorothi", + "Dorothy", + "Dorr", + "Dorran", + "Dorree", + "Dorren", + "Dorri", + "Dorrie", + "Dorris", + "Dorry", + "Dorsey", + "Dorsman", + "Dorsy", + "Dorthea", + "Dorthy", + "Dorweiler", + "Dorwin", + "Dory", + "Doscher", + "Dosh", + "Dosi", + "Dosia", + "Doss", + "Dot", + "Doti", + "Dotson", + "Dott", + "Dotti", + "Dottie", + "Dotty", + "Doty", + "Doubler", + "Doug", + "Dougal", + "Dougald", + "Dougall", + "Dougherty", + "Doughman", + "Doughty", + "Dougie", + "Douglas", + "Douglass", + "Dougy", + "Douty", + "Douville", + "Dov", + "Dove", + "Dovev", + "Dow", + "Dowd", + "Dowdell", + "Dowell", + "Dowlen", + "Dowling", + "Down", + "Downall", + "Downe", + "Downes", + "Downey", + "Downing", + "Downs", + "Dowski", + "Dowzall", + "Doxia", + "Doy", + "Doykos", + "Doyle", + "Drabeck", + "Dragelin", + "Dragon", + "Dragone", + "Dragoon", + "Drain", + "Drais", + "Drake", + "Drandell", + "Drape", + "Draper", + "Dray", + "Dre", + "Dream", + "Dreda", + "Dreddy", + "Dredi", + "Dreeda", + "Dreher", + "Dremann", + "Drescher", + "Dressel", + "Dressler", + "Drew", + "Drewett", + "Drews", + "Drexler", + "Dreyer", + "Dric", + "Drice", + "Drida", + "Dripps", + "Driscoll", + "Driskill", + "Drisko", + "Drislane", + "Drobman", + "Drogin", + "Drolet", + "Drona", + "Dronski", + "Drooff", + "Dru", + "Druce", + "Druci", + "Drucie", + "Drucill", + "Drucilla", + "Drucy", + "Drud", + "Drue", + "Drugge", + "Drugi", + "Drummond", + "Drus", + "Drusi", + "Drusie", + "Drusilla", + "Drusus", + "Drusy", + "Dry", + "Dryden", + "Drye", + "Dryfoos", + "DuBois", + "Duane", + "Duarte", + "Duax", + "Dubenko", + "Dublin", + "Ducan", + "Duck", + "Dud", + "Dudden", + "Dudley", + "Duer", + "Duester", + "Duff", + "Duffie", + "Duffy", + "Dugaid", + "Dugald", + "Dugan", + "Dugas", + "Duggan", + "Duhl", + "Duke", + "Dukey", + "Dukie", + "Duky", + "Dulce", + "Dulcea", + "Dulci", + "Dulcia", + "Dulciana", + "Dulcie", + "Dulcine", + "Dulcinea", + "Dulcle", + "Dulcy", + "Duleba", + "Dulla", + "Dulsea", + "Duma", + "Dumah", + "Dumanian", + "Dumas", + "Dumm", + "Dumond", + "Dun", + "Dunaville", + "Dunc", + "Duncan", + "Dunham", + "Dunkin", + "Dunlavy", + "Dunn", + "Dunning", + "Dunseath", + "Dunson", + "Dunstan", + "Dunston", + "Dunton", + "Duntson", + "Duong", + "Dupaix", + "Dupin", + "Dupre", + "Dupuis", + "Dupuy", + "Duquette", + "Dur", + "Durand", + "Durant", + "Durante", + "Durarte", + "Durer", + "Durgy", + "Durham", + "Durkee", + "Durkin", + "Durman", + "Durnan", + "Durning", + "Durno", + "Durr", + "Durrace", + "Durrell", + "Durrett", + "Durst", + "Durstin", + "Durston", + "Durtschi", + "Durward", + "Durware", + "Durwin", + "Durwood", + "Durwyn", + "Dusa", + "Dusen", + "Dust", + "Dustan", + "Duster", + "Dustie", + "Dustin", + "Dustman", + "Duston", + "Dusty", + "Dusza", + "Dutch", + "Dutchman", + "Duthie", + "Duval", + "Duvall", + "Duwalt", + "Duwe", + "Duyne", + "Dwain", + "Dwaine", + "Dwan", + "Dwane", + "Dwayne", + "Dweck", + "Dwight", + "Dwinnell", + "Dworman", + "Dwyer", + "Dyal", + "Dyan", + "Dyana", + "Dyane", + "Dyann", + "Dyanna", + "Dyanne", + "Dyche", + "Dyer", + "Dygal", + "Dygall", + "Dygert", + "Dyke", + "Dyl", + "Dylan", + "Dylana", + "Dylane", + "Dymoke", + "Dympha", + "Dymphia", + "Dyna", + "Dynah", + "Dysart", + "Dyson", + "Dyun", + "Dzoba", + "Eachelle", + "Eachern", + "Eada", + "Eade", + "Eadie", + "Eadith", + "Eadmund", + "Eads", + "Eadwina", + "Eadwine", + "Eagle", + "Eal", + "Ealasaid", + "Eamon", + "Eanore", + "Earl", + "Earla", + "Earle", + "Earleen", + "Earlene", + "Earley", + "Earlie", + "Early", + "Eartha", + "Earvin", + "East", + "Easter", + "Eastlake", + "Eastman", + "Easton", + "Eaton", + "Eatton", + "Eaves", + "Eb", + "Eba", + "Ebarta", + "Ebba", + "Ebbarta", + "Ebberta", + "Ebbie", + "Ebby", + "Eben", + "Ebeneser", + "Ebenezer", + "Eberhard", + "Eberhart", + "Eberle", + "Eberly", + "Ebert", + "Eberta", + "Eberto", + "Ebner", + "Ebneter", + "Eboh", + "Ebonee", + "Ebony", + "Ebsen", + "Echikson", + "Echo", + "Eckardt", + "Eckart", + "Eckblad", + "Eckel", + "Eckhardt", + "Eckmann", + "Econah", + "Ed", + "Eda", + "Edan", + "Edana", + "Edbert", + "Edd", + "Edda", + "Eddana", + "Eddi", + "Eddie", + "Eddina", + "Eddra", + "Eddy", + "Ede", + "Edea", + "Edee", + "Edeline", + "Edelman", + "Edelson", + "Edelstein", + "Edelsten", + "Eden", + "Edette", + "Edgar", + "Edgard", + "Edgardo", + "Edge", + "Edgell", + "Edgerton", + "Edholm", + "Edi", + "Edie", + "Edik", + "Edin", + "Edina", + "Edison", + "Edita", + "Edith", + "Editha", + "Edithe", + "Ediva", + "Edla", + "Edlin", + "Edlun", + "Edlyn", + "Edmanda", + "Edme", + "Edmea", + "Edmead", + "Edmee", + "Edmon", + "Edmond", + "Edmonda", + "Edmondo", + "Edmonds", + "Edmund", + "Edmunda", + "Edna", + "Edny", + "Edora", + "Edouard", + "Edra", + "Edrea", + "Edrei", + "Edric", + "Edrick", + "Edris", + "Edrock", + "Edroi", + "Edsel", + "Edson", + "Eduard", + "Eduardo", + "Eduino", + "Edva", + "Edvard", + "Edveh", + "Edward", + "Edwards", + "Edwin", + "Edwina", + "Edwine", + "Edwyna", + "Edy", + "Edyth", + "Edythe", + "Effie", + "Effy", + "Efram", + "Efrem", + "Efren", + "Efron", + "Efthim", + "Egan", + "Egarton", + "Egbert", + "Egerton", + "Eggett", + "Eggleston", + "Egide", + "Egidio", + "Egidius", + "Egin", + "Eglanteen", + "Eglantine", + "Egon", + "Egor", + "Egwan", + "Egwin", + "Ehling", + "Ehlke", + "Ehman", + "Ehr", + "Ehrenberg", + "Ehrlich", + "Ehrman", + "Ehrsam", + "Ehud", + "Ehudd", + "Eichman", + "Eidson", + "Eiger", + "Eileen", + "Eilis", + "Eimile", + "Einberger", + "Einhorn", + "Eipper", + "Eirena", + "Eirene", + "Eisele", + "Eisen", + "Eisenberg", + "Eisenhart", + "Eisenstark", + "Eiser", + "Eisinger", + "Eisler", + "Eiten", + "Ekaterina", + "El", + "Ela", + "Elah", + "Elaina", + "Elaine", + "Elana", + "Elane", + "Elata", + "Elatia", + "Elayne", + "Elazaro", + "Elbart", + "Elberfeld", + "Elbert", + "Elberta", + "Elbertina", + "Elbertine", + "Elboa", + "Elbring", + "Elburr", + "Elburt", + "Elconin", + "Elda", + "Elden", + "Elder", + "Eldin", + "Eldon", + "Eldora", + "Eldorado", + "Eldoree", + "Eldoria", + "Eldred", + "Eldreda", + "Eldredge", + "Eldreeda", + "Eldrid", + "Eldrida", + "Eldridge", + "Eldwen", + "Eldwin", + "Eldwon", + "Eldwun", + "Eleanor", + "Eleanora", + "Eleanore", + "Eleazar", + "Electra", + "Eleen", + "Elena", + "Elene", + "Eleni", + "Elenore", + "Eleonora", + "Eleonore", + "Eleph", + "Elephus", + "Elery", + "Elexa", + "Elfie", + "Elfont", + "Elfreda", + "Elfrida", + "Elfrieda", + "Elfstan", + "Elga", + "Elgar", + "Eli", + "Elia", + "Eliades", + "Elianora", + "Elianore", + "Elias", + "Eliason", + "Eliath", + "Eliathan", + "Eliathas", + "Elicia", + "Elidad", + "Elie", + "Eliezer", + "Eliga", + "Elihu", + "Elijah", + "Elinor", + "Elinore", + "Eliot", + "Eliott", + "Elisa", + "Elisabet", + "Elisabeth", + "Elisabetta", + "Elise", + "Elisee", + "Eliseo", + "Elish", + "Elisha", + "Elison", + "Elissa", + "Elita", + "Eliza", + "Elizabet", + "Elizabeth", + "Elka", + "Elke", + "Elkin", + "Ella", + "Elladine", + "Ellan", + "Ellard", + "Ellary", + "Ellata", + "Elle", + "Ellen", + "Ellene", + "Ellerd", + "Ellerey", + "Ellersick", + "Ellery", + "Ellett", + "Ellette", + "Ellga", + "Elli", + "Ellicott", + "Ellie", + "Ellinger", + "Ellingston", + "Elliot", + "Elliott", + "Ellis", + "Ellison", + "Ellissa", + "Ellita", + "Ellmyer", + "Ellon", + "Ellora", + "Ellord", + "Ellswerth", + "Ellsworth", + "Ellwood", + "Elly", + "Ellyn", + "Ellynn", + "Elma", + "Elmajian", + "Elmaleh", + "Elman", + "Elmer", + "Elmina", + "Elmira", + "Elmo", + "Elmore", + "Elna", + "Elnar", + "Elnora", + "Elnore", + "Elo", + "Elodea", + "Elodia", + "Elodie", + "Eloisa", + "Eloise", + "Elon", + "Elonore", + "Elora", + "Elreath", + "Elrod", + "Elroy", + "Els", + "Elsa", + "Elsbeth", + "Else", + "Elset", + "Elsey", + "Elsi", + "Elsie", + "Elsinore", + "Elson", + "Elspet", + "Elspeth", + "Elstan", + "Elston", + "Elsworth", + "Elsy", + "Elton", + "Elum", + "Elurd", + "Elva", + "Elvah", + "Elvera", + "Elvia", + "Elvie", + "Elvin", + "Elvina", + "Elvira", + "Elvis", + "Elvyn", + "Elwaine", + "Elwee", + "Elwin", + "Elwina", + "Elwira", + "Elwood", + "Elwyn", + "Ely", + "Elyn", + "Elyse", + "Elysee", + "Elysha", + "Elysia", + "Elyssa", + "Em", + "Ema", + "Emad", + "Emalee", + "Emalia", + "Emanuel", + "Emanuela", + "Emanuele", + "Emarie", + "Embry", + "Emee", + "Emelda", + "Emelen", + "Emelia", + "Emelin", + "Emelina", + "Emeline", + "Emelita", + "Emelun", + "Emelyne", + "Emera", + "Emerald", + "Emeric", + "Emerick", + "Emersen", + "Emerson", + "Emery", + "Emie", + "Emil", + "Emile", + "Emilee", + "Emili", + "Emilia", + "Emilie", + "Emiline", + "Emilio", + "Emily", + "Emina", + "Emlen", + "Emlin", + "Emlyn", + "Emlynn", + "Emlynne", + "Emma", + "Emmalee", + "Emmaline", + "Emmalyn", + "Emmalynn", + "Emmalynne", + "Emmanuel", + "Emmeline", + "Emmer", + "Emmeram", + "Emmerich", + "Emmerie", + "Emmery", + "Emmet", + "Emmett", + "Emmey", + "Emmi", + "Emmie", + "Emmit", + "Emmons", + "Emmott", + "Emmuela", + "Emmy", + "Emmye", + "Emogene", + "Emory", + "Emrich", + "Emsmus", + "Emyle", + "Emylee", + "Enalda", + "Encrata", + "Encratia", + "Encratis", + "End", + "Ender", + "Endo", + "Endor", + "Endora", + "Endres", + "Enenstein", + "Eng", + "Engdahl", + "Engeddi", + "Engedi", + "Engedus", + "Engel", + "Engelbert", + "Engelhart", + "Engen", + "Engenia", + "England", + "Engle", + "Englebert", + "Engleman", + "Englis", + "English", + "Engracia", + "Engud", + "Engvall", + "Enid", + "Ennis", + "Eno", + "Enoch", + "Enos", + "Enrica", + "Enrichetta", + "Enrico", + "Enrika", + "Enrique", + "Enriqueta", + "Ensign", + "Ensoll", + "Entwistle", + "Enyedy", + "Eoin", + "Eolanda", + "Eolande", + "Eph", + "Ephraim", + "Ephram", + "Ephrayim", + "Ephrem", + "Epifano", + "Epner", + "Epp", + "Epperson", + "Eppes", + "Eppie", + "Epps", + "Epstein", + "Er", + "Eradis", + "Eran", + "Eras", + "Erasme", + "Erasmo", + "Erasmus", + "Erastatus", + "Eraste", + "Erastes", + "Erastus", + "Erb", + "Erbe", + "Erbes", + "Erda", + "Erdah", + "Erdda", + "Erde", + "Erdei", + "Erdman", + "Erdrich", + "Erek", + "Erelia", + "Erena", + "Erfert", + "Ergener", + "Erhard", + "Erhart", + "Eri", + "Eric", + "Erica", + "Erich", + "Ericha", + "Erick", + "Ericka", + "Ericksen", + "Erickson", + "Erida", + "Erie", + "Eriha", + "Erik", + "Erika", + "Erikson", + "Erin", + "Erina", + "Erine", + "Erinn", + "Erinna", + "Erkan", + "Erl", + "Erland", + "Erlandson", + "Erle", + "Erleena", + "Erlene", + "Erlewine", + "Erlin", + "Erlina", + "Erline", + "Erlinna", + "Erlond", + "Erma", + "Ermanno", + "Erme", + "Ermeena", + "Ermengarde", + "Ermentrude", + "Ermey", + "Ermin", + "Ermina", + "Ermine", + "Erminia", + "Erminie", + "Erminna", + "Ern", + "Erna", + "Ernald", + "Ernaldus", + "Ernaline", + "Ernest", + "Ernesta", + "Ernestine", + "Ernesto", + "Ernestus", + "Ernie", + "Ernst", + "Erny", + "Errecart", + "Errick", + "Errol", + "Erroll", + "Erskine", + "Ertha", + "Erund", + "Erv", + "ErvIn", + "Ervin", + "Ervine", + "Erving", + "Erwin", + "Eryn", + "Esau", + "Esbensen", + "Esbenshade", + "Esch", + "Esdras", + "Eshelman", + "Eshman", + "Eskil", + "Eskill", + "Esma", + "Esmaria", + "Esme", + "Esmeralda", + "Esmerelda", + "Esmerolda", + "Esmond", + "Espy", + "Esra", + "Essa", + "Essam", + "Essex", + "Essie", + "Essinger", + "Essy", + "Esta", + "Estas", + "Esteban", + "Estel", + "Estele", + "Estell", + "Estella", + "Estelle", + "Esten", + "Ester", + "Estes", + "Estevan", + "Estey", + "Esther", + "Estis", + "Estrella", + "Estrellita", + "Estren", + "Estrin", + "Estus", + "Eta", + "Etam", + "Etan", + "Etana", + "Etem", + "Ethan", + "Ethban", + "Ethben", + "Ethbin", + "Ethbinium", + "Ethbun", + "Ethe", + "Ethel", + "Ethelbert", + "Ethelda", + "Ethelin", + "Ethelind", + "Ethelinda", + "Etheline", + "Ethelred", + "Ethelstan", + "Ethelyn", + "Ethyl", + "Etienne", + "Etka", + "Etoile", + "Etom", + "Etra", + "Etrem", + "Etta", + "Ettari", + "Etti", + "Ettie", + "Ettinger", + "Ettore", + "Etty", + "Etz", + "Eudo", + "Eudoca", + "Eudocia", + "Eudora", + "Eudosia", + "Eudoxia", + "Euell", + "Eugen", + "Eugene", + "Eugenia", + "Eugenides", + "Eugenie", + "Eugenio", + "Eugenius", + "Eugeniusz", + "Eugenle", + "Eugine", + "Euh", + "Eula", + "Eulalee", + "Eulalia", + "Eulaliah", + "Eulalie", + "Eulau", + "Eunice", + "Eupheemia", + "Euphemia", + "Euphemiah", + "Euphemie", + "Euridice", + "Eurydice", + "Eusebio", + "Eustace", + "Eustache", + "Eustacia", + "Eustashe", + "Eustasius", + "Eustatius", + "Eustazio", + "Eustis", + "Euton", + "Ev", + "Eva", + "Evadne", + "Evadnee", + "Evaleen", + "Evalyn", + "Evan", + "Evander", + "Evangelia", + "Evangelin", + "Evangelina", + "Evangeline", + "Evangelist", + "Evania", + "Evanne", + "Evannia", + "Evans", + "Evante", + "Evanthe", + "Evars", + "Eve", + "Eveleen", + "Evelin", + "Evelina", + "Eveline", + "Evelinn", + "Evelunn", + "Evelyn", + "Even", + "Everara", + "Everard", + "Evered", + "Everest", + "Everett", + "Everick", + "Everrs", + "Evers", + "Eversole", + "Everson", + "Evetta", + "Evette", + "Evey", + "Evie", + "Evin", + "Evita", + "Evonne", + "Evoy", + "Evslin", + "Evvie", + "Evvy", + "Evy", + "Evyn", + "Ewald", + "Ewall", + "Ewan", + "Eward", + "Ewart", + "Ewell", + "Ewen", + "Ewens", + "Ewer", + "Ewold", + "Eyde", + "Eydie", + "Eyeleen", + "Eyla", + "Ez", + "Ezana", + "Ezar", + "Ezara", + "Ezaria", + "Ezarra", + "Ezarras", + "Ezechiel", + "Ezekiel", + "Ezequiel", + "Eziechiele", + "Ezmeralda", + "Ezra", + "Ezri", + "Ezzo", + "Fabe", + "Faber", + "Fabi", + "Fabian", + "Fabiano", + "Fabien", + "Fabio", + "Fabiola", + "Fabiolas", + "Fablan", + "Fabozzi", + "Fabri", + "Fabria", + "Fabriane", + "Fabrianna", + "Fabrianne", + "Fabrice", + "Fabrienne", + "Fabrin", + "Fabron", + "Fabyola", + "Fachan", + "Fachanan", + "Fachini", + "Fadden", + "Faden", + "Fadil", + "Fadiman", + "Fae", + "Fagaly", + "Fagan", + "Fagen", + "Fagin", + "Fahey", + "Fahland", + "Fahy", + "Fai", + "Faina", + "Fair", + "Fairbanks", + "Faires", + "Fairfax", + "Fairfield", + "Fairleigh", + "Fairley", + "Fairlie", + "Fairman", + "Fairweather", + "Faith", + "Fakieh", + "Falcone", + "Falconer", + "Falda", + "Faletti", + "Faline", + "Falito", + "Falk", + "Falkner", + "Fallon", + "Faludi", + "Falzetta", + "Fan", + "Fanchan", + "Fanchet", + "Fanchette", + "Fanchie", + "Fanchon", + "Fancie", + "Fancy", + "Fanechka", + "Fanestil", + "Fang", + "Fania", + "Fanni", + "Fannie", + "Fanning", + "Fanny", + "Fantasia", + "Fante", + "Fanya", + "Far", + "Fara", + "Farah", + "Farand", + "Farant", + "Farhi", + "Fari", + "Faria", + "Farica", + "Farika", + "Fariss", + "Farkas", + "Farl", + "Farland", + "Farlay", + "Farlee", + "Farleigh", + "Farley", + "Farlie", + "Farly", + "Farman", + "Farmann", + "Farmelo", + "Farmer", + "Farnham", + "Farnsworth", + "Farny", + "Faro", + "Farr", + "Farra", + "Farrah", + "Farrand", + "Farrar", + "Farrel", + "Farrell", + "Farrica", + "Farrington", + "Farris", + "Farrish", + "Farrison", + "Farro", + "Farron", + "Farrow", + "Faruq", + "Farver", + "Farwell", + "Fasano", + "Faso", + "Fassold", + "Fast", + "Fasta", + "Fasto", + "Fates", + "Fatima", + "Fatimah", + "Fatma", + "Fattal", + "Faubert", + "Faubion", + "Fauch", + "Faucher", + "Faulkner", + "Fauman", + "Faun", + "Faunia", + "Faunie", + "Faus", + "Faust", + "Fausta", + "Faustena", + "Faustina", + "Faustine", + "Faustus", + "Fauver", + "Faux", + "Favata", + "Favian", + "Favianus", + "Favien", + "Favin", + "Favrot", + "Fawcett", + "Fawcette", + "Fawn", + "Fawna", + "Fawne", + "Fawnia", + "Fax", + "Faxan", + "Faxen", + "Faxon", + "Faxun", + "Fay", + "Faydra", + "Faye", + "Fayette", + "Fayina", + "Fayola", + "Fayre", + "Fayth", + "Faythe", + "Fazeli", + "Fe", + "Featherstone", + "February", + "Fechter", + "Fedak", + "Federica", + "Federico", + "Fedirko", + "Fedora", + "Fee", + "Feeley", + "Feeney", + "Feer", + "Feigin", + "Feil", + "Fein", + "Feinberg", + "Feingold", + "Feinleib", + "Feinstein", + "Feld", + "Felder", + "Feldman", + "Feldstein", + "Feldt", + "Felecia", + "Feledy", + "Felic", + "Felicdad", + "Felice", + "Felicia", + "Felicidad", + "Felicie", + "Felicio", + "Felicity", + "Felicle", + "Felike", + "Feliks", + "Felipa", + "Felipe", + "Felise", + "Felisha", + "Felita", + "Felix", + "Feliza", + "Felizio", + "Fellner", + "Fellows", + "Felske", + "Felt", + "Felten", + "Feltie", + "Felton", + "Felty", + "Fem", + "Femi", + "Femmine", + "Fen", + "Fendig", + "Fenelia", + "Fenella", + "Fenn", + "Fennell", + "Fennelly", + "Fenner", + "Fennessy", + "Fennie", + "Fenny", + "Fenton", + "Fenwick", + "Feodor", + "Feodora", + "Feodore", + "Feola", + "Ferd", + "Ferde", + "Ferdie", + "Ferdinana", + "Ferdinand", + "Ferdinanda", + "Ferdinande", + "Ferdy", + "Fergus", + "Ferguson", + "Feriga", + "Ferino", + "Fermin", + "Fern", + "Ferna", + "Fernald", + "Fernand", + "Fernanda", + "Fernande", + "Fernandes", + "Fernandez", + "Fernandina", + "Fernando", + "Fernas", + "Ferne", + "Ferneau", + "Fernyak", + "Ferrand", + "Ferreby", + "Ferree", + "Ferrel", + "Ferrell", + "Ferren", + "Ferretti", + "Ferri", + "Ferrick", + "Ferrigno", + "Ferris", + "Ferriter", + "Ferro", + "Ferullo", + "Ferwerda", + "Festa", + "Festatus", + "Festus", + "Feucht", + "Feune", + "Fevre", + "Fey", + "Fi", + "Fia", + "Fiann", + "Fianna", + "Fidel", + "Fidela", + "Fidelas", + "Fidele", + "Fidelia", + "Fidelio", + "Fidelis", + "Fidelity", + "Fidellas", + "Fidellia", + "Fiden", + "Fidole", + "Fiedler", + "Fiedling", + "Field", + "Fielding", + "Fields", + "Fiertz", + "Fiester", + "Fife", + "Fifi", + "Fifine", + "Figge", + "Figone", + "Figueroa", + "Filbert", + "Filberte", + "Filberto", + "Filemon", + "Files", + "Filia", + "Filiano", + "Filide", + "Filip", + "Filipe", + "Filippa", + "Filippo", + "Fillander", + "Fillbert", + "Fillender", + "Filler", + "Fillian", + "Filmer", + "Filmore", + "Filomena", + "Fin", + "Fina", + "Finbar", + "Finbur", + "Findlay", + "Findley", + "Fine", + "Fineberg", + "Finegan", + "Finella", + "Fineman", + "Finer", + "Fini", + "Fink", + "Finkelstein", + "Finlay", + "Finley", + "Finn", + "Finnegan", + "Finnie", + "Finnigan", + "Finny", + "Finstad", + "Finzer", + "Fiona", + "Fionna", + "Fionnula", + "Fiora", + "Fiore", + "Fiorenza", + "Fiorenze", + "Firestone", + "Firman", + "Firmin", + "Firooc", + "Fisch", + "Fischer", + "Fish", + "Fishback", + "Fishbein", + "Fisher", + "Fishman", + "Fisk", + "Fiske", + "Fisken", + "Fitting", + "Fitton", + "Fitts", + "Fitz", + "Fitzger", + "Fitzgerald", + "Fitzhugh", + "Fitzpatrick", + "Fitzsimmons", + "Flagler", + "Flaherty", + "Flam", + "Flan", + "Flanagan", + "Flanders", + "Flanigan", + "Flann", + "Flanna", + "Flannery", + "Flatto", + "Flavia", + "Flavian", + "Flavio", + "Flavius", + "Fleck", + "Fleda", + "Fleece", + "Fleeman", + "Fleeta", + "Fleischer", + "Fleisher", + "Fleisig", + "Flem", + "Fleming", + "Flemings", + "Flemming", + "Flessel", + "Fleta", + "Fletch", + "Fletcher", + "Fleur", + "Fleurette", + "Flieger", + "Flight", + "Flin", + "Flinn", + "Flint", + "Flip", + "Flita", + "Flo", + "Floeter", + "Flor", + "Flora", + "Florance", + "Flore", + "Florella", + "Florence", + "Florencia", + "Florentia", + "Florenza", + "Florette", + "Flori", + "Floria", + "Florian", + "Florida", + "Floridia", + "Florie", + "Florin", + "Florina", + "Florinda", + "Florine", + "Florio", + "Floris", + "Floro", + "Florri", + "Florrie", + "Florry", + "Flory", + "Flosi", + "Floss", + "Flosser", + "Flossi", + "Flossie", + "Flossy", + "Flower", + "Flowers", + "Floyd", + "Flss", + "Flyn", + "Flynn", + "Foah", + "Fogarty", + "Fogel", + "Fogg", + "Fokos", + "Folberth", + "Foley", + "Folger", + "Follansbee", + "Follmer", + "Folly", + "Folsom", + "Fonda", + "Fondea", + "Fong", + "Fons", + "Fonseca", + "Fonsie", + "Fontana", + "Fontes", + "Fonville", + "Fonz", + "Fonzie", + "Foote", + "Forbes", + "Forcier", + "Ford", + "Fording", + "Forelli", + "Forest", + "Forester", + "Forkey", + "Forland", + "Forlini", + "Formenti", + "Formica", + "Fornof", + "Forras", + "Forrer", + "Forrest", + "Forrester", + "Forsta", + "Forster", + "Forsyth", + "Forta", + "Fortier", + "Fortin", + "Fortna", + "Fortuna", + "Fortunato", + "Fortune", + "Fortunia", + "Fortunio", + "Fortunna", + "Forward", + "Foscalina", + "Fosdick", + "Foskett", + "Fosque", + "Foss", + "Foster", + "Fotina", + "Fotinas", + "Fougere", + "Foulk", + "Four", + "Foushee", + "Fowkes", + "Fowle", + "Fowler", + "Fox", + "Foy", + "Fraase", + "Fradin", + "Frager", + "Frame", + "Fran", + "France", + "Francene", + "Frances", + "Francesca", + "Francesco", + "Franchot", + "Franci", + "Francie", + "Francine", + "Francis", + "Francisca", + "Franciscka", + "Francisco", + "Franciska", + "Franciskus", + "Franck", + "Francklin", + "Francklyn", + "Franckot", + "Francois", + "Francoise", + "Francyne", + "Franek", + "Frangos", + "Frank", + "Frankel", + "Frankhouse", + "Frankie", + "Franklin", + "Franklyn", + "Franky", + "Franni", + "Frannie", + "Franny", + "Frans", + "Fransen", + "Fransis", + "Fransisco", + "Frants", + "Frantz", + "Franz", + "Franza", + "Franzen", + "Franzoni", + "Frasch", + "Frasco", + "Fraser", + "Frasier", + "Frasquito", + "Fraya", + "Frayda", + "Frayne", + "Fraze", + "Frazer", + "Frazier", + "Frear", + "Freberg", + "Frech", + "Frechette", + "Fred", + "Freda", + "Freddi", + "Freddie", + "Freddy", + "Fredek", + "Fredel", + "Fredela", + "Fredelia", + "Fredella", + "Fredenburg", + "Frederic", + "Frederica", + "Frederich", + "Frederick", + "Fredericka", + "Frederico", + "Frederigo", + "Frederik", + "Frederiksen", + "Frederique", + "Fredette", + "Fredi", + "Fredia", + "Fredie", + "Fredkin", + "Fredra", + "Fredric", + "Fredrick", + "Fredrika", + "Free", + "Freeborn", + "Freed", + "Freedman", + "Freeland", + "Freeman", + "Freemon", + "Fregger", + "Freida", + "Freiman", + "Fremont", + "French", + "Frendel", + "Frentz", + "Frere", + "Frerichs", + "Fretwell", + "Freud", + "Freudberg", + "Frey", + "Freya", + "Freyah", + "Freytag", + "Frick", + "Fricke", + "Frida", + "Friday", + "Fridell", + "Fridlund", + "Fried", + "Frieda", + "Friedberg", + "Friede", + "Frieder", + "Friederike", + "Friedland", + "Friedlander", + "Friedly", + "Friedman", + "Friedrich", + "Friedrick", + "Friend", + "Frierson", + "Fries", + "Frisse", + "Frissell", + "Fritts", + "Fritz", + "Fritze", + "Fritzie", + "Fritzsche", + "Frodeen", + "Frodi", + "Frodin", + "Frodina", + "Frodine", + "Froehlich", + "Froemming", + "Froh", + "Frohman", + "Frohne", + "Frolick", + "Froma", + "Fromma", + "Fronia", + "Fronnia", + "Fronniah", + "Frost", + "Fruin", + "Frulla", + "Frum", + "Fruma", + "Fry", + "Fryd", + "Frydman", + "Frye", + "Frymire", + "Fu", + "Fuchs", + "Fugate", + "Fugazy", + "Fugere", + "Fuhrman", + "Fujio", + "Ful", + "Fulbert", + "Fulbright", + "Fulcher", + "Fuld", + "Fulks", + "Fuller", + "Fullerton", + "Fulmer", + "Fulmis", + "Fulton", + "Fulvi", + "Fulvia", + "Fulviah", + "Funch", + "Funda", + "Funk", + "Furey", + "Furgeson", + "Furie", + "Furiya", + "Furlani", + "Furlong", + "Furmark", + "Furnary", + "Furr", + "Furtek", + "Fusco", + "Gaal", + "Gabbert", + "Gabbey", + "Gabbi", + "Gabbie", + "Gabby", + "Gabe", + "Gabel", + "Gabey", + "Gabi", + "Gabie", + "Gable", + "Gabler", + "Gabor", + "Gabriel", + "Gabriela", + "Gabriele", + "Gabriell", + "Gabriella", + "Gabrielle", + "Gabrielli", + "Gabriellia", + "Gabriello", + "Gabrielson", + "Gabrila", + "Gaby", + "Gad", + "Gaddi", + "Gader", + "Gadmann", + "Gadmon", + "Gae", + "Gael", + "Gaelan", + "Gaeta", + "Gage", + "Gagliano", + "Gagne", + "Gagnon", + "Gahan", + "Gahl", + "Gaidano", + "Gaige", + "Gail", + "Gaile", + "Gaillard", + "Gainer", + "Gainor", + "Gaiser", + "Gaither", + "Gaivn", + "Gal", + "Gala", + "Galan", + "Galang", + "Galanti", + "Galasyn", + "Galatea", + "Galateah", + "Galatia", + "Gale", + "Galen", + "Galer", + "Galina", + "Galitea", + "Gall", + "Gallager", + "Gallagher", + "Gallard", + "Gallenz", + "Galliett", + "Galligan", + "Galloway", + "Gally", + "Galvan", + "Galven", + "Galvin", + "Gamages", + "Gamal", + "Gamali", + "Gamaliel", + "Gambell", + "Gamber", + "Gambrell", + "Gambrill", + "Gamin", + "Gan", + "Ganiats", + "Ganley", + "Gannes", + "Gannie", + "Gannon", + "Ganny", + "Gans", + "Gant", + "Gapin", + "Gar", + "Garald", + "Garate", + "Garaway", + "Garbe", + "Garber", + "Garbers", + "Garceau", + "Garcia", + "Garcon", + "Gard", + "Garda", + "Gardal", + "Gardas", + "Gardel", + "Gardell", + "Gardener", + "Gardia", + "Gardie", + "Gardiner", + "Gardner", + "Gardol", + "Gardy", + "Gare", + "Garek", + "Gareri", + "Gareth", + "Garett", + "Garey", + "Garfield", + "Garfinkel", + "Gargan", + "Garges", + "Garibald", + "Garibold", + "Garibull", + "Gariepy", + "Garik", + "Garin", + "Garlaand", + "Garlan", + "Garland", + "Garlanda", + "Garlen", + "Garlinda", + "Garling", + "Garmaise", + "Garneau", + "Garner", + "Garnes", + "Garnet", + "Garnett", + "Garnette", + "Garold", + "Garrard", + "Garratt", + "Garrek", + "Garret", + "Garreth", + "Garretson", + "Garrett", + "Garrick", + "Garrik", + "Garris", + "Garrison", + "Garrity", + "Garrot", + "Garrott", + "Garry", + "Garson", + "Garth", + "Garv", + "Garvey", + "Garvin", + "Garvy", + "Garwin", + "Garwood", + "Gary", + "Garzon", + "Gascony", + "Gaskill", + "Gaskin", + "Gaskins", + "Gaspar", + "Gaspard", + "Gasparo", + "Gasper", + "Gasperoni", + "Gass", + "Gasser", + "Gassman", + "Gastineau", + "Gaston", + "Gates", + "Gathard", + "Gathers", + "Gati", + "Gatian", + "Gatias", + "Gaudet", + "Gaudette", + "Gaughan", + "Gaul", + "Gauldin", + "Gaulin", + "Gault", + "Gaultiero", + "Gauntlett", + "Gausman", + "Gaut", + "Gautea", + "Gauthier", + "Gautier", + "Gautious", + "Gav", + "Gavan", + "Gaven", + "Gavette", + "Gavin", + "Gavini", + "Gavra", + "Gavrah", + "Gavriella", + "Gavrielle", + "Gavrila", + "Gavrilla", + "Gaw", + "Gawain", + "Gawen", + "Gawlas", + "Gay", + "Gaye", + "Gayel", + "Gayelord", + "Gayl", + "Gayla", + "Gayle", + "Gayleen", + "Gaylene", + "Gayler", + "Gaylor", + "Gaylord", + "Gayn", + "Gayner", + "Gaynor", + "Gazo", + "Gazzo", + "Geaghan", + "Gean", + "Geanine", + "Gearalt", + "Gearard", + "Gearhart", + "Gebelein", + "Gebhardt", + "Gebler", + "Geddes", + "Gee", + "Geehan", + "Geer", + "Geerts", + "Geesey", + "Gefell", + "Gefen", + "Geffner", + "Gehlbach", + "Gehman", + "Geibel", + "Geier", + "Geiger", + "Geilich", + "Geis", + "Geiss", + "Geithner", + "Gelasias", + "Gelasius", + "Gelb", + "Geldens", + "Gelhar", + "Geller", + "Gellman", + "Gelman", + "Gelya", + "Gemina", + "Gemini", + "Geminian", + "Geminius", + "Gemma", + "Gemmell", + "Gemoets", + "Gemperle", + "Gen", + "Gena", + "Genaro", + "Gene", + "Genesa", + "Genesia", + "Genet", + "Geneva", + "Genevieve", + "Genevra", + "Genia", + "Genie", + "Genisia", + "Genna", + "Gennaro", + "Genni", + "Gennie", + "Gennifer", + "Genny", + "Geno", + "Genovera", + "Gensler", + "Gensmer", + "Gent", + "Gentes", + "Gentilis", + "Gentille", + "Gentry", + "Genvieve", + "Geof", + "Geoff", + "Geoffrey", + "Geoffry", + "Georas", + "Geordie", + "Georg", + "George", + "Georgeanna", + "Georgeanne", + "Georgena", + "Georges", + "Georgeta", + "Georgetta", + "Georgette", + "Georgi", + "Georgia", + "Georgiana", + "Georgianna", + "Georgianne", + "Georgie", + "Georgina", + "Georgine", + "Georglana", + "Georgy", + "Ger", + "Geraint", + "Gerald", + "Geralda", + "Geraldina", + "Geraldine", + "Gerard", + "Gerardo", + "Geraud", + "Gerbold", + "Gerda", + "Gerdeen", + "Gerdi", + "Gerdy", + "Gere", + "Gerek", + "Gereld", + "Gereron", + "Gerfen", + "Gerge", + "Gerger", + "Gerhan", + "Gerhard", + "Gerhardine", + "Gerhardt", + "Geri", + "Gerianna", + "Gerianne", + "Gerick", + "Gerik", + "Gerita", + "Gerius", + "Gerkman", + "Gerlac", + "Gerladina", + "Germain", + "Germaine", + "German", + "Germana", + "Germann", + "Germano", + "Germaun", + "Germayne", + "Germin", + "Gernhard", + "Gerome", + "Gerrald", + "Gerrard", + "Gerri", + "Gerrie", + "Gerrilee", + "Gerrit", + "Gerry", + "Gersham", + "Gershom", + "Gershon", + "Gerson", + "Gerstein", + "Gerstner", + "Gert", + "Gerta", + "Gerti", + "Gertie", + "Gertrud", + "Gertruda", + "Gertrude", + "Gertrudis", + "Gerty", + "Gervais", + "Gervase", + "Gery", + "Gesner", + "Gessner", + "Getraer", + "Getter", + "Gettings", + "Gewirtz", + "Ghassan", + "Gherardi", + "Gherardo", + "Gherlein", + "Ghiselin", + "Giacamo", + "Giacinta", + "Giacobo", + "Giacomo", + "Giacopo", + "Giaimo", + "Giamo", + "Gian", + "Giana", + "Gianina", + "Gianna", + "Gianni", + "Giannini", + "Giarla", + "Giavani", + "Gib", + "Gibb", + "Gibbeon", + "Gibbie", + "Gibbon", + "Gibbons", + "Gibbs", + "Gibby", + "Gibe", + "Gibeon", + "Gibert", + "Gibrian", + "Gibson", + "Gibun", + "Giddings", + "Gide", + "Gideon", + "Giefer", + "Gies", + "Giesecke", + "Giess", + "Giesser", + "Giff", + "Giffard", + "Giffer", + "Gifferd", + "Giffie", + "Gifford", + "Giffy", + "Gigi", + "Giglio", + "Gignac", + "Giguere", + "Gil", + "Gilba", + "Gilbart", + "Gilbert", + "Gilberta", + "Gilberte", + "Gilbertina", + "Gilbertine", + "Gilberto", + "Gilbertson", + "Gilboa", + "Gilburt", + "Gilbye", + "Gilchrist", + "Gilcrest", + "Gilda", + "Gildas", + "Gildea", + "Gilder", + "Gildus", + "Gile", + "Gilead", + "Gilemette", + "Giles", + "Gilford", + "Gilges", + "Giliana", + "Giliane", + "Gill", + "Gillan", + "Gillead", + "Gilleod", + "Gilles", + "Gillespie", + "Gillett", + "Gilletta", + "Gillette", + "Gilli", + "Gilliam", + "Gillian", + "Gillie", + "Gilliette", + "Gilligan", + "Gillman", + "Gillmore", + "Gilly", + "Gilman", + "Gilmer", + "Gilmore", + "Gilmour", + "Gilpin", + "Gilroy", + "Gilson", + "Giltzow", + "Gilud", + "Gilus", + "Gimble", + "Gimpel", + "Gina", + "Ginder", + "Gine", + "Ginelle", + "Ginevra", + "Ginger", + "Gingras", + "Ginni", + "Ginnie", + "Ginnifer", + "Ginny", + "Gino", + "Ginsberg", + "Ginsburg", + "Gintz", + "Ginzburg", + "Gio", + "Giordano", + "Giorgi", + "Giorgia", + "Giorgio", + "Giovanna", + "Giovanni", + "Gipps", + "Gipson", + "Gipsy", + "Giralda", + "Giraldo", + "Girand", + "Girard", + "Girardi", + "Girardo", + "Giraud", + "Girhiny", + "Girish", + "Girovard", + "Girvin", + "Gisela", + "Giselbert", + "Gisele", + "Gisella", + "Giselle", + "Gish", + "Gisser", + "Gitel", + "Githens", + "Gitlow", + "Gitt", + "Gittel", + "Gittle", + "Giuditta", + "Giule", + "Giulia", + "Giuliana", + "Giulietta", + "Giulio", + "Giuseppe", + "Giustina", + "Giustino", + "Giusto", + "Given", + "Giverin", + "Giza", + "Gizela", + "Glaab", + "Glad", + "Gladdie", + "Gladdy", + "Gladi", + "Gladine", + "Gladis", + "Gladstone", + "Gladwin", + "Gladys", + "Glanti", + "Glantz", + "Glanville", + "Glarum", + "Glaser", + "Glasgo", + "Glass", + "Glassco", + "Glassman", + "Glaudia", + "Glavin", + "Gleason", + "Gleda", + "Gleeson", + "Gleich", + "Glen", + "Glenda", + "Glenden", + "Glendon", + "Glenine", + "Glenn", + "Glenna", + "Glennie", + "Glennis", + "Glennon", + "Glialentn", + "Glick", + "Glimp", + "Glinys", + "Glogau", + "Glori", + "Gloria", + "Gloriana", + "Gloriane", + "Glorianna", + "Glory", + "Glover", + "Glovsky", + "Gluck", + "Glyn", + "Glynas", + "Glynda", + "Glynias", + "Glynis", + "Glynn", + "Glynnis", + "Gmur", + "Gnni", + "Goar", + "Goat", + "Gobert", + "God", + "Goda", + "Godard", + "Godart", + "Godbeare", + "Godber", + "Goddard", + "Goddart", + "Godden", + "Godderd", + "Godding", + "Goddord", + "Godewyn", + "Godfree", + "Godfrey", + "Godfry", + "Godiva", + "Godliman", + "Godred", + "Godric", + "Godrich", + "Godspeed", + "Godwin", + "Goebel", + "Goeger", + "Goer", + "Goerke", + "Goeselt", + "Goetz", + "Goff", + "Goggin", + "Goines", + "Gokey", + "Golanka", + "Gold", + "Golda", + "Goldarina", + "Goldberg", + "Golden", + "Goldenberg", + "Goldfarb", + "Goldfinch", + "Goldi", + "Goldia", + "Goldie", + "Goldin", + "Goldina", + "Golding", + "Goldman", + "Goldner", + "Goldshell", + "Goldshlag", + "Goldsmith", + "Goldstein", + "Goldston", + "Goldsworthy", + "Goldwin", + "Goldy", + "Goles", + "Golightly", + "Gollin", + "Golliner", + "Golter", + "Goltz", + "Golub", + "Gomar", + "Gombach", + "Gombosi", + "Gomer", + "Gomez", + "Gona", + "Gonagle", + "Gone", + "Gonick", + "Gonnella", + "Gonroff", + "Gonsalve", + "Gonta", + "Gonyea", + "Gonzales", + "Gonzalez", + "Gonzalo", + "Goober", + "Good", + "Goodard", + "Goodden", + "Goode", + "Goodhen", + "Goodill", + "Goodkin", + "Goodman", + "Goodrich", + "Goodrow", + "Goodson", + "Goodspeed", + "Goodwin", + "Goody", + "Goodyear", + "Googins", + "Gora", + "Goran", + "Goraud", + "Gord", + "Gordan", + "Gorden", + "Gordie", + "Gordon", + "Gordy", + "Gore", + "Goren", + "Gorey", + "Gorga", + "Gorges", + "Gorlicki", + "Gorlin", + "Gorman", + "Gorrian", + "Gorrono", + "Gorski", + "Gorton", + "Gosnell", + "Gosney", + "Goss", + "Gosselin", + "Gosser", + "Gotcher", + "Goth", + "Gothar", + "Gothard", + "Gothart", + "Gothurd", + "Goto", + "Gottfried", + "Gotthard", + "Gotthelf", + "Gottlieb", + "Gottuard", + "Gottwald", + "Gough", + "Gould", + "Goulden", + "Goulder", + "Goulet", + "Goulette", + "Gove", + "Gow", + "Gower", + "Gowon", + "Gowrie", + "Graaf", + "Grace", + "Graces", + "Gracia", + "Gracie", + "Gracye", + "Gradeigh", + "Gradey", + "Grados", + "Grady", + "Grae", + "Graehl", + "Graehme", + "Graeme", + "Graf", + "Graff", + "Graham", + "Graig", + "Grail", + "Gram", + "Gran", + "Grand", + "Grane", + "Graner", + "Granese", + "Grange", + "Granger", + "Grani", + "Grania", + "Graniah", + "Graniela", + "Granlund", + "Grannia", + "Granniah", + "Grannias", + "Grannie", + "Granny", + "Granoff", + "Grant", + "Grantham", + "Granthem", + "Grantland", + "Grantley", + "Granville", + "Grassi", + "Grata", + "Grath", + "Grati", + "Gratia", + "Gratiana", + "Gratianna", + "Gratt", + "Graubert", + "Gravante", + "Graves", + "Gray", + "Graybill", + "Grayce", + "Grayson", + "Grazia", + "Greabe", + "Grearson", + "Gredel", + "Greeley", + "Green", + "Greenberg", + "Greenburg", + "Greene", + "Greenebaum", + "Greenes", + "Greenfield", + "Greenland", + "Greenleaf", + "Greenlee", + "Greenman", + "Greenquist", + "Greenstein", + "Greenwald", + "Greenwell", + "Greenwood", + "Greer", + "Greerson", + "Greeson", + "Grefe", + "Grefer", + "Greff", + "Greg", + "Grega", + "Gregg", + "Greggory", + "Greggs", + "Gregoire", + "Gregoor", + "Gregor", + "Gregorio", + "Gregorius", + "Gregory", + "Gregrory", + "Gregson", + "Greiner", + "Grekin", + "Grenier", + "Grenville", + "Gresham", + "Greta", + "Gretal", + "Gretchen", + "Grete", + "Gretel", + "Grethel", + "Gretna", + "Gretta", + "Grevera", + "Grew", + "Grewitz", + "Grey", + "Greyso", + "Greyson", + "Greysun", + "Grider", + "Gridley", + "Grier", + "Grieve", + "Griff", + "Griffie", + "Griffin", + "Griffis", + "Griffith", + "Griffiths", + "Griffy", + "Griggs", + "Grigson", + "Grim", + "Grimaldi", + "Grimaud", + "Grimbal", + "Grimbald", + "Grimbly", + "Grimes", + "Grimona", + "Grimonia", + "Grindlay", + "Grindle", + "Grinnell", + "Gris", + "Griselda", + "Griseldis", + "Grishilda", + "Grishilde", + "Grissel", + "Grissom", + "Gristede", + "Griswold", + "Griz", + "Grizel", + "Grizelda", + "Groark", + "Grobe", + "Grochow", + "Grodin", + "Grof", + "Grogan", + "Groh", + "Gromme", + "Grondin", + "Gronseth", + "Groome", + "Groos", + "Groot", + "Grory", + "Grosberg", + "Groscr", + "Grose", + "Grosmark", + "Gross", + "Grossman", + "Grosvenor", + "Grosz", + "Grote", + "Grounds", + "Grous", + "Grove", + "Groveman", + "Grover", + "Groves", + "Grubb", + "Grube", + "Gruber", + "Grubman", + "Gruchot", + "Grunberg", + "Grunenwald", + "Grussing", + "Gruver", + "Gschu", + "Guadalupe", + "Gualterio", + "Gualtiero", + "Guarino", + "Gudren", + "Gudrin", + "Gudrun", + "Guendolen", + "Guenevere", + "Guenna", + "Guenzi", + "Guerin", + "Guerra", + "Guevara", + "Guglielma", + "Guglielmo", + "Gui", + "Guibert", + "Guido", + "Guidotti", + "Guilbert", + "Guild", + "Guildroy", + "Guillaume", + "Guillema", + "Guillemette", + "Guillermo", + "Guimar", + "Guimond", + "Guinevere", + "Guinn", + "Guinna", + "Guise", + "Gujral", + "Gula", + "Gulgee", + "Gulick", + "Gun", + "Gunar", + "Gunas", + "Gundry", + "Gunilla", + "Gunn", + "Gunnar", + "Gunner", + "Gunning", + "Guntar", + "Gunter", + "Gunthar", + "Gunther", + "Gunzburg", + "Gupta", + "Gurango", + "Gurevich", + "Guria", + "Gurias", + "Gurl", + "Gurney", + "Gurolinick", + "Gurtner", + "Gus", + "Gusba", + "Gusella", + "Guss", + "Gussi", + "Gussie", + "Gussman", + "Gussy", + "Gusta", + "Gustaf", + "Gustafson", + "Gustafsson", + "Gustav", + "Gustave", + "Gustavo", + "Gustavus", + "Gusti", + "Gustie", + "Gustin", + "Gusty", + "Gut", + "Guthrey", + "Guthrie", + "Guthry", + "Gutow", + "Guttery", + "Guy", + "Guyer", + "Guyon", + "Guzel", + "Gwen", + "Gwendolen", + "Gwendolin", + "Gwendolyn", + "Gweneth", + "Gwenette", + "Gwenn", + "Gwenneth", + "Gwenni", + "Gwennie", + "Gwenny", + "Gwenora", + "Gwenore", + "Gwyn", + "Gwyneth", + "Gwynne", + "Gyasi", + "Gyatt", + "Gyimah", + "Gylys", + "Gypsie", + "Gypsy", + "Gytle", + "Ha", + "Haag", + "Haakon", + "Haas", + "Haase", + "Haberman", + "Hach", + "Hachman", + "Hachmann", + "Hachmin", + "Hackathorn", + "Hacker", + "Hackett", + "Hackney", + "Had", + "Haddad", + "Hadden", + "Haden", + "Hadik", + "Hadlee", + "Hadleigh", + "Hadley", + "Hadria", + "Hadrian", + "Hadsall", + "Hadwin", + "Hadwyn", + "Haeckel", + "Haerle", + "Haerr", + "Haff", + "Hafler", + "Hagai", + "Hagan", + "Hagar", + "Hagen", + "Hagerman", + "Haggai", + "Haggar", + "Haggerty", + "Haggi", + "Hagi", + "Hagood", + "Hahn", + "Hahnert", + "Hahnke", + "Haida", + "Haig", + "Haile", + "Hailee", + "Hailey", + "Haily", + "Haim", + "Haimes", + "Haines", + "Hak", + "Hakan", + "Hake", + "Hakeem", + "Hakim", + "Hako", + "Hakon", + "Hal", + "Haland", + "Halbeib", + "Halbert", + "Halda", + "Haldan", + "Haldane", + "Haldas", + "Haldeman", + "Halden", + "Haldes", + "Haldi", + "Haldis", + "Hale", + "Haleigh", + "Haletky", + "Haletta", + "Halette", + "Haley", + "Halfdan", + "Halfon", + "Halford", + "Hali", + "Halie", + "Halima", + "Halimeda", + "Hall", + "Halla", + "Hallagan", + "Hallam", + "Halland", + "Halle", + "Hallee", + "Hallerson", + "Hallett", + "Hallette", + "Halley", + "Halli", + "Halliday", + "Hallie", + "Hallock", + "Hallsy", + "Hallvard", + "Hally", + "Halona", + "Halonna", + "Halpern", + "Halsey", + "Halstead", + "Halsted", + "Halsy", + "Halvaard", + "Halverson", + "Ham", + "Hama", + "Hamachi", + "Hamal", + "Haman", + "Hamann", + "Hambley", + "Hamburger", + "Hamel", + "Hamer", + "Hamford", + "Hamforrd", + "Hamfurd", + "Hamid", + "Hamil", + "Hamilton", + "Hamish", + "Hamlani", + "Hamlen", + "Hamlet", + "Hamlin", + "Hammad", + "Hammel", + "Hammer", + "Hammerskjold", + "Hammock", + "Hammond", + "Hamner", + "Hamnet", + "Hamo", + "Hamon", + "Hampton", + "Hamrah", + "Hamrnand", + "Han", + "Hana", + "Hanae", + "Hanafee", + "Hanako", + "Hanan", + "Hance", + "Hancock", + "Handal", + "Handbook", + "Handel", + "Handler", + "Hands", + "Handy", + "Haney", + "Hanford", + "Hanforrd", + "Hanfurd", + "Hank", + "Hankins", + "Hanleigh", + "Hanley", + "Hanna", + "Hannah", + "Hannan", + "Hanni", + "Hannibal", + "Hannie", + "Hannis", + "Hannon", + "Hannover", + "Hannus", + "Hanny", + "Hanover", + "Hans", + "Hanschen", + "Hansel", + "Hanselka", + "Hansen", + "Hanser", + "Hanshaw", + "Hansiain", + "Hanson", + "Hanus", + "Hanway", + "Hanzelin", + "Happ", + "Happy", + "Hapte", + "Hara", + "Harald", + "Harbard", + "Harberd", + "Harbert", + "Harbird", + "Harbison", + "Harbot", + "Harbour", + "Harcourt", + "Hardan", + "Harday", + "Hardden", + "Hardej", + "Harden", + "Hardi", + "Hardie", + "Hardigg", + "Hardin", + "Harding", + "Hardman", + "Hardner", + "Hardunn", + "Hardwick", + "Hardy", + "Hare", + "Harelda", + "Harewood", + "Harhay", + "Harilda", + "Harim", + "Harl", + "Harlamert", + "Harlan", + "Harland", + "Harle", + "Harleigh", + "Harlen", + "Harlene", + "Harley", + "Harli", + "Harlie", + "Harlin", + "Harlow", + "Harman", + "Harmaning", + "Harmon", + "Harmonia", + "Harmonie", + "Harmony", + "Harms", + "Harned", + "Harneen", + "Harness", + "Harod", + "Harold", + "Harolda", + "Haroldson", + "Haroun", + "Harp", + "Harper", + "Harpole", + "Harpp", + "Harragan", + "Harrell", + "Harri", + "Harrie", + "Harriet", + "Harriett", + "Harrietta", + "Harriette", + "Harriman", + "Harrington", + "Harriot", + "Harriott", + "Harris", + "Harrison", + "Harrod", + "Harrow", + "Harrus", + "Harry", + "Harshman", + "Harsho", + "Hart", + "Harte", + "Hartfield", + "Hartill", + "Hartley", + "Hartman", + "Hartmann", + "Hartmunn", + "Hartnett", + "Harts", + "Hartwell", + "Harty", + "Hartzel", + "Hartzell", + "Hartzke", + "Harv", + "Harvard", + "Harve", + "Harvey", + "Harvie", + "Harvison", + "Harwell", + "Harwill", + "Harwilll", + "Harwin", + "Hasan", + "Hasen", + "Hasheem", + "Hashim", + "Hashimoto", + "Hashum", + "Hasin", + "Haskel", + "Haskell", + "Haskins", + "Haslam", + "Haslett", + "Hasseman", + "Hassett", + "Hassi", + "Hassin", + "Hastie", + "Hastings", + "Hasty", + "Haswell", + "Hatch", + "Hatcher", + "Hatfield", + "Hathaway", + "Hathcock", + "Hatti", + "Hattie", + "Hatty", + "Hau", + "Hauck", + "Hauge", + "Haugen", + "Hauger", + "Haughay", + "Haukom", + "Hauser", + "Hausmann", + "Hausner", + "Havard", + "Havelock", + "Haveman", + "Haven", + "Havener", + "Havens", + "Havstad", + "Hawger", + "Hawk", + "Hawken", + "Hawker", + "Hawkie", + "Hawkins", + "Hawley", + "Hawthorn", + "Hax", + "Hay", + "Haya", + "Hayashi", + "Hayden", + "Haydon", + "Haye", + "Hayes", + "Hayley", + "Hayman", + "Haymes", + "Haymo", + "Hayne", + "Haynes", + "Haynor", + "Hayott", + "Hays", + "Hayse", + "Hayton", + "Hayward", + "Haywood", + "Hayyim", + "Hazaki", + "Hazard", + "Haze", + "Hazeghi", + "Hazel", + "Hazelton", + "Hazem", + "Hazen", + "Hazlett", + "Hazlip", + "Head", + "Heady", + "Healey", + "Healion", + "Heall", + "Healy", + "Heaps", + "Hearn", + "Hearsh", + "Heater", + "Heath", + "Heathcote", + "Heather", + "Hebbe", + "Hebe", + "Hebel", + "Heber", + "Hebert", + "Hebner", + "Hebrew", + "Hecht", + "Heck", + "Hecker", + "Hecklau", + "Hector", + "Heda", + "Hedberg", + "Hedda", + "Heddi", + "Heddie", + "Heddy", + "Hedelman", + "Hedgcock", + "Hedges", + "Hedi", + "Hedley", + "Hedva", + "Hedvah", + "Hedve", + "Hedveh", + "Hedvig", + "Hedvige", + "Hedwig", + "Hedwiga", + "Hedy", + "Heeley", + "Heer", + "Heffron", + "Hefter", + "Hegarty", + "Hege", + "Heger", + "Hegyera", + "Hehre", + "Heid", + "Heida", + "Heidi", + "Heidie", + "Heidt", + "Heidy", + "Heigho", + "Heigl", + "Heilman", + "Heilner", + "Heim", + "Heimer", + "Heimlich", + "Hein", + "Heindrick", + "Heiner", + "Heiney", + "Heinrich", + "Heinrick", + "Heinrik", + "Heinrike", + "Heins", + "Heintz", + "Heise", + "Heisel", + "Heiskell", + "Heisser", + "Hekker", + "Hekking", + "Helaina", + "Helaine", + "Helali", + "Helban", + "Helbon", + "Helbona", + "Helbonia", + "Helbonna", + "Helbonnah", + "Helbonnas", + "Held", + "Helen", + "Helena", + "Helene", + "Helenka", + "Helfand", + "Helfant", + "Helga", + "Helge", + "Helgeson", + "Hellene", + "Heller", + "Helli", + "Hellman", + "Helm", + "Helman", + "Helmer", + "Helms", + "Helmut", + "Heloise", + "Helprin", + "Helsa", + "Helse", + "Helsell", + "Helsie", + "Helve", + "Helyn", + "Heman", + "Hembree", + "Hemingway", + "Hemminger", + "Hemphill", + "Hen", + "Hendel", + "Henden", + "Henderson", + "Hendon", + "Hendren", + "Hendrick", + "Hendricks", + "Hendrickson", + "Hendrik", + "Hendrika", + "Hendrix", + "Hendry", + "Henebry", + "Heng", + "Hengel", + "Henghold", + "Henig", + "Henigman", + "Henka", + "Henke", + "Henleigh", + "Henley", + "Henn", + "Hennahane", + "Hennebery", + "Hennessey", + "Hennessy", + "Henni", + "Hennie", + "Henning", + "Henri", + "Henricks", + "Henrie", + "Henrieta", + "Henrietta", + "Henriette", + "Henriha", + "Henrik", + "Henrion", + "Henrique", + "Henriques", + "Henry", + "Henryetta", + "Henryk", + "Henryson", + "Henson", + "Hentrich", + "Hephzibah", + "Hephzipa", + "Hephzipah", + "Heppman", + "Hepsiba", + "Hepsibah", + "Hepza", + "Hepzi", + "Hera", + "Herald", + "Herb", + "Herbert", + "Herbie", + "Herbst", + "Herby", + "Herc", + "Hercule", + "Hercules", + "Herculie", + "Hereld", + "Heriberto", + "Heringer", + "Herm", + "Herman", + "Hermann", + "Hermes", + "Hermia", + "Hermie", + "Hermina", + "Hermine", + "Herminia", + "Hermione", + "Hermon", + "Hermosa", + "Hermy", + "Hernandez", + "Hernando", + "Hernardo", + "Herod", + "Herodias", + "Herold", + "Heron", + "Herr", + "Herra", + "Herrah", + "Herrera", + "Herrick", + "Herries", + "Herring", + "Herrington", + "Herriott", + "Herrle", + "Herrmann", + "Herrod", + "Hersch", + "Herschel", + "Hersh", + "Hershel", + "Hershell", + "Herson", + "Herstein", + "Herta", + "Hertberg", + "Hertha", + "Hertz", + "Hertzfeld", + "Hertzog", + "Herv", + "Herve", + "Hervey", + "Herwick", + "Herwig", + "Herwin", + "Herzberg", + "Herzel", + "Herzen", + "Herzig", + "Herzog", + "Hescock", + "Heshum", + "Hesketh", + "Hesky", + "Hesler", + "Hesper", + "Hess", + "Hessler", + "Hessney", + "Hesta", + "Hester", + "Hesther", + "Hestia", + "Heti", + "Hett", + "Hetti", + "Hettie", + "Hetty", + "Heurlin", + "Heuser", + "Hew", + "Hewart", + "Hewe", + "Hewes", + "Hewet", + "Hewett", + "Hewie", + "Hewitt", + "Hey", + "Heyde", + "Heydon", + "Heyer", + "Heyes", + "Heyman", + "Heymann", + "Heyward", + "Heywood", + "Hezekiah", + "Hi", + "Hibben", + "Hibbert", + "Hibbitts", + "Hibbs", + "Hickey", + "Hickie", + "Hicks", + "Hidie", + "Hieronymus", + "Hiett", + "Higbee", + "Higginbotham", + "Higgins", + "Higginson", + "Higgs", + "High", + "Highams", + "Hightower", + "Higinbotham", + "Higley", + "Hijoung", + "Hike", + "Hilaire", + "Hilar", + "Hilaria", + "Hilario", + "Hilarius", + "Hilary", + "Hilbert", + "Hild", + "Hilda", + "Hildagard", + "Hildagarde", + "Hilde", + "Hildebrandt", + "Hildegaard", + "Hildegard", + "Hildegarde", + "Hildick", + "Hildie", + "Hildy", + "Hilel", + "Hill", + "Hillard", + "Hillari", + "Hillary", + "Hilleary", + "Hillegass", + "Hillel", + "Hillell", + "Hiller", + "Hillery", + "Hillhouse", + "Hilliard", + "Hilliary", + "Hillie", + "Hillier", + "Hillinck", + "Hillman", + "Hills", + "Hilly", + "Hillyer", + "Hiltan", + "Hilten", + "Hiltner", + "Hilton", + "Him", + "Hime", + "Himelman", + "Hinch", + "Hinckley", + "Hinda", + "Hindorff", + "Hindu", + "Hines", + "Hinkel", + "Hinkle", + "Hinman", + "Hinson", + "Hintze", + "Hinze", + "Hippel", + "Hirai", + "Hiram", + "Hirasuna", + "Hiro", + "Hiroko", + "Hiroshi", + "Hirsch", + "Hirschfeld", + "Hirsh", + "Hirst", + "Hirz", + "Hirza", + "Hisbe", + "Hitchcock", + "Hite", + "Hitoshi", + "Hitt", + "Hittel", + "Hizar", + "Hjerpe", + "Hluchy", + "Ho", + "Hoag", + "Hoagland", + "Hoang", + "Hoashis", + "Hoban", + "Hobard", + "Hobart", + "Hobbie", + "Hobbs", + "Hobey", + "Hobie", + "Hochman", + "Hock", + "Hocker", + "Hodess", + "Hodge", + "Hodges", + "Hodgkinson", + "Hodgson", + "Hodosh", + "Hoebart", + "Hoeg", + "Hoehne", + "Hoem", + "Hoenack", + "Hoes", + "Hoeve", + "Hoffarth", + "Hoffer", + "Hoffert", + "Hoffman", + "Hoffmann", + "Hofmann", + "Hofstetter", + "Hogan", + "Hogarth", + "Hogen", + "Hogg", + "Hogle", + "Hogue", + "Hoi", + "Hoisch", + "Hokanson", + "Hola", + "Holbrook", + "Holbrooke", + "Holcman", + "Holcomb", + "Holden", + "Holder", + "Holds", + "Hole", + "Holey", + "Holladay", + "Hollah", + "Holland", + "Hollander", + "Holle", + "Hollenbeck", + "Holleran", + "Hollerman", + "Holli", + "Hollie", + "Hollinger", + "Hollingsworth", + "Hollington", + "Hollis", + "Hollister", + "Holloway", + "Holly", + "Holly-Anne", + "Hollyanne", + "Holman", + "Holmann", + "Holmen", + "Holmes", + "Holms", + "Holmun", + "Holna", + "Holofernes", + "Holsworth", + "Holt", + "Holton", + "Holtorf", + "Holtz", + "Holub", + "Holzman", + "Homans", + "Home", + "Homer", + "Homere", + "Homerus", + "Homovec", + "Honan", + "Honebein", + "Honey", + "Honeyman", + "Honeywell", + "Hong", + "Honig", + "Honna", + "Honniball", + "Honor", + "Honora", + "Honoria", + "Honorine", + "Hoo", + "Hooge", + "Hook", + "Hooke", + "Hooker", + "Hoon", + "Hoopen", + "Hooper", + "Hoopes", + "Hootman", + "Hoover", + "Hope", + "Hopfinger", + "Hopkins", + "Hoppe", + "Hopper", + "Horace", + "Horacio", + "Horan", + "Horatia", + "Horatio", + "Horatius", + "Horbal", + "Horgan", + "Horick", + "Horlacher", + "Horn", + "Horne", + "Horner", + "Hornstein", + "Horodko", + "Horowitz", + "Horsey", + "Horst", + "Hort", + "Horten", + "Hortensa", + "Hortense", + "Hortensia", + "Horter", + "Horton", + "Horvitz", + "Horwath", + "Horwitz", + "Hosbein", + "Hose", + "Hosea", + "Hoseia", + "Hosfmann", + "Hoshi", + "Hoskinson", + "Hospers", + "Hotchkiss", + "Hotze", + "Hough", + "Houghton", + "Houlberg", + "Hound", + "Hourigan", + "Hourihan", + "Housen", + "Houser", + "Houston", + "Housum", + "Hovey", + "How", + "Howard", + "Howarth", + "Howe", + "Howell", + "Howenstein", + "Howes", + "Howey", + "Howie", + "Howlan", + "Howland", + "Howlend", + "Howlond", + "Howlyn", + "Howund", + "Howzell", + "Hoxie", + "Hoxsie", + "Hoy", + "Hoye", + "Hoyt", + "Hrutkay", + "Hsu", + "Hu", + "Huai", + "Huan", + "Huang", + "Huba", + "Hubbard", + "Hubble", + "Hube", + "Huber", + "Huberman", + "Hubert", + "Huberto", + "Huberty", + "Hubey", + "Hubie", + "Hubing", + "Hubsher", + "Huckaby", + "Huda", + "Hudgens", + "Hudis", + "Hudnut", + "Hudson", + "Huebner", + "Huei", + "Huesman", + "Hueston", + "Huey", + "Huff", + "Hufnagel", + "Huggins", + "Hugh", + "Hughes", + "Hughett", + "Hughie", + "Hughmanick", + "Hugibert", + "Hugo", + "Hugon", + "Hugues", + "Hui", + "Hujsak", + "Hukill", + "Hulbard", + "Hulbert", + "Hulbig", + "Hulburt", + "Hulda", + "Huldah", + "Hulen", + "Hull", + "Hullda", + "Hultgren", + "Hultin", + "Hulton", + "Hum", + "Humbert", + "Humberto", + "Humble", + "Hume", + "Humfrey", + "Humfrid", + "Humfried", + "Hummel", + "Humo", + "Hump", + "Humpage", + "Humph", + "Humphrey", + "Hun", + "Hunfredo", + "Hung", + "Hungarian", + "Hunger", + "Hunley", + "Hunsinger", + "Hunt", + "Hunter", + "Huntingdon", + "Huntington", + "Huntlee", + "Huntley", + "Huoh", + "Huppert", + "Hurd", + "Hurff", + "Hurlbut", + "Hurlee", + "Hurleigh", + "Hurless", + "Hurley", + "Hurlow", + "Hurst", + "Hurty", + "Hurwit", + "Hurwitz", + "Husain", + "Husch", + "Husein", + "Husha", + "Huskamp", + "Huskey", + "Hussar", + "Hussein", + "Hussey", + "Huston", + "Hut", + "Hutchings", + "Hutchins", + "Hutchinson", + "Hutchison", + "Hutner", + "Hutson", + "Hutt", + "Huttan", + "Hutton", + "Hux", + "Huxham", + "Huxley", + "Hwang", + "Hwu", + "Hy", + "Hyacinth", + "Hyacintha", + "Hyacinthe", + "Hyacinthia", + "Hyacinthie", + "Hyams", + "Hyatt", + "Hyde", + "Hylan", + "Hyland", + "Hylton", + "Hyman", + "Hymen", + "Hymie", + "Hynda", + "Hynes", + "Hyo", + "Hyozo", + "Hyps", + "Hyrup", + "Iago", + "Iain", + "Iams", + "Ian", + "Iand", + "Ianteen", + "Ianthe", + "Iaria", + "Iaverne", + "Ib", + "Ibbetson", + "Ibbie", + "Ibbison", + "Ibby", + "Ibrahim", + "Ibson", + "Ichabod", + "Icken", + "Id", + "Ida", + "Idalia", + "Idalina", + "Idaline", + "Idalla", + "Idden", + "Iddo", + "Ide", + "Idel", + "Idelia", + "Idell", + "Idelle", + "Idelson", + "Iden", + "Idette", + "Idleman", + "Idola", + "Idolah", + "Idolla", + "Idona", + "Idonah", + "Idonna", + "Idou", + "Idoux", + "Idzik", + "Iene", + "Ier", + "Ierna", + "Ieso", + "Ietta", + "Iey", + "Ifill", + "Igal", + "Igenia", + "Iggie", + "Iggy", + "Iglesias", + "Ignace", + "Ignacia", + "Ignacio", + "Ignacius", + "Ignatia", + "Ignatius", + "Ignatz", + "Ignatzia", + "Ignaz", + "Ignazio", + "Igor", + "Ihab", + "Iiette", + "Iila", + "Iinde", + "Iinden", + "Iives", + "Ike", + "Ikeda", + "Ikey", + "Ikkela", + "Ilaire", + "Ilan", + "Ilana", + "Ilario", + "Ilarrold", + "Ilbert", + "Ileana", + "Ileane", + "Ilene", + "Iline", + "Ilise", + "Ilka", + "Ilke", + "Illa", + "Illene", + "Illona", + "Illyes", + "Ilona", + "Ilonka", + "Ilowell", + "Ilsa", + "Ilse", + "Ilwain", + "Ilysa", + "Ilyse", + "Ilyssa", + "Im", + "Ima", + "Imalda", + "Iman", + "Imelda", + "Imelida", + "Imena", + "Immanuel", + "Imogen", + "Imogene", + "Imojean", + "Imray", + "Imre", + "Imtiaz", + "Ina", + "Incrocci", + "Indihar", + "Indira", + "Inerney", + "Ines", + "Inesita", + "Ineslta", + "Inessa", + "Inez", + "Infeld", + "Infield", + "Ing", + "Inga", + "Ingaberg", + "Ingaborg", + "Ingalls", + "Ingamar", + "Ingar", + "Inge", + "Ingeberg", + "Ingeborg", + "Ingelbert", + "Ingemar", + "Inger", + "Ingham", + "Inglebert", + "Ingles", + "Inglis", + "Ingmar", + "Ingold", + "Ingra", + "Ingraham", + "Ingram", + "Ingrid", + "Ingrim", + "Ingunna", + "Ingvar", + "Inigo", + "Inkster", + "Inman", + "Inna", + "Innes", + "Inness", + "Innis", + "Inoue", + "Intisar", + "Intosh", + "Intyre", + "Inverson", + "Iny", + "Ioab", + "Iolande", + "Iolanthe", + "Iolenta", + "Ion", + "Iona", + "Iong", + "Iorgo", + "Iorgos", + "Iorio", + "Iormina", + "Iosep", + "Ioved", + "Iover", + "Ioves", + "Iow", + "Ioyal", + "Iphagenia", + "Iphigenia", + "Iphigeniah", + "Iphlgenia", + "Ira", + "Iran", + "Irby", + "Iredale", + "Ireland", + "Irena", + "Irene", + "Irfan", + "Iridis", + "Iridissa", + "Irina", + "Iris", + "Irisa", + "Irish", + "Irita", + "Irma", + "Irme", + "Irmgard", + "Irmina", + "Irmine", + "Irra", + "Irv", + "Irvin", + "Irvine", + "Irving", + "Irwin", + "Irwinn", + "Isa", + "Isaac", + "Isaacs", + "Isaacson", + "Isaak", + "Isabea", + "Isabeau", + "Isabel", + "Isabelita", + "Isabella", + "Isabelle", + "Isac", + "Isacco", + "Isador", + "Isadora", + "Isadore", + "Isahella", + "Isaiah", + "Isak", + "Isbel", + "Isbella", + "Isborne", + "Iseabal", + "Isherwood", + "Ishii", + "Ishmael", + "Ishmul", + "Isia", + "Isiah", + "Isiahi", + "Isidor", + "Isidora", + "Isidore", + "Isidoro", + "Isidro", + "Isis", + "Isla", + "Islaen", + "Island", + "Isle", + "Islean", + "Isleana", + "Isleen", + "Islek", + "Isma", + "Isman", + "Isobel", + "Isola", + "Isolda", + "Isolde", + "Isolt", + "Israel", + "Israeli", + "Issi", + "Issiah", + "Issie", + "Issy", + "Ita", + "Itagaki", + "Itch", + "Ithaman", + "Ithnan", + "Itin", + "Iva", + "Ivah", + "Ivan", + "Ivana", + "Ivanah", + "Ivanna", + "Ivar", + "Ivatts", + "Ive", + "Ivens", + "Iver", + "Ivers", + "Iverson", + "Ives", + "Iveson", + "Ivett", + "Ivette", + "Ivetts", + "Ivey", + "Ivie", + "Ivo", + "Ivon", + "Ivonne", + "Ivor", + "Ivory", + "Ivy", + "Iy", + "Iyre", + "Iz", + "Izaak", + "Izabel", + "Izak", + "Izawa", + "Izy", + "Izzy", + "Ja", + "Jaal", + "Jaala", + "Jaan", + "Jaban", + "Jabe", + "Jabez", + "Jabin", + "Jablon", + "Jabon", + "Jac", + "Jacenta", + "Jacey", + "Jacie", + "Jacinda", + "Jacinta", + "Jacintha", + "Jacinthe", + "Jacinto", + "Jack", + "Jackelyn", + "Jacki", + "Jackie", + "Jacklin", + "Jacklyn", + "Jackquelin", + "Jackqueline", + "Jackson", + "Jacky", + "Jaclin", + "Jaclyn", + "Jaco", + "Jacob", + "Jacoba", + "Jacobah", + "Jacobba", + "Jacobina", + "Jacobine", + "Jacobo", + "Jacobs", + "Jacobsen", + "Jacobsohn", + "Jacobson", + "Jacoby", + "Jacquelin", + "Jacqueline", + "Jacquelyn", + "Jacquelynn", + "Jacquenetta", + "Jacquenette", + "Jacques", + "Jacquet", + "Jacquetta", + "Jacquette", + "Jacqui", + "Jacquie", + "Jacy", + "Jacynth", + "Jada", + "Jadd", + "Jadda", + "Jaddan", + "Jaddo", + "Jade", + "Jadwiga", + "Jae", + "Jaeger", + "Jaehne", + "Jael", + "Jaela", + "Jaella", + "Jaenicke", + "Jaf", + "Jaffe", + "Jagir", + "Jago", + "Jahdai", + "Jahdal", + "Jahdiel", + "Jahdol", + "Jahn", + "Jahncke", + "Jaime", + "Jaime ", + "Jaimie", + "Jain", + "Jaine", + "Jair", + "Jairia", + "Jake", + "Jakie", + "Jakob", + "Jakoba", + "Jala", + "Jalbert", + "Jallier", + "Jamaal", + "Jamal", + "Jamel", + "James", + "Jameson", + "Jamesy", + "Jamey", + "Jami", + "Jamie", + "Jamieson", + "Jamil", + "Jamila", + "Jamill", + "Jamilla", + "Jamille", + "Jamima", + "Jamin", + "Jamison", + "Jammal", + "Jammie", + "Jammin", + "Jamnes", + "Jamnis", + "Jan", + "Jana", + "Janaya", + "Janaye", + "Jandel", + "Jandy", + "Jane", + "Janean", + "Janeczka", + "Janeen", + "Janek", + "Janel", + "Janela", + "Janella", + "Janelle", + "Janene", + "Janenna", + "Janerich", + "Janessa", + "Janet", + "Janeta", + "Janetta", + "Janette", + "Janeva", + "Janey", + "Jangro", + "Jania", + "Janice", + "Janicki", + "Janie", + "Janifer", + "Janik", + "Janina", + "Janine", + "Janis", + "Janith", + "Janiuszck", + "Janka", + "Jankell", + "Jankey", + "Jann", + "Janna", + "Jannel", + "Jannelle", + "Jannery", + "Janos", + "Janot", + "Jansen", + "Jansson", + "Januarius", + "January", + "Januisz", + "Janus", + "Jany", + "Janyte", + "Japeth", + "Japha", + "Japheth", + "Jaqitsch", + "Jaquelin", + "Jaquelyn", + "Jaquenetta", + "Jaquenette", + "Jaquiss", + "Jaquith", + "Jara", + "Jarad", + "Jard", + "Jardena", + "Jareb", + "Jared", + "Jarek", + "Jaret", + "Jari", + "Jariah", + "Jarib", + "Jarid", + "Jarietta", + "Jarita", + "Jarl", + "Jarlath", + "Jarlathus", + "Jarlen", + "Jarnagin", + "Jarrad", + "Jarred", + "Jarrell", + "Jarret", + "Jarrett", + "Jarrid", + "Jarrod", + "Jarrow", + "Jarv", + "Jarvey", + "Jarvis", + "Jary", + "Jase", + "Jasen", + "Jasik", + "Jasisa", + "Jasmin", + "Jasmina", + "Jasmine", + "Jason", + "Jasper", + "Jasun", + "Jauch", + "Jaunita", + "Javed", + "Javier", + "Javler", + "Jaworski", + "Jay", + "Jaycee", + "Jaye", + "Jaylene", + "Jayme", + "Jaymee", + "Jaymie", + "Jayne", + "Jaynell", + "Jaynes", + "Jayson", + "Jazmin", + "Jdavie", + "Jea", + "Jean", + "Jean-Claude", + "Jeana", + "Jeane", + "Jeanelle", + "Jeanette", + "Jeanie", + "Jeanine", + "Jeanna", + "Jeanne", + "Jeannette", + "Jeannie", + "Jeannine", + "Jeavons", + "Jeaz", + "Jeb", + "Jecho", + "Jecoa", + "Jecon", + "Jeconiah", + "Jed", + "Jedd", + "Jeddy", + "Jedediah", + "Jedidiah", + "Jedlicka", + "Jedthus", + "Jeff", + "Jeffcott", + "Jefferey", + "Jeffers", + "Jefferson", + "Jeffery", + "Jeffie", + "Jeffrey", + "Jeffries", + "Jeffry", + "Jeffy", + "Jegar", + "Jeggar", + "Jegger", + "Jehanna", + "Jehiah", + "Jehial", + "Jehias", + "Jehiel", + "Jehius", + "Jehoash", + "Jehovah", + "Jehu", + "Jelena", + "Jelene", + "Jelks", + "Jelle", + "Jelsma", + "Jem", + "Jemena", + "Jemie", + "Jemima", + "Jemimah", + "Jemina", + "Jeminah", + "Jemine", + "Jemma", + "Jemmie", + "Jemmy", + "Jempty", + "Jemy", + "Jen", + "Jena", + "Jenda", + "Jenei", + "Jenelle", + "Jenesia", + "Jenette", + "Jeni", + "Jenica", + "Jeniece", + "Jenifer", + "Jeniffer", + "Jenilee", + "Jenine", + "Jenkel", + "Jenkins", + "Jenks", + "Jenn", + "Jenna", + "Jenne", + "Jennee", + "Jenness", + "Jennette", + "Jenni", + "Jennica", + "Jennie", + "Jennifer", + "Jennilee", + "Jennine", + "Jennings", + "Jenny", + "Jeno", + "Jens", + "Jensen", + "Jentoft", + "Jephthah", + "Jephum", + "Jepson", + "Jepum", + "Jer", + "Jerad", + "Jerald", + "Jeraldine", + "Jeralee", + "Jeramey", + "Jeramie", + "Jere", + "Jereld", + "Jereme", + "Jeremiah", + "Jeremias", + "Jeremie", + "Jeremy", + "Jeri", + "Jeritah", + "Jermain", + "Jermaine", + "Jerman", + "Jermayne", + "Jermyn", + "Jerol", + "Jerold", + "Jeroma", + "Jerome", + "Jeromy", + "Jerri", + "Jerrie", + "Jerrilee", + "Jerrilyn", + "Jerrine", + "Jerrol", + "Jerrold", + "Jerroll", + "Jerrome", + "Jerry", + "Jerrylee", + "Jerusalem", + "Jervis", + "Jerz", + "Jesh", + "Jesher", + "Jess", + "Jessa", + "Jessabell", + "Jessalin", + "Jessalyn", + "Jessamine", + "Jessamyn", + "Jesse", + "Jessee", + "Jesselyn", + "Jessen", + "Jessey", + "Jessi", + "Jessica", + "Jessie", + "Jessika", + "Jessy", + "Jestude", + "Jesus", + "Jeth", + "Jethro", + "Jeu", + "Jeunesse", + "Jeuz", + "Jevon", + "Jew", + "Jewel", + "Jewell", + "Jewelle", + "Jewett", + "Jews", + "Jez", + "Jezabel", + "Jezabella", + "Jezabelle", + "Jezebel", + "Jezreel", + "Ji", + "Jill", + "Jillana", + "Jillane", + "Jillayne", + "Jilleen", + "Jillene", + "Jilli", + "Jillian", + "Jillie", + "Jilly", + "Jim", + "Jimmie", + "Jimmy", + "Jinny", + "Jit", + "Jo", + "Jo Ann", + "Jo-Ann", + "Jo-Anne", + "JoAnn", + "JoAnne", + "Joab", + "Joachim", + "Joachima", + "Joacima", + "Joacimah", + "Joan", + "Joana", + "Joane", + "Joanie", + "Joann", + "Joanna", + "Joanne", + "Joannes", + "Joao", + "Joappa", + "Joaquin", + "Joash", + "Joashus", + "Job", + "Jobe", + "Jobey", + "Jobi", + "Jobie", + "Jobina", + "Joby", + "Jobye", + "Jobyna", + "Jocelin", + "Joceline", + "Jocelyn", + "Jocelyne", + "Jochbed", + "Jochebed", + "Jock", + "Jocko", + "Jodee", + "Jodi", + "Jodie", + "Jodoin", + "Jody", + "Joe", + "Joeann", + "Joed", + "Joel", + "Joela", + "Joelie", + "Joell", + "Joella", + "Joelle", + "Joellen", + "Joelly", + "Joellyn", + "Joelynn", + "Joerg", + "Joete", + "Joette", + "Joey", + "Joh", + "Johan", + "Johanan", + "Johann", + "Johanna", + "Johannah", + "Johannes", + "Johannessen", + "Johansen", + "Johathan", + "Johen", + "Johiah", + "Johm", + "John", + "Johna", + "Johnath", + "Johnathan", + "Johnathon", + "Johnette", + "Johnna", + "Johnnie", + "Johnny", + "Johns", + "Johnson", + "Johnsson", + "Johnsten", + "Johnston", + "Johnstone", + "Johny", + "Johppa", + "Johppah", + "Johst", + "Joice", + "Joiner", + "Jojo", + "Joktan", + "Jola", + "Jolanta", + "Jolda", + "Jolee", + "Joleen", + "Jolene", + "Jolenta", + "Joletta", + "Joli", + "Jolie", + "Joliet", + "Joline", + "Jollanta", + "Jollenta", + "Joly", + "Jolyn", + "Jolynn", + "Jon", + "Jona", + "Jonah", + "Jonas", + "Jonathan", + "Jonathon", + "Jonati", + "Jone", + "Jonell", + "Jones", + "Jonette", + "Joni", + "Jonie", + "Jonina", + "Jonis", + "Jonme", + "Jonna", + "Jonny", + "Joo", + "Joon", + "Joost", + "Jopa", + "Jordain", + "Jordan", + "Jordana", + "Jordanna", + "Jordans", + "Jordanson", + "Jordison", + "Jordon", + "Jorey", + "Jorgan", + "Jorge", + "Jorgensen", + "Jorgenson", + "Jori", + "Jorie", + "Jorin", + "Joris", + "Jorrie", + "Jorry", + "Jory", + "Jos", + "Joscelin", + "Jose", + "Josee", + "Josefa", + "Josefina", + "Joseito", + "Joselow", + "Joselyn", + "Joseph", + "Josepha", + "Josephina", + "Josephine", + "Josephson", + "Joses", + "Josey", + "Josh", + "Joshi", + "Joshia", + "Joshua", + "Joshuah", + "Josi", + "Josiah", + "Josias", + "Josie", + "Josler", + "Joslyn", + "Josselyn", + "Josy", + "Jotham", + "Joub", + "Joung", + "Jourdain", + "Jourdan", + "Jovi", + "Jovia", + "Jovita", + "Jovitah", + "Jovitta", + "Jowett", + "Joy", + "Joya", + "Joyan", + "Joyann", + "Joyce", + "Joycelin", + "Joye", + "Jozef", + "Jsandye", + "Juan", + "Juana", + "Juanita", + "Juanne", + "Juback", + "Jud", + "Judah", + "Judas", + "Judd", + "Jude", + "Judenberg", + "Judi", + "Judie", + "Judith", + "Juditha", + "Judon", + "Judsen", + "Judson", + "Judus", + "Judy", + "Judye", + "Jueta", + "Juetta", + "Juieta", + "Jule", + "Julee", + "Jules", + "Juley", + "Juli", + "Julia", + "Julian", + "Juliana", + "Juliane", + "Juliann", + "Julianna", + "Julianne", + "Juliano", + "Julide", + "Julie", + "Julienne", + "Juliet", + "Julieta", + "Julietta", + "Juliette", + "Julina", + "Juline", + "Julio", + "Julis", + "Julissa", + "Julita", + "Julius", + "Jumbala", + "Jump", + "Jun", + "Juna", + "June", + "Junette", + "Jung", + "Juni", + "Junia", + "Junie", + "Junieta", + "Junina", + "Junius", + "Junji", + "Junko", + "Junna", + "Junno", + "Juno", + "Jurdi", + "Jurgen", + "Jurkoic", + "Just", + "Justen", + "Juster", + "Justicz", + "Justin", + "Justina", + "Justine", + "Justinian", + "Justinn", + "Justino", + "Justis", + "Justus", + "Juta", + "Jutta", + "Juxon", + "Jyoti", + "Kablesh", + "Kacerek", + "Kacey", + "Kachine", + "Kacie", + "Kacy", + "Kaczer", + "Kaden", + "Kadner", + "Kado", + "Kaela", + "Kaenel", + "Kaete", + "Kafka", + "Kahaleel", + "Kahl", + "Kahle", + "Kahler", + "Kahlil", + "Kahn", + "Kai", + "Kaia", + "Kaila", + "Kaile", + "Kailey", + "Kain", + "Kaine", + "Kaiser", + "Kaitlin", + "Kaitlyn", + "Kaitlynn", + "Kaiulani", + "Kaja", + "Kajdan", + "Kakalina", + "Kal", + "Kala", + "Kalagher", + "Kalasky", + "Kalb", + "Kalbli", + "Kale", + "Kaleb", + "Kaleena", + "Kalfas", + "Kali", + "Kalie", + "Kalikow", + "Kalil", + "Kalila", + "Kalin", + "Kalina", + "Kalinda", + "Kalindi", + "Kaliope", + "Kaliski", + "Kalk", + "Kall", + "Kalle", + "Kalli", + "Kallick", + "Kallista", + "Kallman", + "Kally", + "Kalman", + "Kalmick", + "Kaltman", + "Kalvin", + "Kalvn", + "Kam", + "Kama", + "Kamal", + "Kamaria", + "Kamat", + "Kameko", + "Kamerman", + "Kamila", + "Kamilah", + "Kamillah", + "Kamin", + "Kammerer", + "Kamp", + "Kampmann", + "Kampmeier", + "Kan", + "Kanal", + "Kancler", + "Kandace", + "Kandy", + "Kane", + "Kania", + "Kannan", + "Kannry", + "Kano", + "Kant", + "Kanter", + "Kantor", + "Kantos", + "Kanya", + "Kape", + "Kaplan", + "Kapoor", + "Kapor", + "Kappel", + "Kappenne", + "Kara", + "Kara-Lynn", + "Karalee", + "Karalynn", + "Karame", + "Karas", + "Karb", + "Kare", + "Karee", + "Kareem", + "Karel", + "Karen", + "Karena", + "Kari", + "Karia", + "Karie", + "Karil", + "Karilla", + "Karilynn", + "Karim", + "Karin", + "Karina", + "Karine", + "Kariotta", + "Karisa", + "Karissa", + "Karita", + "Karl", + "Karla", + "Karlan", + "Karlee", + "Karleen", + "Karlen", + "Karlene", + "Karlens", + "Karli", + "Karlie", + "Karlik", + "Karlin", + "Karlis", + "Karlise", + "Karlotta", + "Karlotte", + "Karlow", + "Karly", + "Karlyn", + "Karmen", + "Karna", + "Karney", + "Karol", + "Karola", + "Karole", + "Karolina", + "Karoline", + "Karoly", + "Karolyn", + "Karon", + "Karp", + "Karr", + "Karrah", + "Karrie", + "Karry", + "Karsten", + "Kartis", + "Karwan", + "Kary", + "Karyl", + "Karylin", + "Karyn", + "Kasevich", + "Kasey", + "Kashden", + "Kask", + "Kaslik", + "Kaspar", + "Kasper", + "Kass", + "Kassab", + "Kassandra", + "Kassaraba", + "Kassel", + "Kassey", + "Kassi", + "Kassia", + "Kassie", + "Kassity", + "Kast", + "Kat", + "Kata", + "Katalin", + "Kataway", + "Kate", + "Katee", + "Katerina", + "Katerine", + "Katey", + "Kath", + "Katha", + "Katharina", + "Katharine", + "Katharyn", + "Kathe", + "Katherin", + "Katherina", + "Katherine", + "Katheryn", + "Kathi", + "Kathie", + "Kathleen", + "Kathlene", + "Kathlin", + "Kathrine", + "Kathryn", + "Kathryne", + "Kathy", + "Kathye", + "Kati", + "Katie", + "Katina", + "Katine", + "Katinka", + "Katlaps", + "Katleen", + "Katlin", + "Kato", + "Katonah", + "Katrina", + "Katrine", + "Katrinka", + "Katsuyama", + "Katt", + "Katti", + "Kattie", + "Katuscha", + "Katusha", + "Katushka", + "Katy", + "Katya", + "Katz", + "Katzen", + "Katzir", + "Katzman", + "Kauffman", + "Kauffmann", + "Kaufman", + "Kaufmann", + "Kaule", + "Kauppi", + "Kauslick", + "Kavanagh", + "Kavanaugh", + "Kavita", + "Kawai", + "Kawasaki", + "Kay", + "Kaya", + "Kaycee", + "Kaye", + "Kayla", + "Kayle", + "Kaylee", + "Kayley", + "Kaylil", + "Kaylyn", + "Kayne", + "Kaz", + "Kazim", + "Kazimir", + "Kazmirci", + "Kazue", + "Kealey", + "Kean", + "Keane", + "Keare", + "Kearney", + "Keary", + "Keating", + "Keavy", + "Kee", + "Keefe", + "Keefer", + "Keegan", + "Keel", + "Keelby", + "Keele", + "Keeler", + "Keeley", + "Keelia", + "Keelin", + "Keely", + "Keen", + "Keenan", + "Keene", + "Keener", + "Keese", + "Keeton", + "Keever", + "Keffer", + "Keg", + "Kegan", + "Keheley", + "Kehoe", + "Kehr", + "Kei", + "Keifer", + "Keiko", + "Keil", + "Keily", + "Keir", + "Keisling", + "Keith", + "Keithley", + "Kela", + "Kelbee", + "Kelby", + "Kelcey", + "Kelci", + "Kelcie", + "Kelcy", + "Kelda", + "Keldah", + "Keldon", + "Kele", + "Keli", + "Keligot", + "Kelila", + "Kella", + "Kellby", + "Kellda", + "Kelleher", + "Kellen", + "Kellene", + "Keller", + "Kelley", + "Kelli", + "Kellia", + "Kellie", + "Kellina", + "Kellsie", + "Kelly", + "Kellyann", + "Kellyn", + "Kelsey", + "Kelsi", + "Kelson", + "Kelsy", + "Kelton", + "Kelula", + "Kelvin", + "Kelwen", + "Kelwin", + "Kelwunn", + "Kemble", + "Kemeny", + "Kemme", + "Kemp", + "Kempe", + "Kemppe", + "Ken", + "Kenay", + "Kenaz", + "Kendal", + "Kendall", + "Kendell", + "Kendra", + "Kendrah", + "Kendre", + "Kendrick", + "Kendricks", + "Kendry", + "Kendy", + "Kendyl", + "Kenelm", + "Kenison", + "Kenji", + "Kenlay", + "Kenlee", + "Kenleigh", + "Kenley", + "Kenn", + "Kenna", + "Kennan", + "Kennard", + "Kennedy", + "Kennet", + "Kenneth", + "Kennett", + "Kenney", + "Kennie", + "Kennith", + "Kenny", + "Kenon", + "Kenric", + "Kenrick", + "Kensell", + "Kent", + "Kenta", + "Kenti", + "Kentiga", + "Kentigera", + "Kentigerma", + "Kentiggerma", + "Kenton", + "Kenward", + "Kenway", + "Kenwee", + "Kenweigh", + "Kenwood", + "Kenwrick", + "Kenyon", + "Kenzi", + "Kenzie", + "Keon", + "Kepner", + "Keppel", + "Ker", + "Kerby", + "Kerek", + "Kerekes", + "Kerge", + "Keri", + "Keriann", + "Kerianne", + "Kerin", + "Kerk", + "Kerman", + "Kermie", + "Kermit", + "Kermy", + "Kern", + "Kernan", + "Kerns", + "Kerr", + "Kerri", + "Kerrie", + "Kerril", + "Kerrill", + "Kerrin", + "Kerrison", + "Kerry", + "Kersten", + "Kerstin", + "Kerwin", + "Kerwinn", + "Kerwon", + "Kery", + "Kesia", + "Kesley", + "Keslie", + "Kessel", + "Kessia", + "Kessiah", + "Kessler", + "Kester", + "Ketchan", + "Ketchum", + "Ketti", + "Kettie", + "Ketty", + "Keung", + "Kev", + "Kevan", + "Keven", + "Keverian", + "Keverne", + "Kevin", + "Kevina", + "Kevon", + "Kevyn", + "Key", + "Keyek", + "Keyes", + "Keynes", + "Keyser", + "Keyte", + "Kezer", + "Khai", + "Khajeh", + "Khalid", + "Khalil", + "Khalin", + "Khalsa", + "Khan", + "Khanna", + "Khano", + "Khichabia", + "Kho", + "Khorma", + "Khosrow", + "Khoury", + "Khudari", + "Ki", + "Kiah", + "Kial", + "Kidd", + "Kidder", + "Kiefer", + "Kieffer", + "Kieger", + "Kiehl", + "Kiel", + "Kiele", + "Kielty", + "Kienan", + "Kier", + "Kieran", + "Kiernan", + "Kiersten", + "Kikelia", + "Kiker", + "Kiki", + "Kila", + "Kilah", + "Kilan", + "Kilar", + "Kilbride", + "Kilby", + "Kile", + "Kiley", + "Kilgore", + "Kilian", + "Kilk", + "Killam", + "Killarney", + "Killen", + "Killian", + "Killie", + "Killigrew", + "Killion", + "Killoran", + "Killy", + "Kilmarx", + "Kilroy", + "Kim", + "Kimball", + "Kimbell", + "Kimber", + "Kimberlee", + "Kimberley", + "Kimberli", + "Kimberly", + "Kimberlyn", + "Kimble", + "Kimbra", + "Kimitri", + "Kimmel", + "Kimmi", + "Kimmie", + "Kimmy", + "Kimon", + "Kimura", + "Kin", + "Kinata", + "Kincaid", + "Kinch", + "Kinchen", + "Kind", + "Kindig", + "Kinelski", + "King", + "Kingdon", + "Kinghorn", + "Kingsbury", + "Kingsley", + "Kingsly", + "Kingston", + "Kinna", + "Kinnard", + "Kinney", + "Kinnie", + "Kinnon", + "Kinny", + "Kinsler", + "Kinsley", + "Kinsman", + "Kinson", + "Kinzer", + "Kiona", + "Kip", + "Kipp", + "Kippar", + "Kipper", + "Kippie", + "Kippy", + "Kipton", + "Kira", + "Kiran", + "Kirbee", + "Kirbie", + "Kirby", + "Kirch", + "Kirchner", + "Kiri", + "Kirima", + "Kirimia", + "Kirit", + "Kirk", + "Kirkpatrick", + "Kirkwood", + "Kironde", + "Kirsch", + "Kirschner", + "Kirshbaum", + "Kirst", + "Kirsten", + "Kirsteni", + "Kirsti", + "Kirstin", + "Kirstyn", + "Kirt", + "Kirtley", + "Kirven", + "Kirwin", + "Kisor", + "Kissee", + "Kissel", + "Kissiah", + "Kissie", + "Kissner", + "Kistner", + "Kisung", + "Kit", + "Kitchen", + "Kitti", + "Kittie", + "Kitty", + "Kiyohara", + "Kiyoshi", + "Kizzee", + "Kizzie", + "Kjersti", + "Klapp", + "Klara", + "Klarika", + "Klarrisa", + "Klatt", + "Klaus", + "Klayman", + "Klecka", + "Kleeman", + "Klehm", + "Kleiman", + "Klein", + "Kleinstein", + "Klemens", + "Klement", + "Klemm", + "Klemperer", + "Klenk", + "Kleon", + "Klepac", + "Kleper", + "Kletter", + "Kliber", + "Kliman", + "Kliment", + "Klimesh", + "Klina", + "Kline", + "Kling", + "Klingel", + "Klinger", + "Klinges", + "Klockau", + "Kloman", + "Klos", + "Kloster", + "Klotz", + "Klug", + "Kluge", + "Klump", + "Klusek", + "Klute", + "Knapp", + "Kneeland", + "Knepper", + "Knick", + "Knight", + "Knighton", + "Knipe", + "Knitter", + "Knobloch", + "Knoll", + "Knorring", + "Knowland", + "Knowle", + "Knowles", + "Knowling", + "Knowlton", + "Knox", + "Knudson", + "Knut", + "Knute", + "Knuth", + "Knutson", + "Ko", + "Koa", + "Koah", + "Koal", + "Koball", + "Kobe", + "Kobi", + "Koblas", + "Koblick", + "Koby", + "Kobylak", + "Koch", + "Koehler", + "Koenig", + "Koeninger", + "Koenraad", + "Koeppel", + "Koerlin", + "Koerner", + "Koetke", + "Koffler", + "Koffman", + "Koh", + "Kohl", + "Kohler", + "Kohn", + "Kokaras", + "Kokoruda", + "Kolb", + "Kolivas", + "Kolk", + "Koller", + "Kolnick", + "Kolnos", + "Kolodgie", + "Kolosick", + "Koloski", + "Kolva", + "Komara", + "Komarek", + "Komsa", + "Kondon", + "Kone", + "Kong", + "Konikow", + "Kono", + "Konopka", + "Konrad", + "Konstance", + "Konstantin", + "Konstantine", + "Konstanze", + "Konyn", + "Koo", + "Kooima", + "Koosis", + "Kopans", + "Kopaz", + "Kopp", + "Koppel", + "Kopple", + "Kora", + "Koral", + "Koralie", + "Koralle", + "Koran", + "Kordula", + "Kore", + "Korella", + "Koren", + "Korenblat", + "Koressa", + "Korey", + "Korff", + "Korfonta", + "Kori", + "Korie", + "Korman", + "Korney", + "Kornher", + "Korns", + "Korrie", + "Korry", + "Kort", + "Korten", + "Korwin", + "Korwun", + "Kory", + "Kosak", + "Kosaka", + "Kosel", + "Koser", + "Kosey", + "Kosiur", + "Koslo", + "Koss", + "Kosse", + "Kostival", + "Kostman", + "Kotick", + "Kotta", + "Kotto", + "Kotz", + "Kovacev", + "Kovacs", + "Koval", + "Kovar", + "Kowal", + "Kowalski", + "Kowatch", + "Kowtko", + "Koy", + "Koziara", + "Koziarz", + "Koziel", + "Kozloski", + "Kraft", + "Kragh", + "Krahling", + "Krahmer", + "Krakow", + "Krall", + "Kramer", + "Kramlich", + "Krantz", + "Kraska", + "Krasner", + "Krasnoff", + "Kraul", + "Kraus", + "Krause", + "Krauss", + "Kravits", + "Krawczyk", + "Kreager", + "Krebs", + "Kreda", + "Kreegar", + "Krefetz", + "Kreg", + "Kreiker", + "Krein", + "Kreindler", + "Kreiner", + "Kreis", + "Kreit", + "Kreitman", + "Krell", + "Kremer", + "Krenek", + "Krenn", + "Kresic", + "Kress", + "Krever", + "Kries", + "Krigsman", + "Krilov", + "Kris", + "Krischer", + "Krisha", + "Krishna", + "Krishnah", + "Krispin", + "Kriss", + "Krissie", + "Krissy", + "Krista", + "Kristal", + "Kristan", + "Kriste", + "Kristel", + "Kristen", + "Kristi", + "Kristian", + "Kristianson", + "Kristie", + "Kristien", + "Kristin", + "Kristina", + "Kristine", + "Kristo", + "Kristof", + "Kristofer", + "Kristoffer", + "Kristofor", + "Kristoforo", + "Kristopher", + "Kristos", + "Kristy", + "Kristyn", + "Krock", + "Kroll", + "Kronfeld", + "Krongold", + "Kronick", + "Kroo", + "Krucik", + "Krueger", + "Krug", + "Kruger", + "Krum", + "Krusche", + "Kruse", + "Krute", + "Kruter", + "Krutz", + "Krys", + "Kryska", + "Krysta", + "Krystal", + "Krystalle", + "Krystin", + "Krystle", + "Krystyna", + "Ku", + "Kubetz", + "Kubiak", + "Kubis", + "Kucik", + "Kudva", + "Kuebbing", + "Kuehn", + "Kuehnel", + "Kuhlman", + "Kuhn", + "Kulda", + "Kulseth", + "Kulsrud", + "Kumagai", + "Kumar", + "Kumler", + "Kung", + "Kunin", + "Kunkle", + "Kunz", + "Kuo", + "Kurland", + "Kurman", + "Kurr", + "Kursh", + "Kurt", + "Kurth", + "Kurtis", + "Kurtz", + "Kurtzig", + "Kurtzman", + "Kurys", + "Kurzawa", + "Kus", + "Kushner", + "Kusin", + "Kuska", + "Kussell", + "Kuster", + "Kutchins", + "Kuth", + "Kutzenco", + "Kutzer", + "Kwabena", + "Kwan", + "Kwang", + "Kwapong", + "Kwarteng", + "Kwasi", + "Kwei", + "Kwok", + "Kwon", + "Ky", + "Kyd", + "Kyl", + "Kyla", + "Kylah", + "Kylander", + "Kyle", + "Kylen", + "Kylie", + "Kylila", + "Kylstra", + "Kylynn", + "Kym", + "Kynan", + "Kyne", + "Kynthia", + "Kyriako", + "Kyrstin", + "Kyte", + "La", + "La Verne", + "LaBaw", + "LaMee", + "LaMonica", + "LaMori", + "LaRue", + "LaSorella", + "Laaspere", + "Laban", + "Labana", + "Laband", + "Labanna", + "Labannah", + "Labors", + "Lacagnia", + "Lacee", + "Lacefield", + "Lacey", + "Lach", + "Lachance", + "Lachish", + "Lachlan", + "Lachman", + "Lachus", + "Lacie", + "Lacombe", + "Lacy", + "Lad", + "Ladd", + "Laddie", + "Laddy", + "Laden", + "Ladew", + "Ladonna", + "Lady", + "Lael", + "Laetitia", + "Laflam", + "Lafleur", + "Laforge", + "Lagas", + "Lagasse", + "Lahey", + "Lai", + "Laidlaw", + "Lail", + "Laina", + "Laine", + "Lainey", + "Laing", + "Laird", + "Lais", + "Laise", + "Lait", + "Laith", + "Laius", + "Lakin", + "Laks", + "Laktasic", + "Lal", + "Lala", + "Lalage", + "Lali", + "Lalise", + "Lalita", + "Lalitta", + "Lalittah", + "Lalla", + "Lallage", + "Lally", + "Lalo", + "Lam", + "Lamar", + "Lamarre", + "Lamb", + "Lambard", + "Lambart", + "Lambert", + "Lamberto", + "Lambertson", + "Lambrecht", + "Lamdin", + "Lammond", + "Lamond", + "Lamont", + "Lamoree", + "Lamoureux", + "Lamp", + "Lampert", + "Lamphere", + "Lamprey", + "Lamrert", + "Lamrouex", + "Lamson", + "Lan", + "Lana", + "Lanae", + "Lanam", + "Lananna", + "Lancaster", + "Lance", + "Lancelle", + "Lancelot", + "Lancey", + "Lanctot", + "Land", + "Landa", + "Landahl", + "Landan", + "Landau", + "Landbert", + "Landel", + "Lander", + "Landers", + "Landes", + "Landing", + "Landis", + "Landmeier", + "Landon", + "Landre", + "Landri", + "Landrum", + "Landry", + "Landsman", + "Landy", + "Lane", + "Lanette", + "Laney", + "Lanford", + "Lanfri", + "Lang", + "Langan", + "Langbehn", + "Langdon", + "Lange", + "Langelo", + "Langer", + "Langham", + "Langill", + "Langille", + "Langley", + "Langsdon", + "Langston", + "Lani", + "Lanie", + "Lanita", + "Lankton", + "Lanna", + "Lanni", + "Lannie", + "Lanny", + "Lansing", + "Lanta", + "Lantha", + "Lanti", + "Lantz", + "Lanza", + "Lapham", + "Lapides", + "Lapointe", + "Lapotin", + "Lara", + "Laraine", + "Larcher", + "Lardner", + "Lareena", + "Lareine", + "Larena", + "Larentia", + "Laresa", + "Largent", + "Lari", + "Larianna", + "Larimer", + "Larimor", + "Larimore", + "Larina", + "Larine", + "Laris", + "Larisa", + "Larissa", + "Lark", + "Larkin", + "Larkins", + "Larner", + "Larochelle", + "Laroy", + "Larrabee", + "Larrie", + "Larrisa", + "Larry", + "Lars", + "Larsen", + "Larson", + "Laryssa", + "Lasala", + "Lash", + "Lashar", + "Lashoh", + "Lashond", + "Lashonda", + "Lashonde", + "Lashondra", + "Lasko", + "Lasky", + "Lasley", + "Lasonde", + "Laspisa", + "Lasser", + "Lassiter", + "Laszlo", + "Lat", + "Latashia", + "Latea", + "Latham", + "Lathan", + "Lathe", + "Lathrop", + "Lathrope", + "Lati", + "Latia", + "Latif", + "Latimer", + "Latimore", + "Latin", + "Latini", + "Latisha", + "Latona", + "Latonia", + "Latoniah", + "Latouche", + "Latoya", + "Latoye", + "Latoyia", + "Latreece", + "Latreese", + "Latrell", + "Latrena", + "Latreshia", + "Latrice", + "Latricia", + "Latrina", + "Latt", + "Latta", + "Latterll", + "Lattie", + "Lattimer", + "Latton", + "Lattonia", + "Latty", + "Latvina", + "Lau", + "Lauber", + "Laubin", + "Laud", + "Lauder", + "Lauer", + "Laufer", + "Laughlin", + "Laughry", + "Laughton", + "Launce", + "Launcelot", + "Laundes", + "Laura", + "Lauraine", + "Laural", + "Lauralee", + "Laurance", + "Laure", + "Lauree", + "Laureen", + "Laurel", + "Laurella", + "Lauren", + "Laurena", + "Laurence", + "Laurene", + "Laurens", + "Laurent", + "Laurentia", + "Laurentium", + "Lauretta", + "Laurette", + "Lauri", + "Laurianne", + "Laurice", + "Laurie", + "Laurin", + "Laurinda", + "Laurita", + "Lauritz", + "Lauro", + "Lauryn", + "Lauter", + "Laux", + "Lauzon", + "Laval", + "Laveen", + "Lavella", + "Lavelle", + "Laven", + "Lavena", + "Lavern", + "Laverna", + "Laverne", + "Lavery", + "Lavina", + "Lavine", + "Lavinia", + "Lavinie", + "Lavoie", + "Lavona", + "Law", + "Lawford", + "Lawler", + "Lawley", + "Lawlor", + "Lawrence", + "Lawrenson", + "Lawry", + "Laws", + "Lawson", + "Lawton", + "Lawtun", + "Lay", + "Layla", + "Layman", + "Layne", + "Layney", + "Layton", + "Lazar", + "Lazare", + "Lazaro", + "Lazaruk", + "Lazarus", + "Lazes", + "Lazor", + "Lazos", + "Le", + "LeCroy", + "LeDoux", + "LeMay", + "LeRoy", + "LeVitus", + "Lea", + "Leach", + "Leacock", + "Leah", + "Leahey", + "Leake", + "Leal", + "Lean", + "Leanard", + "Leander", + "Leandra", + "Leandre", + "Leandro", + "Leann", + "Leanna", + "Leanne", + "Leanor", + "Leanora", + "Leaper", + "Lear", + "Leary", + "Leasia", + "Leatri", + "Leatrice", + "Leavelle", + "Leavitt", + "Leavy", + "Leban", + "Lebar", + "Lebaron", + "Lebbie", + "Leblanc", + "Lebna", + "Leboff", + "Lechner", + "Lecia", + "Leckie", + "Leclair", + "Lectra", + "Leda", + "Ledah", + "Ledda", + "Leddy", + "Ledeen", + "Lederer", + "Lee", + "LeeAnn", + "Leeann", + "Leeanne", + "Leede", + "Leeke", + "Leela", + "Leelah", + "Leeland", + "Leena", + "Leesa", + "Leese", + "Leesen", + "Leeth", + "Leff", + "Leffen", + "Leffert", + "Lefkowitz", + "Lefton", + "Leftwich", + "Lefty", + "Leggat", + "Legge", + "Leggett", + "Legra", + "Lehet", + "Lehman", + "Lehmann", + "Lehrer", + "Leia", + "Leibman", + "Leicester", + "Leid", + "Leif", + "Leifer", + "Leifeste", + "Leigh", + "Leigha", + "Leighland", + "Leighton", + "Leila", + "Leilah", + "Leilani", + "Leipzig", + "Leis", + "Leiser", + "Leisha", + "Leitao", + "Leith", + "Leitman", + "Lejeune", + "Lek", + "Lela", + "Lelah", + "Leland", + "Leler", + "Lelia", + "Lelith", + "Lello", + "Lem", + "Lema", + "Lemaceon", + "Lemal", + "Lemar", + "Lemcke", + "Lemieux", + "Lemire", + "Lemkul", + "Lemmie", + "Lemmuela", + "Lemmueu", + "Lemmy", + "Lemon", + "Lempres", + "Lemuel", + "Lemuela", + "Lemuelah", + "Len", + "Lena", + "Lenard", + "Lenci", + "Lenee", + "Lenes", + "Lenette", + "Lengel", + "Lenhard", + "Lenhart", + "Lenka", + "Lenna", + "Lennard", + "Lenni", + "Lennie", + "Lenno", + "Lennon", + "Lennox", + "Lenny", + "Leno", + "Lenora", + "Lenore", + "Lenox", + "Lenrow", + "Lenssen", + "Lentha", + "Lenwood", + "Lenz", + "Lenzi", + "Leo", + "Leod", + "Leodora", + "Leoine", + "Leola", + "Leoline", + "Leon", + "Leona", + "Leonanie", + "Leonard", + "Leonardi", + "Leonardo", + "Leone", + "Leonelle", + "Leonerd", + "Leong", + "Leonhard", + "Leoni", + "Leonid", + "Leonidas", + "Leonie", + "Leonor", + "Leonora", + "Leonore", + "Leonsis", + "Leonteen", + "Leontina", + "Leontine", + "Leontyne", + "Leopold", + "Leopoldeen", + "Leopoldine", + "Leor", + "Leora", + "Leotie", + "Lepine", + "Lepley", + "Lepp", + "Lepper", + "Lerner", + "Leroi", + "Leroy", + "Les", + "Lesak", + "Leschen", + "Lesh", + "Leshia", + "Lesko", + "Leslee", + "Lesley", + "Lesli", + "Leslie", + "Lesly", + "Lessard", + "Lesser", + "Lesslie", + "Lester", + "Lesya", + "Let", + "Leta", + "Letch", + "Letha", + "Lethia", + "Leticia", + "Letisha", + "Letitia", + "Letizia", + "Letreece", + "Letrice", + "Letsou", + "Letta", + "Lette", + "Letti", + "Lettie", + "Letty", + "Leund", + "Leupold", + "Lev", + "Levan", + "Levana", + "Levania", + "Levenson", + "Leventhal", + "Leventis", + "Leverett", + "Leverick", + "Leveridge", + "Leveroni", + "Levesque", + "Levey", + "Levi", + "Levin", + "Levina", + "Levine", + "Levins", + "Levinson", + "Levison", + "Levitan", + "Levitt", + "Levon", + "Levona", + "Levy", + "Lew", + "Lewak", + "Lewan", + "Lewanna", + "Lewellen", + "Lewendal", + "Lewert", + "Lewes", + "Lewie", + "Lewin", + "Lewis", + "Lewison", + "Lewiss", + "Lewls", + "Lewse", + "Lexi", + "Lexie", + "Lexine", + "Lexis", + "Lexy", + "Ley", + "Leyes", + "Leyla", + "Lezley", + "Lezlie", + "Lhary", + "Li", + "Lia", + "Liam", + "Lian", + "Liana", + "Liane", + "Lianna", + "Lianne", + "Lias", + "Liatrice", + "Liatris", + "Lib", + "Liba", + "Libb", + "Libbey", + "Libbi", + "Libbie", + "Libbna", + "Libby", + "Libenson", + "Liberati", + "Libna", + "Libnah", + "Liborio", + "Libove", + "Libre", + "Licastro", + "Licha", + "Licht", + "Lichtenfeld", + "Lichter", + "Licko", + "Lida", + "Lidah", + "Lidda", + "Liddie", + "Liddle", + "Liddy", + "Lidia", + "Lidstone", + "Lieberman", + "Liebermann", + "Liebman", + "Liebowitz", + "Liederman", + "Lief", + "Lienhard", + "Liesa", + "Lietman", + "Liew", + "Lifton", + "Ligetti", + "Liggett", + "Liggitt", + "Light", + "Lightfoot", + "Lightman", + "Lil", + "Lila", + "Lilac", + "Lilah", + "Lilas", + "Lili", + "Lilia", + "Lilian", + "Liliane", + "Lilias", + "Lilith", + "Lilithe", + "Lilla", + "Lilli", + "Lillian", + "Lillie", + "Lillis", + "Lillith", + "Lilllie", + "Lilly", + "Lillywhite", + "Lily", + "Lilyan", + "Lilybel", + "Lilybelle", + "Lim", + "Liman", + "Limann", + "Limber", + "Limbert", + "Limemann", + "Limoli", + "Lin", + "Lina", + "Linc", + "Lincoln", + "Lind", + "Linda", + "Lindahl", + "Lindberg", + "Lindblad", + "Lindbom", + "Lindeberg", + "Lindell", + "Lindemann", + "Linden", + "Linder", + "Linders", + "Lindgren", + "Lindholm", + "Lindi", + "Lindie", + "Lindley", + "Lindly", + "Lindner", + "Lindo", + "Lindon", + "Lindsay", + "Lindsey", + "Lindsley", + "Lindsy", + "Lindy", + "Line", + "Linea", + "Linehan", + "Linell", + "Linet", + "Linetta", + "Linette", + "Ling", + "Lingwood", + "Linis", + "Link", + "Linker", + "Linkoski", + "Linn", + "Linnea", + "Linnell", + "Linneman", + "Linnet", + "Linnette", + "Linnie", + "Linoel", + "Linsk", + "Linskey", + "Linson", + "Linus", + "Linzer", + "Linzy", + "Lion", + "Lionel", + "Lionello", + "Lipcombe", + "Lipfert", + "Lipinski", + "Lipkin", + "Lipman", + "Liponis", + "Lipp", + "Lippold", + "Lipps", + "Lipscomb", + "Lipsey", + "Lipski", + "Lipson", + "Lira", + "Liris", + "Lisa", + "Lisabet", + "Lisabeth", + "Lisan", + "Lisandra", + "Lisbeth", + "Liscomb", + "Lise", + "Lisetta", + "Lisette", + "Lisha", + "Lishe", + "Lisk", + "Lisle", + "Liss", + "Lissa", + "Lissak", + "Lissi", + "Lissie", + "Lissner", + "Lissy", + "Lister", + "Lita", + "Litch", + "Litha", + "Lithea", + "Litman", + "Litt", + "Litta", + "Littell", + "Little", + "Littlejohn", + "Littman", + "Litton", + "Liu", + "Liuka", + "Liv", + "Liva", + "Livesay", + "Livi", + "Livia", + "Livingston", + "Livingstone", + "Livvi", + "Livvie", + "Livvy", + "Livvyy", + "Livy", + "Liz", + "Liza", + "Lizabeth", + "Lizbeth", + "Lizette", + "Lizzie", + "Lizzy", + "Ljoka", + "Llewellyn", + "Llovera", + "Lloyd", + "Llywellyn", + "Loar", + "Loats", + "Lobel", + "Lobell", + "Lochner", + "Lock", + "Locke", + "Lockhart", + "Locklin", + "Lockwood", + "Lodge", + "Lodhia", + "Lodi", + "Lodie", + "Lodmilla", + "Lodovico", + "Lody", + "Loeb", + "Loella", + "Loesceke", + "Loferski", + "Loftis", + "Loftus", + "Logan", + "Loggia", + "Loggins", + "Loginov", + "Lohman", + "Lohner", + "Lohrman", + "Lohse", + "Lois", + "Loise", + "Lola", + "Lolande", + "Lolanthe", + "Lole", + "Loleta", + "Lolita", + "Lolly", + "Loma", + "Lomasi", + "Lomax", + "Lombard", + "Lombardi", + "Lombardo", + "Lombardy", + "Lon", + "Lona", + "London", + "Londoner", + "Lonee", + "Lonergan", + "Long", + "Longan", + "Longawa", + "Longerich", + "Longfellow", + "Longley", + "Longmire", + "Longo", + "Longtin", + "Longwood", + "Loni", + "Lonier", + "Lonna", + "Lonnard", + "Lonne", + "Lonni", + "Lonnie", + "Lonny", + "Lontson", + "Loomis", + "Loos", + "Lopes", + "Lopez", + "Lora", + "Lorain", + "Loraine", + "Loralee", + "Loralie", + "Loralyn", + "Loram", + "Lorant", + "Lord", + "Lordan", + "Loredana", + "Loredo", + "Loree", + "Loreen", + "Lorelei", + "Lorelie", + "Lorelle", + "Loren", + "Lorena", + "Lorene", + "Lorens", + "Lorenz", + "Lorenza", + "Lorenzana", + "Lorenzo", + "Loresz", + "Loretta", + "Lorette", + "Lori", + "Loria", + "Lorianna", + "Lorianne", + "Lorie", + "Lorien", + "Lorilee", + "Lorilyn", + "Lorimer", + "Lorin", + "Lorinda", + "Lorine", + "Loriner", + "Loring", + "Loris", + "Lorita", + "Lorn", + "Lorna", + "Lorne", + "Lorola", + "Lorolla", + "Lorollas", + "Lorou", + "Lorraine", + "Lorrayne", + "Lorri", + "Lorrie", + "Lorrimer", + "Lorrimor", + "Lorrin", + "Lorry", + "Lorsung", + "Lorusso", + "Lory", + "Lose", + "Loseff", + "Loss", + "Lossa", + "Losse", + "Lot", + "Lothair", + "Lothaire", + "Lothar", + "Lothario", + "Lotson", + "Lotta", + "Lotte", + "Lotti", + "Lottie", + "Lotty", + "Lotus", + "Lotz", + "Lou", + "Louanna", + "Louanne", + "Louella", + "Lough", + "Lougheed", + "Loughlin", + "Louie", + "Louis", + "Louisa", + "Louise", + "Louisette", + "Louls", + "Lounge", + "Lourdes", + "Lourie", + "Louth", + "Loutitia", + "Loux", + "Lovash", + "Lovato", + "Love", + "Lovel", + "Lovell", + "Loveridge", + "Lovering", + "Lovett", + "Lovich", + "Lovmilla", + "Low", + "Lowe", + "Lowell", + "Lowenstein", + "Lowenstern", + "Lower", + "Lowery", + "Lowis", + "Lowndes", + "Lowney", + "Lowrance", + "Lowrie", + "Lowry", + "Lowson", + "Loy", + "Loyce", + "Loydie", + "Lozano", + "Lozar", + "Lu", + "Luana", + "Luane", + "Luann", + "Luanne", + "Luanni", + "Luba", + "Lubba", + "Lubbi", + "Lubbock", + "Lubeck", + "Luben", + "Lubet", + "Lubin", + "Lubow", + "Luby", + "Luca", + "Lucais", + "Lucania", + "Lucas", + "Lucchesi", + "Luce", + "Lucey", + "Lucho", + "Luci", + "Lucia", + "Lucian", + "Luciana", + "Luciano", + "Lucias", + "Lucic", + "Lucie", + "Lucien", + "Lucienne", + "Lucier", + "Lucila", + "Lucilia", + "Lucilla", + "Lucille", + "Lucina", + "Lucinda", + "Lucine", + "Lucio", + "Lucita", + "Lucius", + "Luckett", + "Luckin", + "Lucky", + "Lucrece", + "Lucretia", + "Lucy", + "Lud", + "Ludeman", + "Ludewig", + "Ludie", + "Ludlew", + "Ludlow", + "Ludly", + "Ludmilla", + "Ludovick", + "Ludovico", + "Ludovika", + "Ludvig", + "Ludwig", + "Ludwigg", + "Ludwog", + "Luebke", + "Luedtke", + "Luehrmann", + "Luella", + "Luelle", + "Lugar", + "Lugo", + "Luhe", + "Luhey", + "Luht", + "Luigi", + "Luigino", + "Luing", + "Luis", + "Luisa", + "Luise", + "Luiza", + "Lukas", + "Lukash", + "Lukasz", + "Luke", + "Lukey", + "Lukin", + "Lula", + "Lulita", + "Lull", + "Lulu", + "Lumbard", + "Lumbye", + "Lumpkin", + "Luna", + "Lund", + "Lundberg", + "Lundeen", + "Lundell", + "Lundgren", + "Lundin", + "Lundquist", + "Lundt", + "Lune", + "Lunetta", + "Lunette", + "Lunn", + "Lunna", + "Lunneta", + "Lunnete", + "Lunseth", + "Lunsford", + "Lunt", + "Luo", + "Lupe", + "Lupee", + "Lupien", + "Lupita", + "Lura", + "Lurette", + "Lurie", + "Lurleen", + "Lurlene", + "Lurline", + "Lusa", + "Lussi", + "Lussier", + "Lust", + "Lustick", + "Lustig", + "Lusty", + "Lutero", + "Luthanen", + "Luther", + "Luttrell", + "Luwana", + "Lux", + "Luz", + "Luzader", + "Ly", + "Lyall", + "Lyckman", + "Lyda", + "Lydell", + "Lydia", + "Lydie", + "Lydon", + "Lyell", + "Lyford", + "Lyle", + "Lyman", + "Lymann", + "Lymn", + "Lyn", + "Lynch", + "Lynd", + "Lynda", + "Lynde", + "Lyndel", + "Lyndell", + "Lynden", + "Lyndes", + "Lyndon", + "Lyndsay", + "Lyndsey", + "Lyndsie", + "Lyndy", + "Lynea", + "Lynelle", + "Lynett", + "Lynette", + "Lynn", + "Lynna", + "Lynne", + "Lynnea", + "Lynnell", + "Lynnelle", + "Lynnet", + "Lynnett", + "Lynnette", + "Lynnworth", + "Lyns", + "Lynsey", + "Lynus", + "Lyon", + "Lyons", + "Lyontine", + "Lyris", + "Lysander", + "Lyssa", + "Lytle", + "Lytton", + "Lyudmila", + "Ma", + "Maag", + "Mab", + "Mabel", + "Mabelle", + "Mable", + "Mac", + "MacCarthy", + "MacDermot", + "MacDonald", + "MacDonell", + "MacDougall", + "MacEgan", + "MacFadyn", + "MacFarlane", + "MacGregor", + "MacGuiness", + "MacIlroy", + "MacIntosh", + "MacIntyre", + "MacKay", + "MacKenzie", + "MacLaine", + "MacLay", + "MacLean", + "MacLeod", + "MacMahon", + "MacMillan", + "MacMullin", + "MacNair", + "MacNamara", + "MacPherson", + "MacRae", + "MacSwan", + "Macario", + "Maccarone", + "Mace", + "Macegan", + "Macey", + "Machos", + "Machute", + "Machutte", + "Mack", + "Mackenie", + "Mackenzie", + "Mackey", + "Mackie", + "Mackintosh", + "Mackler", + "Macknair", + "Mackoff", + "Macnair", + "Macomber", + "Macri", + "Macur", + "Macy", + "Mada", + "Madai", + "Madaih", + "Madalena", + "Madalyn", + "Madancy", + "Madaras", + "Maddalena", + "Madden", + "Maddeu", + "Maddi", + "Maddie", + "Maddis", + "Maddock", + "Maddocks", + "Maddox", + "Maddy", + "Madea", + "Madel", + "Madelaine", + "Madeleine", + "Madelena", + "Madelene", + "Madelin", + "Madelina", + "Madeline", + "Madella", + "Madelle", + "Madelon", + "Madelyn", + "Madge", + "Madi", + "Madian", + "Madid", + "Madigan", + "Madison", + "Madlen", + "Madlin", + "Madoc", + "Madonia", + "Madonna", + "Madora", + "Madox", + "Madra", + "Madriene", + "Madson", + "Mady", + "Mae", + "Maegan", + "Maeve", + "Mafala", + "Mafalda", + "Maffa", + "Maffei", + "Mag", + "Magan", + "Magas", + "Magavern", + "Magbie", + "Magda", + "Magdaia", + "Magdala", + "Magdalen", + "Magdalena", + "Magdalene", + "Magdau", + "Magee", + "Magel", + "Magen", + "Magena", + "Mages", + "Maggee", + "Maggi", + "Maggie", + "Maggio", + "Maggs", + "Maggy", + "Maghutte", + "Magill", + "Magna", + "Magner", + "Magnien", + "Magnolia", + "Magnum", + "Magnus", + "Magnuson", + "Magnusson", + "Magocsi", + "Magree", + "Maguire", + "Magulac", + "Mahala", + "Mahalia", + "Mahan", + "Mahau", + "Maher", + "Mahla", + "Mahmoud", + "Mahmud", + "Mahon", + "Mahoney", + "Maia", + "Maiah", + "Maibach", + "Maible", + "Maice", + "Maida", + "Maidel", + "Maidie", + "Maidy", + "Maier", + "Maiga", + "Maighdiln", + "Maighdlin", + "Mailand", + "Main", + "Mainis", + "Maiocco", + "Mair", + "Maire", + "Maise", + "Maisel", + "Maisey", + "Maisie", + "Maison", + "Maite", + "Maitilde", + "Maitland", + "Maitund", + "Maje", + "Majka", + "Major", + "Mak", + "Makell", + "Maker", + "Mal", + "Mala", + "Malachi", + "Malachy", + "Malamud", + "Malamut", + "Malan", + "Malanie", + "Malarkey", + "Malaspina", + "Malca", + "Malcah", + "Malchus", + "Malchy", + "Malcolm", + "Malcom", + "Malda", + "Maleeny", + "Malek", + "Maleki", + "Malena", + "Malet", + "Maletta", + "Mali", + "Malia", + "Malik", + "Malin", + "Malina", + "Malinda", + "Malinde", + "Malinin", + "Malinowski", + "Malissa", + "Malissia", + "Malita", + "Malka", + "Malkah", + "Malkin", + "Mall", + "Mallen", + "Maller", + "Malley", + "Mallin", + "Mallina", + "Mallis", + "Mallissa", + "Malloch", + "Mallon", + "Mallorie", + "Mallory", + "Malloy", + "Malo", + "Malone", + "Maloney", + "Malonis", + "Malony", + "Malorie", + "Malory", + "Maloy", + "Malti", + "Maltz", + "Maltzman", + "Malva", + "Malvia", + "Malvie", + "Malvin", + "Malvina", + "Malvino", + "Malynda", + "Mame", + "Mamie", + "Mamoun", + "Man", + "Manaker", + "Manara", + "Manard", + "Manchester", + "Mancino", + "Manda", + "Mandal", + "Mandel", + "Mandelbaum", + "Mandell", + "Mandeville", + "Mandi", + "Mandie", + "Mandle", + "Mandler", + "Mandy", + "Mandych", + "Manella", + "Manfred", + "Manheim", + "Mani", + "Manley", + "Manlove", + "Manly", + "Mann", + "Mannes", + "Mannie", + "Manning", + "Manno", + "Mannos", + "Mannuela", + "Manny", + "Mano", + "Manoff", + "Manolo", + "Manon", + "Manouch", + "Mansfield", + "Manson", + "Mansoor", + "Mansur", + "Manthei", + "Manton", + "Manuel", + "Manuela", + "Manus", + "Manvel", + "Manvell", + "Manvil", + "Manville", + "Manwell", + "Manya", + "Mapel", + "Mapes", + "Maples", + "Mar", + "Mara", + "Marabel", + "Marabelle", + "Marala", + "Marasco", + "Marashio", + "Marbut", + "Marc", + "Marceau", + "Marcel", + "Marcela", + "Marcelia", + "Marcell", + "Marcella", + "Marcelle", + "Marcellina", + "Marcelline", + "Marcello", + "Marcellus", + "Marcelo", + "March", + "Marchak", + "Marchal", + "Marchall", + "Marchelle", + "Marchese", + "Marci", + "Marcia", + "Marciano", + "Marcie", + "Marcile", + "Marcille", + "Marcin", + "Marco", + "Marcos", + "Marcoux", + "Marcus", + "Marcy", + "Marden", + "Marder", + "Marduk", + "Mareah", + "Marek", + "Marela", + "Mareld", + "Marelda", + "Marella", + "Marelya", + "Maren", + "Marena", + "Marentic", + "Maressa", + "Maretz", + "Marga", + "Margalit", + "Margalo", + "Margaret", + "Margareta", + "Margarete", + "Margaretha", + "Margarethe", + "Margaretta", + "Margarette", + "Margarida", + "Margarita", + "Margaux", + "Marge", + "Margeaux", + "Margery", + "Marget", + "Margette", + "Margetts", + "Margherita", + "Margi", + "Margie", + "Margit", + "Margo", + "Margot", + "Margret", + "Margreta", + "Marguerie", + "Marguerita", + "Marguerite", + "Margy", + "Mari", + "Maria", + "Mariam", + "Marian", + "Mariana", + "Mariand", + "Mariande", + "Mariandi", + "Mariann", + "Marianna", + "Marianne", + "Mariano", + "Maribel", + "Maribelle", + "Maribeth", + "Marice", + "Maridel", + "Marie", + "Marie-Ann", + "Marie-Jeanne", + "Marieann", + "Mariejeanne", + "Mariel", + "Mariele", + "Marielle", + "Mariellen", + "Marienthal", + "Marietta", + "Mariette", + "Marigold", + "Marigolda", + "Marigolde", + "Marijane", + "Marijn", + "Marijo", + "Marika", + "Mariken", + "Mariko", + "Maril", + "Marilee", + "Marilin", + "Marilla", + "Marillin", + "Marilou", + "Marilyn", + "Marin", + "Marina", + "Marinelli", + "Marinna", + "Marino", + "Mario", + "Marion", + "Mariquilla", + "Maris", + "Marisa", + "Mariska", + "Marissa", + "Marita", + "Maritsa", + "Marius", + "Mariya", + "Marj", + "Marja", + "Marjana", + "Marje", + "Marji", + "Marjie", + "Marjorie", + "Marjory", + "Marjy", + "Mark", + "Market", + "Marketa", + "Markland", + "Markman", + "Marko", + "Markos", + "Markowitz", + "Marks", + "Markson", + "Markus", + "Marl", + "Marla", + "Marlane", + "Marlea", + "Marleah", + "Marlee", + "Marleen", + "Marlen", + "Marlena", + "Marlene", + "Marler", + "Marlette", + "Marley", + "Marlie", + "Marlin", + "Marline", + "Marlo", + "Marlon", + "Marlow", + "Marlowe", + "Marlyn", + "Marmaduke", + "Marmawke", + "Marmion", + "Marna", + "Marne", + "Marney", + "Marni", + "Marnia", + "Marnie", + "Maro", + "Marola", + "Marolda", + "Maroney", + "Marou", + "Marozas", + "Marozik", + "Marpet", + "Marquardt", + "Marquet", + "Marquez", + "Marquis", + "Marquita", + "Marr", + "Marra", + "Marras", + "Marrilee", + "Marrin", + "Marriott", + "Marris", + "Marrissa", + "Marron", + "Mars", + "Marsden", + "Marsh", + "Marsha", + "Marshal", + "Marshall", + "Marsiella", + "Marsland", + "Marston", + "Mart", + "Marta", + "Martainn", + "Marte", + "Marteena", + "Martel", + "Martell", + "Martella", + "Martelle", + "Martelli", + "Marten", + "Martens", + "Martguerita", + "Martha", + "Marthe", + "Marthena", + "Marti", + "Martica", + "Martie", + "Martijn", + "Martin", + "Martina", + "Martine", + "Martineau", + "Martinelli", + "Martinez", + "Martinic", + "Martino", + "Martinsen", + "Martinson", + "Martita", + "Martres", + "Martsen", + "Marty", + "Martyn", + "Martynne", + "Martz", + "Marucci", + "Marutani", + "Marv", + "Marva", + "Marve", + "Marvel", + "Marvella", + "Marven", + "Marvin", + "Marwin", + "Marx", + "Mary", + "Marya", + "Maryann", + "Maryanna", + "Maryanne", + "Marybella", + "Marybelle", + "Marybeth", + "Maryellen", + "Maryjane", + "Maryjo", + "Maryl", + "Marylee", + "Marylin", + "Marylinda", + "Marylou", + "Maryly", + "Marylynne", + "Maryn", + "Maryrose", + "Marys", + "Marysa", + "Marzi", + "Mas", + "Masao", + "Mascia", + "Masera", + "Masha", + "Mashe", + "Mason", + "Masry", + "Massarelli", + "Massey", + "Massie", + "Massimiliano", + "Massimo", + "Massingill", + "Masson", + "Mast", + "Mastat", + "Masterson", + "Mastic", + "Mastrianni", + "Mat", + "Mata", + "Matazzoni", + "Matejka", + "Matelda", + "Mateo", + "Materi", + "Materse", + "Mateusz", + "Mateya", + "Mathe", + "Matheny", + "Mather", + "Matheson", + "Mathew", + "Mathews", + "Mathi", + "Mathia", + "Mathian", + "Mathias", + "Mathilda", + "Mathilde", + "Mathis", + "Mathre", + "Mathur", + "Matias", + "Matilda", + "Matilde", + "Matland", + "Matless", + "Matlick", + "Matrona", + "Matronna", + "Matt", + "Matta", + "Mattah", + "Matteo", + "Matthaeus", + "Matthaus", + "Matthei", + "Mattheus", + "Matthew", + "Matthews", + "Matthia", + "Matthias", + "Matthieu", + "Matthiew", + "Matthus", + "Matti", + "Mattias", + "Mattie", + "Mattland", + "Mattox", + "Mattson", + "Matty", + "Matusow", + "Mauceri", + "Mauchi", + "Maud", + "Maude", + "Maudie", + "Mauer", + "Mauldon", + "Maunsell", + "Maupin", + "Maura", + "Mauralia", + "Maure", + "Maureen", + "Maureene", + "Maurene", + "Maurer", + "Mauretta", + "Maurey", + "Mauri", + "Maurice", + "Mauricio", + "Maurie", + "Maurili", + "Maurilia", + "Maurilla", + "Maurine", + "Maurise", + "Maurita", + "Maurits", + "Maurizia", + "Maurizio", + "Mauro", + "Maurreen", + "Maury", + "Mauve", + "Mavilia", + "Mavis", + "Mavra", + "Max", + "Maxa", + "Maxama", + "Maxantia", + "Maxentia", + "Maxey", + "Maxfield", + "Maxi", + "Maxia", + "Maxie", + "Maxim", + "Maxima", + "Maximilian", + "Maximilianus", + "Maximilien", + "Maximo", + "Maxine", + "Maxma", + "Maxwell", + "Maxy", + "May", + "Maya", + "Maybelle", + "Mayberry", + "Mayce", + "Mayda", + "Maye", + "Mayeda", + "Mayer", + "Mayes", + "Mayfield", + "Mayhew", + "Mayman", + "Maynard", + "Mayne", + "Maynord", + "Mayor", + "Mays", + "Mayworm", + "Maze", + "Mazel", + "Maziar", + "Mazlack", + "Mazman", + "Mazonson", + "Mazur", + "Mazurek", + "McAdams", + "McAfee", + "McAllister", + "McArthur", + "McBride", + "McCafferty", + "McCahill", + "McCall", + "McCallion", + "McCallum", + "McCandless", + "McCartan", + "McCarthy", + "McCarty", + "McClain", + "McClary", + "McClees", + "McClelland", + "McClenaghan", + "McClenon", + "McClimans", + "McClish", + "McClure", + "McCollum", + "McComb", + "McConaghy", + "McConnell", + "McCord", + "McCormac", + "McCormick", + "McCourt", + "McCowyn", + "McCoy", + "McCready", + "McCreary", + "McCreery", + "McCulloch", + "McCullough", + "McCully", + "McCurdy", + "McCutcheon", + "McDade", + "McDermott", + "McDonald", + "McDougall", + "McDowell", + "McEvoy", + "McFadden", + "McFarland", + "McFerren", + "McGannon", + "McGaw", + "McGean", + "McGee", + "McGill", + "McGinnis", + "McGrath", + "McGraw", + "McGray", + "McGregor", + "McGrody", + "McGruter", + "McGuire", + "McGurn", + "McHail", + "McHale", + "McHenry", + "McHugh", + "McIlroy", + "McIntosh", + "McIntyre", + "McKale", + "McKay", + "McKee", + "McKenna", + "McKenzie", + "McKeon", + "McKinney", + "McKnight", + "McLain", + "McLaughlin", + "McLaurin", + "McLeod", + "McLeroy", + "McLoughlin", + "McLyman", + "McMahon", + "McMaster", + "McMath", + "McMillan", + "McMullan", + "McMurry", + "McNair", + "McNalley", + "McNally", + "McNamara", + "McNamee", + "McNeely", + "McNeil", + "McNelly", + "McNully", + "McNutt", + "McQuade", + "McQuillin", + "McQuoid", + "McRipley", + "McRoberts", + "McSpadden", + "McTyre", + "McWherter", + "McWilliams", + "Mead", + "Meade", + "Meador", + "Meadow", + "Meadows", + "Meagan", + "Meaghan", + "Meagher", + "Meakem", + "Means", + "Meara", + "Meares", + "Mears", + "Meave", + "Mechelle", + "Mechling", + "Mecke", + "Meda", + "Medarda", + "Medardas", + "Medea", + "Medeah", + "Medin", + "Medina", + "Medlin", + "Medor", + "Medora", + "Medorra", + "Medovich", + "Medrek", + "Medwin", + "Meece", + "Meehan", + "Meek", + "Meeker", + "Meeks", + "Meenen", + "Meg", + "Megan", + "Megargee", + "Megdal", + "Megen", + "Meggi", + "Meggie", + "Meggs", + "Meggy", + "Meghan", + "Meghann", + "Mehala", + "Mehalek", + "Mehalick", + "Mehetabel", + "Mehitable", + "Mehta", + "Mei", + "Meibers", + "Meier", + "Meijer", + "Meilen", + "Meill", + "Meingolda", + "Meingoldas", + "Meir", + "Meisel", + "Meit", + "Mel", + "Mela", + "Melamed", + "Melamie", + "Melan", + "Melania", + "Melanie", + "Melantha", + "Melany", + "Melar", + "Melba", + "Melborn", + "Melbourne", + "Melburn", + "Melcher", + "Melda", + "Meldoh", + "Meldon", + "Melena", + "Melentha", + "Melesa", + "Melessa", + "Meletius", + "Melgar", + "Meli", + "Melia", + "Melicent", + "Melina", + "Melinda", + "Melinde", + "Melisa", + "Melisande", + "Melisandra", + "Melise", + "Melisenda", + "Melisent", + "Melissa", + "Melisse", + "Melita", + "Melitta", + "Mell", + "Mella", + "Mellar", + "Mellen", + "Melleta", + "Mellette", + "Melli", + "Mellicent", + "Mellie", + "Mellins", + "Mellisa", + "Mellisent", + "Mellitz", + "Mellman", + "Mello", + "Melloney", + "Melly", + "Melmon", + "Melnick", + "Melodee", + "Melodie", + "Melody", + "Melone", + "Melonie", + "Melony", + "Melosa", + "Melquist", + "Melton", + "Melva", + "Melvena", + "Melville", + "Melvin", + "Melvina", + "Melvyn", + "Memberg", + "Memory", + "Mena", + "Menard", + "Menashem", + "Mencher", + "Mendel", + "Mendelsohn", + "Mendelson", + "Mendes", + "Mendez", + "Mendie", + "Mendive", + "Mendoza", + "Mendy", + "Meneau", + "Menedez", + "Menell", + "Menendez", + "Meng", + "Menides", + "Menis", + "Menken", + "Menon", + "Mensch", + "Menzies", + "Mera", + "Meraree", + "Merari", + "Meras", + "Merat", + "Merc", + "Mercado", + "Merce", + "Mercedes", + "Merceer", + "Mercer", + "Merchant", + "Merci", + "Mercie", + "Mercier", + "Mercola", + "Mercorr", + "Mercuri", + "Mercy", + "Merdith", + "Meredeth", + "Meredi", + "Meredith", + "Meredithe", + "Merell", + "Merete", + "Meri", + "Meridel", + "Merideth", + "Meridith", + "Meriel", + "Merilee", + "Merill", + "Merilyn", + "Meris", + "Merissa", + "Merkle", + "Merkley", + "Merl", + "Merla", + "Merle", + "Merlin", + "Merlina", + "Merline", + "Merna", + "Merola", + "Merow", + "Merralee", + "Merras", + "Merrel", + "Merrell", + "Merri", + "Merriam", + "Merrick", + "Merridie", + "Merrie", + "Merrielle", + "Merril", + "Merrile", + "Merrilee", + "Merrili", + "Merrill", + "Merrily", + "Merriman", + "Merriott", + "Merritt", + "Merrow", + "Merry", + "Mersey", + "Mert", + "Merta", + "Merth", + "Merton", + "Merv", + "Mervin", + "Merwin", + "Merwyn", + "Meryl", + "Mesics", + "Messere", + "Messing", + "Meta", + "Metabel", + "Metcalf", + "Meter", + "Methuselah", + "Metsky", + "Mettah", + "Metts", + "Metzgar", + "Metzger", + "Meunier", + "Meurer", + "Meuse", + "Meuser", + "Meyer", + "Meyeroff", + "Meyers", + "Mezoff", + "Mia", + "Mic", + "Micaela", + "Micah", + "Micco", + "Mich", + "Michael", + "Michaela", + "Michaele", + "Michaelina", + "Michaeline", + "Michaella", + "Michaeu", + "Michail", + "Michal", + "Michale", + "Michaud", + "Miche", + "Micheal", + "Micheil", + "Michel", + "Michele", + "Michelina", + "Micheline", + "Michell", + "Michella", + "Michelle", + "Michelsen", + "Michey", + "Michi", + "Michigan", + "Michiko", + "Michon", + "Mick", + "Mickelson", + "Mickey", + "Micki", + "Mickie", + "Micky", + "Micro", + "Miculek", + "Midas", + "Middendorf", + "Middle", + "Middlesworth", + "Middleton", + "Mide", + "Midge", + "Midian", + "Midis", + "Mientao", + "Miett", + "Migeon", + "Mighell", + "Mignon", + "Mignonne", + "Miguel", + "Miguela", + "Miguelita", + "Mihalco", + "Mihe", + "Mika", + "Mikael", + "Mikaela", + "Mikal", + "Mike", + "Mikel", + "Mikes", + "Mikey", + "Miki", + "Mikihisa", + "Mikiso", + "Mikkanen", + "Mikkel", + "Miko", + "Mikol", + "Miksen", + "Mil", + "Mila", + "Milan", + "Milano", + "Milburn", + "Milburr", + "Milburt", + "Milda", + "Milde", + "Mildred", + "Mildrid", + "Mile", + "Milena", + "Miles", + "Milewski", + "Milford", + "Milicent", + "Milinda", + "Milissa", + "Milissent", + "Milka", + "Milks", + "Mill", + "Milla", + "Millan", + "Millar", + "Millard", + "Millburn", + "Millda", + "Miller", + "Millford", + "Millham", + "Millhon", + "Milli", + "Millian", + "Millicent", + "Millie", + "Millisent", + "Millman", + "Mills", + "Millur", + "Millwater", + "Milly", + "Milman", + "Milo", + "Milon", + "Milone", + "Milore", + "Milson", + "Milstone", + "Milt", + "Miltie", + "Milton", + "Milty", + "Milurd", + "Milzie", + "Mima", + "Mimi", + "Min", + "Mina", + "Minabe", + "Minardi", + "Minda", + "Mindi", + "Mindy", + "Miner", + "Minerva", + "Mines", + "Minetta", + "Minette", + "Ming", + "Mingche", + "Mini", + "Minica", + "Minier", + "Minna", + "Minnaminnie", + "Minne", + "Minni", + "Minnie", + "Minnnie", + "Minny", + "Minor", + "Minoru", + "Minsk", + "Minta", + "Minton", + "Mintun", + "Mintz", + "Miof Mela", + "Miquela", + "Mir", + "Mira", + "Mirabel", + "Mirabella", + "Mirabelle", + "Miran", + "Miranda", + "Mireielle", + "Mireille", + "Mirella", + "Mirelle", + "Miriam", + "Mirielle", + "Mirilla", + "Mirisola", + "Mirna", + "Mirth", + "Miru", + "Mischa", + "Misha", + "Mishaan", + "Missi", + "Missie", + "Missy", + "Misti", + "Mistrot", + "Misty", + "Mita", + "Mitch", + "Mitchael", + "Mitchel", + "Mitchell", + "Mitchiner", + "Mitinger", + "Mitman", + "Mitran", + "Mittel", + "Mitzi", + "Mitzie", + "Mitzl", + "Miun", + "Mixie", + "Miyasawa", + "Mizuki", + "Mlawsky", + "Mllly", + "Moazami", + "Moberg", + "Mobley", + "Mochun", + "Mode", + "Modern", + "Modesta", + "Modeste", + "Modestia", + "Modestine", + "Modesty", + "Modie", + "Modla", + "Moe", + "Moersch", + "Moffat", + "Moffit", + "Moffitt", + "Mogerly", + "Moguel", + "Mohamed", + "Mohammad", + "Mohammed", + "Mohandas", + "Mohandis", + "Mohl", + "Mohn", + "Mohr", + "Mohsen", + "Mohun", + "Moia", + "Moina", + "Moir", + "Moira", + "Moise", + "Moises", + "Moishe", + "Moitoso", + "Mojgan", + "Mok", + "Mokas", + "Molini", + "Moll", + "Mollee", + "Molli", + "Mollie", + "Molloy", + "Molly", + "Molton", + "Mommy", + "Mona", + "Monaco", + "Monafo", + "Monagan", + "Monah", + "Monahan", + "Monahon", + "Monarski", + "Moncear", + "Mond", + "Monda", + "Moneta", + "Monetta", + "Mongeau", + "Monia", + "Monica", + "Monie", + "Monika", + "Monique", + "Monjan", + "Monjo", + "Monk", + "Monney", + "Monreal", + "Monro", + "Monroe", + "Monroy", + "Monson", + "Monsour", + "Mont", + "Montagna", + "Montagu", + "Montague", + "Montana", + "Montanez", + "Montano", + "Monte", + "Monteith", + "Monteria", + "Montford", + "Montfort", + "Montgomery", + "Monti", + "Monto", + "Monty", + "Moody", + "Mook", + "Moon", + "Mooney", + "Moonier", + "Moor", + "Moore", + "Moorefield", + "Moorish", + "Mor", + "Mora", + "Moran", + "Mord", + "Mordecai", + "Mordy", + "Moreen", + "Morehouse", + "Morel", + "Moreland", + "Morell", + "Morena", + "Moreno", + "Morentz", + "Moreta", + "Moretta", + "Morette", + "Moreville", + "Morey", + "Morez", + "Morgan", + "Morgana", + "Morganica", + "Morganne", + "Morganstein", + "Morgen", + "Morgenthaler", + "Morgun", + "Mori", + "Moria", + "Moriah", + "Moriarty", + "Morice", + "Morie", + "Morissa", + "Morita", + "Moritz", + "Moriyama", + "Morlee", + "Morley", + "Morly", + "Morna", + "Morocco", + "Morra", + "Morrell", + "Morrie", + "Morril", + "Morrill", + "Morris", + "Morrison", + "Morrissey", + "Morry", + "Morse", + "Mort", + "Morten", + "Mortensen", + "Mortie", + "Mortimer", + "Morton", + "Morty", + "Morven", + "Morville", + "Morvin", + "Mosa", + "Mosby", + "Moscow", + "Mose", + "Moseley", + "Moselle", + "Mosenthal", + "Moser", + "Mosera", + "Moses", + "Moshe", + "Moshell", + "Mosier", + "Mosira", + "Moskow", + "Mosley", + "Mosora", + "Mosra", + "Moss", + "Mossberg", + "Mossman", + "Most", + "Motch", + "Moth", + "Mott", + "Motteo", + "Mou", + "Moulden", + "Mouldon", + "Moule", + "Moulton", + "Mount", + "Mountford", + "Mountfort", + "Mourant", + "Moureaux", + "Mowbray", + "Moya", + "Moyer", + "Moyers", + "Moyna", + "Moynahan", + "Moyra", + "Mozart", + "Mozelle", + "Mozes", + "Mozza", + "Mraz", + "Mroz", + "Mueller", + "Muffin", + "Mufi", + "Mufinella", + "Muhammad", + "Muir", + "Muire", + "Muirhead", + "Mukerji", + "Mukul", + "Mukund", + "Mulcahy", + "Mulderig", + "Muldon", + "Mulford", + "Mullane", + "Mullen", + "Muller", + "Mulligan", + "Mullins", + "Mulloy", + "Mulry", + "Mulvihill", + "Mumford", + "Mun", + "Muna", + "Munafo", + "Muncey", + "Mundford", + "Mundt", + "Mundy", + "Munford", + "Mungo", + "Mungovan", + "Munmro", + "Munn", + "Munniks", + "Munro", + "Munroe", + "Muns", + "Munsey", + "Munshi", + "Munson", + "Munster", + "Munt", + "Mur", + "Murage", + "Muraida", + "Murat", + "Murdocca", + "Murdoch", + "Murdock", + "Mureil", + "Muriah", + "Murial", + "Muriel", + "Murielle", + "Murphy", + "Murrah", + "Murray", + "Murrell", + "Murry", + "Murtagh", + "Murtha", + "Murton", + "Murvyn", + "Musa", + "Muscolo", + "Musetta", + "Musette", + "Mushro", + "Muslim", + "Musser", + "Mussman", + "Mutz", + "My", + "Mya", + "Myca", + "Mycah", + "Mychael", + "Mychal", + "Myer", + "Myers", + "Myke", + "Mylan", + "Mylander", + "Myles", + "Mylo", + "Mylor", + "Myna", + "Myo", + "Myra", + "Myrah", + "Myranda", + "Myriam", + "Myrilla", + "Myrle", + "Myrlene", + "Myrna", + "Myron", + "Myrt", + "Myrta", + "Myrtia", + "Myrtice", + "Myrtie", + "Myrtle", + "Myrvyn", + "Myrwyn", + "Na", + "Naam", + "Naaman", + "Naamana", + "Naamann", + "Naara", + "Naarah", + "Naashom", + "Nabal", + "Nabala", + "Nabalas", + "Nabila", + "Nace", + "Nachison", + "Nada", + "Nadab", + "Nadaba", + "Nadabas", + "Nadabb", + "Nadabus", + "Nadaha", + "Nadbus", + "Nadda", + "Nadean", + "Nadeau", + "Nadeen", + "Nader", + "Nadia", + "Nadine", + "Nadiya", + "Nadler", + "Nador", + "Nady", + "Nadya", + "Nafis", + "Naga", + "Nagel", + "Nagey", + "Nagle", + "Nagy", + "Nahama", + "Nahamas", + "Nahshon", + "Nahshu", + "Nahshun", + "Nahshunn", + "Nahtanha", + "Nahum", + "Naiditch", + "Naima", + "Naji", + "Nakada", + "Nakashima", + "Nakasuji", + "Nalani", + "Nalda", + "Naldo", + "Nalepka", + "Nally", + "Nalor", + "Nam", + "Naman", + "Namara", + "Names", + "Nan", + "Nana", + "Nananne", + "Nance", + "Nancee", + "Nancey", + "Nanci", + "Nancie", + "Nancy", + "Nandor", + "Nanete", + "Nanette", + "Nani", + "Nanice", + "Nanine", + "Nanji", + "Nannette", + "Nanni", + "Nannie", + "Nanny", + "Nanon", + "Naoma", + "Naomi", + "Naor", + "Nap", + "Napier", + "Naples", + "Napoleon", + "Nappie", + "Nappy", + "Naquin", + "Nara", + "Narah", + "Narayan", + "Narcho", + "Narcis", + "Narcissus", + "Narda", + "Naresh", + "Nari", + "Nariko", + "Narine", + "Narra", + "Narton", + "Nary", + "Nash", + "Nashbar", + "Nashner", + "Nasho", + "Nashom", + "Nashoma", + "Nasia", + "Nason", + "Nassi", + "Nassir", + "Nastassia", + "Nasya", + "Nat", + "Nata", + "Natal", + "Natala", + "Natale", + "Natalee", + "Natalia", + "Natalie", + "Natalina", + "Nataline", + "Natalya", + "Nataniel", + "Natascha", + "Natasha", + "Natassia", + "Nate", + "Natelson", + "Nath", + "Nathalia", + "Nathalie", + "Nathan", + "Nathanael", + "Nathanial", + "Nathaniel", + "Nathanil", + "Nathanson", + "Natica", + "Natie", + "Natiha", + "Natika", + "Nations", + "Natividad", + "Natka", + "Nattie", + "Natty", + "Nava", + "Navada", + "Naval", + "Navarro", + "Nawrocki", + "Nay", + "Naylor", + "Nazar", + "Nazario", + "Nazarius", + "Nazler", + "Nea", + "Neal", + "Neala", + "Nealah", + "Neale", + "Nealey", + "Neall", + "Nealon", + "Nealson", + "Nealy", + "Neau", + "Ned", + "Neda", + "Nedda", + "Neddie", + "Neddra", + "Neddy", + "Nedi", + "Nedra", + "Nedrah", + "Nedrud", + "Nedry", + "Nee", + "Neel", + "Neela", + "Neelon", + "Neely", + "Neeoma", + "Nefen", + "Neff", + "Negris", + "Nehemiah", + "Neibart", + "Neidhardt", + "Neil", + "Neila", + "Neile", + "Neill", + "Neilla", + "Neille", + "Neils", + "Neilson", + "Neiman", + "Neisa", + "Nel", + "Nela", + "Nelan", + "Nelda", + "Nelia", + "Nelie", + "Nell", + "Nella", + "Nellda", + "Nelle", + "Nelli", + "Nellie", + "Nellir", + "Nelly", + "Nelrsa", + "Nels", + "Nelsen", + "Nelson", + "Nema", + "Nemhauser", + "Nena", + "Nenney", + "Neo", + "Neom", + "Neoma", + "Neomah", + "Neona", + "Nepean", + "Nepil", + "Nereen", + "Nereids", + "Nereus", + "Neri", + "Nerin", + "Nerine", + "Nerissa", + "Nerita", + "Nerland", + "Nero", + "Neron", + "Nert", + "Nerta", + "Nerte", + "Nerti", + "Nertie", + "Nerty", + "Nesbitt", + "Nesline", + "Neslund", + "Ness", + "Nessa", + "Nessi", + "Nessie", + "Nessim", + "Nessy", + "Nesta", + "Nester", + "Nesto", + "Nestor", + "Nett", + "Netta", + "Nette", + "Netti", + "Nettie", + "Nettle", + "Netty", + "Neu", + "Neuberger", + "Neuburger", + "Neufer", + "Neukam", + "Neumann", + "Neumark", + "Neumeyer", + "Neurath", + "Nev", + "Neva", + "Nevada", + "Nevai", + "Neve", + "Neveda", + "Nevil", + "Nevile", + "Neville", + "Nevin", + "Nevins", + "Nevlin", + "Nevsa", + "New", + "Newberry", + "Newbill", + "Newbold", + "Newby", + "Newcomb", + "Newcomer", + "Newel", + "Newell", + "Newfeld", + "Newhall", + "Newkirk", + "Newlin", + "Newman", + "Newmann", + "Newmark", + "Newsom", + "Newton", + "Neysa", + "Ng", + "Ngo", + "Nguyen", + "Niabi", + "Nial", + "Niall", + "Nibbs", + "Nic", + "Nica", + "Niccolo", + "Nich", + "Nichani", + "Nichol", + "Nichola", + "Nicholas", + "Nichole", + "Nicholl", + "Nicholle", + "Nichols", + "Nicholson", + "Nichy", + "Nick", + "Nickelsen", + "Nickerson", + "Nickey", + "Nicki", + "Nickie", + "Nickles", + "Nicko", + "Nickola", + "Nickolai", + "Nickolas", + "Nickolaus", + "Nicks", + "Nicky", + "Nico", + "Nicodemus", + "Nicol", + "Nicola", + "Nicolai", + "Nicolais", + "Nicolas", + "Nicolau", + "Nicole", + "Nicolea", + "Nicolella", + "Nicolette", + "Nicoli", + "Nicolina", + "Nicoline", + "Nicolis", + "Nicolle", + "Nidia", + "Nidorf", + "Nieberg", + "Niehaus", + "Niel", + "Niela", + "Niels", + "Nielsen", + "Nielson", + "Nierman", + "Nies", + "Nievelt", + "Nigel", + "Nightingale", + "Nihhi", + "Nihi", + "Nika", + "Nikaniki", + "Nike", + "Niki", + "Nikita", + "Nikki", + "Nikkie", + "Niklaus", + "Niko", + "Nikola", + "Nikolai", + "Nikolaos", + "Nikolas", + "Nikolaus", + "Nikoletta", + "Nikolia", + "Nikolos", + "Nikos", + "Nil", + "Nila", + "Nile", + "Niles", + "Nilla", + "Nils", + "Nilson", + "Nimesh", + "Nimocks", + "Nims", + "Nina", + "Nine", + "Ninetta", + "Ninette", + "Ninnetta", + "Ninnette", + "Nino", + "Ninon", + "Ninos", + "Niobe", + "Nipha", + "Niple", + "Nisa", + "Nisbet", + "Nisen", + "Nishi", + "Nissa", + "Nisse", + "Nissensohn", + "Nissie", + "Nissy", + "Nita", + "Nitin", + "Nitz", + "Nitza", + "Niu", + "Niven", + "Nixie", + "Nixon", + "Noach", + "Noah", + "Noak", + "Noakes", + "Noam", + "Noami", + "Nobe", + "Nobel", + "Nobell", + "Nobie", + "Nobile", + "Noble", + "Noby", + "Nochur", + "Nodab", + "Nodababus", + "Nodarse", + "Noe", + "Noel", + "Noelani", + "Noell", + "Noella", + "Noelle", + "Noellyn", + "Noelyn", + "Noemi", + "Nogas", + "Noguchi", + "Nola", + "Nolan", + "Nolana", + "Noland", + "Nole", + "Noleta", + "Noletta", + "Noli", + "Nolie", + "Nolita", + "Nolitta", + "Noll", + "Nollie", + "Nolly", + "Nolte", + "Noma", + "Noman", + "Nomi", + "Nona", + "Nonah", + "Noni", + "Nonie", + "Nonna", + "Nonnah", + "Noonan", + "Noonberg", + "Nor", + "Nora", + "Norah", + "Norbert", + "Norbie", + "Norby", + "Nord", + "Nordgren", + "Nordin", + "Nordine", + "Nore", + "Norean", + "Noreen", + "Norene", + "Norford", + "Norina", + "Norine", + "Norita", + "Nork", + "Norling", + "Norm", + "Norma", + "Normalie", + "Norman", + "Normand", + "Normandy", + "Normi", + "Normie", + "Normy", + "Norri", + "Norrie", + "Norris", + "Norrv", + "Norry", + "Norse", + "North", + "Northey", + "Northington", + "Northrop", + "Northrup", + "Northway", + "Norton", + "Norty", + "Norval", + "Norvall", + "Norvan", + "Norvell", + "Norven", + "Norvil", + "Norvin", + "Norvol", + "Norvun", + "Norward", + "Norwood", + "Norword", + "Nottage", + "Nova", + "Novah", + "Novak", + "Novelia", + "Novello", + "Novia", + "Novick", + "Novikoff", + "Nowell", + "Noyes", + "Nozicka", + "Nudd", + "Nugent", + "Nuli", + "Nunci", + "Nuncia", + "Nunciata", + "Nunes", + "Nunnery", + "Nur", + "Nuri", + "Nuriel", + "Nuris", + "Nurse", + "Nussbaum", + "Nutter", + "Nuzzi", + "Nyberg", + "Nydia", + "Nye", + "Nyhagen", + "Nysa", + "Nyssa", + "O'Hara", + "O'Neill", + "Oak", + "Oakes", + "Oakie", + "Oakleil", + "Oakley", + "Oakman", + "Oaks", + "Oates", + "Oatis", + "Oba", + "Obadiah", + "Obadias", + "Obala", + "Oballa", + "Obara", + "Obau", + "Obaza", + "Obbard", + "Obe", + "Obed", + "Obeded", + "Obediah", + "Obel", + "Obelia", + "Obellia", + "Obeng", + "Ober", + "Oberg", + "Oberheim", + "Oberon", + "Oberstone", + "Obidiah", + "Obie", + "Obla", + "Obola", + "Obrien", + "Oby", + "Oca", + "Ocana", + "Ochs", + "Ocker", + "Ocko", + "Oconnor", + "Octave", + "Octavia", + "Octavian", + "Octavie", + "Octavius", + "Octavla", + "Octavus", + "Odab", + "Odawa", + "Ode", + "Odeen", + "Odel", + "Odele", + "Odelet", + "Odelia", + "Odelinda", + "Odell", + "Odella", + "Odelle", + "Odericus", + "Odessa", + "Odetta", + "Odette", + "Odey", + "Odie", + "Odilia", + "Odille", + "Odilo", + "Odin", + "Odine", + "Odlo", + "Odo", + "Odom", + "Odoric", + "Odrick", + "Ody", + "Odysseus", + "Odyssey", + "Oech", + "Oeflein", + "Oehsen", + "Ofelia", + "Ofella", + "Offen", + "Ofilia", + "Ofori", + "Og", + "Ogata", + "Ogawa", + "Ogdan", + "Ogden", + "Ogdon", + "Ogg", + "Ogilvie", + "Ogilvy", + "Oglesby", + "Ogren", + "Ohara", + "Ohare", + "Ohaus", + "Ohl", + "Oilla", + "Oina", + "Oira", + "Okajima", + "Okechuku", + "Okubo", + "Okun", + "Okwu", + "Ola", + "Olaf", + "Olag", + "Olatha", + "Olathe", + "Olav", + "Olcott", + "Old", + "Older", + "Olds", + "Ole", + "Oleg", + "Olen", + "Olenka", + "Olenolin", + "Olenta", + "Oler", + "Oleta", + "Oletha", + "Olethea", + "Oletta", + "Olette", + "Olfe", + "Olga", + "Olia", + "Oliana", + "Olimpia", + "Olin", + "Olinde", + "Oliva", + "Olivann", + "Olive", + "Oliver", + "Olivero", + "Olivette", + "Olivia", + "Olivie", + "Olivier", + "Oliviero", + "Oliy", + "Ollayos", + "Olli", + "Ollie", + "Olly", + "Olmstead", + "Olmsted", + "Olnay", + "Olnee", + "Olnek", + "Olney", + "Olnton", + "Olodort", + "Olpe", + "Olsen", + "Olsewski", + "Olshausen", + "Olson", + "Olsson", + "Olva", + "Olvan", + "Olwen", + "Olwena", + "Oly", + "Olympe", + "Olympia", + "Olympias", + "Olympie", + "Olympium", + "Om", + "Oman", + "Omar", + "Omari", + "Omarr", + "Omer", + "Omero", + "Omidyar", + "Omland", + "Omor", + "Omora", + "Omura", + "On", + "Ona", + "Onder", + "Ondine", + "Ondrea", + "Ondrej", + "Oneal", + "Oneida", + "Oneil", + "Oneill", + "Onfre", + "Onfroi", + "Ong", + "Ongun", + "Oni", + "Onia", + "Onida", + "Oniskey", + "Onofredo", + "Onstad", + "Ontina", + "Ontine", + "Onyx", + "Oona", + "Opal", + "Opalina", + "Opaline", + "Ophelia", + "Ophelie", + "Oppen", + "Opportina", + "Opportuna", + "Ora", + "Orabel", + "Orabelle", + "Oralee", + "Oralia", + "Oralie", + "Oralla", + "Oralle", + "Oram", + "Oran", + "Orazio", + "Orbadiah", + "Orban", + "Ordway", + "Orel", + "Orelee", + "Orelia", + "Orelie", + "Orella", + "Orelle", + "Orelu", + "Oren", + "Orest", + "Oreste", + "Orestes", + "Orferd", + "Orfield", + "Orfinger", + "Orford", + "Orfurd", + "Orgel", + "Orgell", + "Ori", + "Oria", + "Orian", + "Oriana", + "Oriane", + "Orianna", + "Oribel", + "Oribella", + "Oribelle", + "Oriel", + "Orin", + "Oringa", + "Oringas", + "Oriole", + "Orion", + "Orit", + "Orji", + "Orlan", + "Orland", + "Orlando", + "Orlanta", + "Orlantha", + "Orlena", + "Orlene", + "Orlina", + "Orling", + "Orlosky", + "Orlov", + "Orly", + "Orman", + "Ormand", + "Orme", + "Ormiston", + "Ormond", + "Orms", + "Ormsby", + "Orna", + "Ornas", + "Ornie", + "Ornstead", + "Orola", + "Orose", + "Orozco", + "Orpah", + "Orpha", + "Orpheus", + "Orr", + "Orran", + "Orren", + "Orrin", + "Orsa", + "Orsay", + "Orsini", + "Orsino", + "Orsola", + "Orson", + "Orten", + "Ortensia", + "Orth", + "Orthman", + "Ortiz", + "Orton", + "Ortrud", + "Ortrude", + "Oruntha", + "Orv", + "Orva", + "Orvah", + "Orvan", + "Orvas", + "Orvie", + "Orvil", + "Orville", + "Orwin", + "Os", + "Osana", + "Osanna", + "Osber", + "Osbert", + "Osborn", + "Osborne", + "Osbourn", + "Osbourne", + "Oscar", + "Osei", + "Osgood", + "Osher", + "Oshinski", + "Osi", + "Osithe", + "Oskar", + "Osman", + "Osmen", + "Osmo", + "Osmond", + "Osmund", + "Osric", + "Osrick", + "Osrock", + "Ossie", + "Osswald", + "Ossy", + "Ostap", + "Oster", + "Osterhus", + "Ostler", + "Ostraw", + "Osugi", + "Oswal", + "Oswald", + "Oswell", + "Oswin", + "Osy", + "Osyth", + "Ot", + "Otero", + "Otes", + "Otha", + "Othe", + "Othelia", + "Othella", + "Othello", + "Other", + "Othilia", + "Othilie", + "Otho", + "Otila", + "Otilia", + "Otina", + "Otis", + "Ott", + "Ottavia", + "Otte", + "Otter", + "Otti", + "Ottie", + "Ottilie", + "Ottillia", + "Ottinger", + "Otto", + "Oulman", + "Outhe", + "Outlaw", + "Ovid", + "Ovida", + "Owades", + "Owain", + "Owen", + "Owena", + "Owens", + "Oxford", + "Oxley", + "Oys", + "Oz", + "Oza", + "Ozan", + "Ozen", + "Ozkum", + "Ozmo", + "Ozzie", + "Ozzy", + "O'Brien", + "O'Callaghan", + "O'Carroll", + "O'Connell", + "O'Conner", + "O'Connor", + "O'Dell", + "O'Doneven", + "O'Donnell", + "O'Donoghue", + "O'Donovan", + "O'Driscoll", + "O'Gowan", + "O'Grady", + "O'Hara", + "O'Kelly", + "O'Mahony", + "O'Malley", + "O'Meara", + "O'Neil", + "O'Neill", + "O'Reilly", + "O'Rourke", + "O'Shee", + "O'Toole", + "Paapanen", + "Pablo", + "Pace", + "Pacheco", + "Pachston", + "Pachton", + "Pacian", + "Pacien", + "Pacifa", + "Pacifica", + "Pacificas", + "Pacificia", + "Pack", + "Packer", + "Packston", + "Packton", + "Paco", + "Pacorro", + "Paddie", + "Paddy", + "Padegs", + "Paderna", + "Padget", + "Padgett", + "Padraic", + "Padraig", + "Padriac", + "Paff", + "Pagas", + "Page", + "Pages", + "Paget", + "Pahl", + "Paige", + "Paik", + "Pail", + "Pain", + "Paine", + "Painter", + "Palecek", + "Palermo", + "Palestine", + "Paley", + "Palgrave", + "Palila", + "Pall", + "Palla", + "Palladin", + "Pallas", + "Pallaten", + "Pallaton", + "Pallua", + "Palm", + "Palma", + "Palmer", + "Palmira", + "Palmore", + "Palocz", + "Paloma", + "Pals", + "Palua", + "Paluas", + "Palumbo", + "Pam", + "Pamela", + "Pamelina", + "Pamella", + "Pammi", + "Pammie", + "Pammy", + "Pampuch", + "Pan", + "Panaggio", + "Panayiotis", + "Panchito", + "Pancho", + "Pandich", + "Pandolfi", + "Pandora", + "Pang", + "Pangaro", + "Pani", + "Pansie", + "Pansir", + "Pansy", + "Panta", + "Panter", + "Panthea", + "Pantheas", + "Panther", + "Panthia", + "Pantia", + "Pantin", + "Paola", + "Paolina", + "Paolo", + "Papagena", + "Papageno", + "Pape", + "Papert", + "Papke", + "Papotto", + "Papp", + "Pappano", + "Pappas", + "Papst", + "Paquito", + "Par", + "Paradies", + "Parcel", + "Pardew", + "Pardner", + "Pardo", + "Pardoes", + "Pare", + "Parent", + "Paresh", + "Parette", + "Parfitt", + "Parhe", + "Parik", + "Paris", + "Parish", + "Park", + "Parke", + "Parker", + "Parks", + "Parlin", + "Parnas", + "Parnell", + "Parrie", + "Parris", + "Parrisch", + "Parrish", + "Parrnell", + "Parrott", + "Parry", + "Parsaye", + "Parshall", + "Parsifal", + "Parsons", + "Partan", + "Parthen", + "Parthena", + "Parthenia", + "Parthinia", + "Particia", + "Partridge", + "Paryavi", + "Pas", + "Pasadis", + "Pasahow", + "Pascal", + "Pascale", + "Pascasia", + "Pascha", + "Paschasia", + "Pascia", + "Pasco", + "Pascoe", + "Pasho", + "Pasia", + "Paske", + "Pasol", + "Pasquale", + "Pass", + "Past", + "Pastelki", + "Pat", + "Pate", + "Paten", + "Paterson", + "Pathe", + "Patience", + "Patin", + "Patman", + "Patnode", + "Paton", + "Patric", + "Patrica", + "Patrice", + "Patrich", + "Patricia", + "Patricio", + "Patrick", + "Patrizia", + "Patrizio", + "Patrizius", + "Patsis", + "Patsy", + "Patt", + "Pattani", + "Patten", + "Patterman", + "Patterson", + "Patti", + "Pattie", + "Pattin", + "Pattison", + "Patton", + "Patty", + "Paucker", + "Paugh", + "Pauiie", + "Paul", + "Paula", + "Paule", + "Pauletta", + "Paulette", + "Pauli", + "Paulie", + "Paulina", + "Pauline", + "Paulita", + "Paulo", + "Paulsen", + "Paulson", + "Pauly", + "Pauwles", + "Pavel", + "Paver", + "Pavia", + "Pavier", + "Pavior", + "Paviour", + "Pavkovic", + "Pavla", + "Pavlish", + "Pavlov", + "Pavyer", + "Pawsner", + "Pax", + "Paxon", + "Paxton", + "Paymar", + "Payne", + "Paynter", + "Payson", + "Payton", + "Paz", + "Paza", + "Pazia", + "Pazice", + "Pazit", + "Peace", + "Peacock", + "Peadar", + "Peale", + "Pearce", + "Pearl", + "Pearla", + "Pearle", + "Pearline", + "Pearlman", + "Pearlstein", + "Pearman", + "Pears", + "Pearse", + "Pearson", + "Pease", + "Peatroy", + "Pebrook", + "Peck", + "Peckham", + "Pedaiah", + "Pedaias", + "Peddada", + "Peder", + "Pedersen", + "Pederson", + "Pedrick", + "Pedro", + "Pedrotti", + "Pedroza", + "Peer", + "Peers", + "Peery", + "Peg", + "Pega", + "Pegasus", + "Pegeen", + "Pegg", + "Peggi", + "Peggie", + "Peggir", + "Peggy", + "Pegma", + "Peh", + "Peirce", + "Peirsen", + "Peisch", + "Pejsach", + "Pelag", + "Pelaga", + "Pelage", + "Pelagi", + "Pelagia", + "Pelagias", + "Pell", + "Pellegrini", + "Pellet", + "Pelletier", + "Pelligrini", + "Pellikka", + "Pelmas", + "Pelpel", + "Pelson", + "Peltier", + "Peltz", + "Pember", + "Pembroke", + "Pembrook", + "Pen", + "Pena", + "Pence", + "Pendergast", + "Pendleton", + "Penelopa", + "Penelope", + "Pengelly", + "Penhall", + "Penland", + "Penman", + "Penn", + "Pennebaker", + "Penney", + "Penni", + "Pennie", + "Pennington", + "Penny", + "Penoyer", + "Penrod", + "Penrose", + "Pentha", + "Penthea", + "Pentheam", + "Pentheas", + "Peonir", + "Peony", + "Peoples", + "Pepe", + "Peper", + "Pepi", + "Pepillo", + "Pepin", + "Pepita", + "Pepito", + "Peppard", + "Peppel", + "Pepper", + "Peppi", + "Peppie", + "Peppy", + "Per", + "Perce", + "Perceval", + "Percival", + "Percy", + "Perdita", + "Peregrine", + "Pergrim", + "Peri", + "Peria", + "Perice", + "Perkin", + "Perkins", + "Perkoff", + "Perl", + "Perla", + "Perle", + "Perlie", + "Perlis", + "Perlman", + "Perloff", + "Pernas", + "Pernell", + "Perni", + "Pernick", + "Pero", + "Perot", + "Perpetua", + "Perr", + "Perreault", + "Perren", + "Perretta", + "Perri", + "Perrie", + "Perrin", + "Perrine", + "Perrins", + "Perron", + "Perry", + "Persas", + "Perseus", + "Persian", + "Persis", + "Persons", + "Persse", + "Persson", + "Perusse", + "Perzan", + "Pesek", + "Peskoff", + "Pessa", + "Pestana", + "Pet", + "Peta", + "Pete", + "Peter", + "Peterec", + "Peterman", + "Peters", + "Petersen", + "Peterson", + "Peterus", + "Petes", + "Petey", + "Peti", + "Petie", + "Petigny", + "Petit", + "Petite", + "Petr", + "Petra", + "Petracca", + "Petras", + "Petrick", + "Petrie", + "Petrina", + "Petrine", + "Petromilli", + "Petronella", + "Petronia", + "Petronilla", + "Petronille", + "Petta", + "Pettifer", + "Pettiford", + "Pettit", + "Petty", + "Petua", + "Petula", + "Petulah", + "Petulia", + "Petunia", + "Petuu", + "Peugia", + "Peursem", + "Pevzner", + "Peyter", + "Peyton", + "Pfaff", + "Pfeffer", + "Pfeifer", + "Pfister", + "Pfosi", + "Phaedra", + "Phaidra", + "Phaih", + "Phail", + "Phalan", + "Pharaoh", + "Phare", + "Phares", + "Phebe", + "Phedra", + "Phelan", + "Phelgen", + "Phelgon", + "Phelia", + "Phelips", + "Phelps", + "Phemia", + "Phene", + "Pheni", + "Phenica", + "Phenice", + "Phi", + "Phia", + "Phil", + "Phila", + "Philan", + "Philana", + "Philander", + "Philbert", + "Philbin", + "Philbo", + "Philbrook", + "Philcox", + "Philemol", + "Philemon", + "Philender", + "Philina", + "Philine", + "Philip", + "Philipa", + "Philipines", + "Philipp", + "Philippa", + "Philippe", + "Philippine", + "Philipps", + "Philips", + "Philipson", + "Philis", + "Phillada", + "Phillane", + "Phillida", + "Phillie", + "Phillip", + "Phillipe", + "Phillipp", + "Phillips", + "Phillis", + "Philly", + "Philo", + "Philomena", + "Philoo", + "Philpot", + "Philps", + "Phina", + "Phineas", + "Phio", + "Phiona", + "Phionna", + "Phip", + "Phippen", + "Phipps", + "Phira", + "Phoebe", + "Phonsa", + "Photima", + "Photina", + "Phox", + "Phyl", + "Phylis", + "Phyllida", + "Phyllis", + "Phyllys", + "Phylys", + "Pia", + "Piane", + "Picardi", + "Picco", + "Pich", + "Pickar", + "Pickard", + "Pickens", + "Picker", + "Pickering", + "Pickett", + "Pickford", + "Piderit", + "Piefer", + "Piegari", + "Pier", + "Pierce", + "Pierette", + "Piero", + "Pierpont", + "Pierre", + "Pierrepont", + "Pierrette", + "Pierro", + "Piers", + "Pierson", + "Pieter", + "Pietje", + "Pietra", + "Pietrek", + "Pietro", + "Pigeon", + "Piggy", + "Pike", + "Pilar", + "Pilloff", + "Pillow", + "Pillsbury", + "Pimbley", + "Pincas", + "Pinchas", + "Pincince", + "Pinckney", + "Pincus", + "Pine", + "Pinebrook", + "Pineda", + "Pinelli", + "Pinette", + "Ping", + "Pinkerton", + "Pinkham", + "Pinsky", + "Pinter", + "Pinto", + "Pinzler", + "Piotr", + "Pip", + "Piper", + "Pippa", + "Pippas", + "Pippo", + "Pippy", + "Pirali", + "Pirbhai", + "Pirnot", + "Pironi", + "Pirozzo", + "Pirri", + "Pirzada", + "Pisano", + "Pisarik", + "Piscatelli", + "Piselli", + "Pish", + "Pitarys", + "Pitchford", + "Pitt", + "Pittel", + "Pittman", + "Pitts", + "Pitzer", + "Pius", + "Piwowar", + "Pizor", + "Placeeda", + "Placia", + "Placida", + "Placidia", + "Placido", + "Plafker", + "Plank", + "Plantagenet", + "Plante", + "Platas", + "Plate", + "Plath", + "Plato", + "Platon", + "Platt", + "Platto", + "Platus", + "Player", + "Pleasant", + "Pleione", + "Plerre", + "Pliam", + "Pliner", + "Pliske", + "Ploch", + "Ploss", + "Plossl", + "Plotkin", + "Plumbo", + "Plume", + "Plunkett", + "Plusch", + "Podvin", + "Pogue", + "Poirer", + "Pokorny", + "Pol", + "Polad", + "Polak", + "Poland", + "Polard", + "Polash", + "Poler", + "Poliard", + "Polik", + "Polinski", + "Polish", + "Politi", + "Polito", + "Polivy", + "Polk", + "Polky", + "Poll", + "Pollack", + "Pollak", + "Pollard", + "Pollerd", + "Pollie", + "Pollitt", + "Polloch", + "Pollock", + "Pollux", + "Polly", + "Pollyanna", + "Pomcroy", + "Pomeroy", + "Pomfret", + "Pomfrey", + "Pomona", + "Pompea", + "Pompei", + "Ponce", + "Pond", + "Pontias", + "Pontius", + "Ponton", + "Pontone", + "Pontus", + "Ponzo", + "Poock", + "Pooh", + "Pooi", + "Pool", + "Poole", + "Pooley", + "Poore", + "Pope", + "Popele", + "Popelka", + "Poppas", + "Popper", + "Poppo", + "Poppy", + "Porche", + "Porcia", + "Poree", + "Porett", + "Port", + "Porta", + "Porte", + "Porter", + "Portia", + "Portie", + "Portingale", + "Portland", + "Portugal", + "Portuna", + "Portwin", + "Portwine", + "Porty", + "Porush", + "Posehn", + "Posner", + "Possing", + "Post", + "Postman", + "Potash", + "Potter", + "Potts", + "Poucher", + "Poul", + "Poulter", + "Pouncey", + "Pournaras", + "Powder", + "Powe", + "Powel", + "Powell", + "Power", + "Powers", + "Pownall", + "Poyssick", + "Pozzy", + "Pradeep", + "Prader", + "Prady", + "Prager", + "Prakash", + "Prasad", + "Pratt", + "Pratte", + "Pravit", + "Prebo", + "Preciosa", + "Preiser", + "Prem", + "Premer", + "Pren", + "Prendergast", + "Prent", + "Prentice", + "Prentiss", + "Presber", + "Prescott", + "Presley", + "Press", + "Pressey", + "Pressman", + "Prestige", + "Preston", + "Pretrice", + "Preuss", + "Previdi", + "Prevot", + "Price", + "Prichard", + "Pricilla", + "Pride", + "Priebe", + "Priest", + "Priestley", + "Prima", + "Primalia", + "Primavera", + "Primaveras", + "Primaveria", + "Primo", + "Primrosa", + "Primrose", + "Prince", + "Princess", + "Prinz", + "Prior", + "Pris", + "Prisca", + "Priscella", + "Priscilla", + "Prisilla", + "Prissie", + "Prissy", + "Pritchard", + "Pritchett", + "Prober", + "Prochora", + "Prochoras", + "Procora", + "Procter", + "Procto", + "Proctor", + "Profant", + "Proffitt", + "Pronty", + "Pros", + "Prosper", + "Prospero", + "Prosperus", + "Prosser", + "Proud", + "Proudfoot", + "Proudlove", + "Proudman", + "Proulx", + "Prouty", + "Prowel", + "Pru", + "Pruchno", + "Prud", + "Prudence", + "Prudhoe", + "Prudi", + "Prudie", + "Prudy", + "Prue", + "Prunella", + "Prussian", + "Pruter", + "Pry", + "Pryce", + "Pryor", + "Psyche", + "Pubilis", + "Publea", + "Publia", + "Publias", + "Publius", + "Publus", + "Pucida", + "Pudendas", + "Pudens", + "Puduns", + "Puett", + "Pufahl", + "Puff", + "Pugh", + "Puglia", + "Puiia", + "Puklich", + "Pul", + "Pulcheria", + "Pulchi", + "Pulchia", + "Pulling", + "Pulsifer", + "Pump", + "Punak", + "Punke", + "Purcell", + "Purdum", + "Purdy", + "Puri", + "Purington", + "Puritan", + "Purity", + "Purpura", + "Purse", + "Purvis", + "Putnam", + "Putnem", + "Puto", + "Putscher", + "Puttergill", + "Py", + "Pyle", + "Pylle", + "Pyne", + "Pyotr", + "Pyszka", + "Pytlik", + "Quackenbush", + "Quar", + "Quarta", + "Quartana", + "Quartas", + "Quartet", + "Quartis", + "Quartus", + "Queen", + "Queena", + "Queenie", + "Quenby", + "Quenna", + "Quennie", + "Quent", + "Quentin", + "Queri", + "Querida", + "Queridas", + "Questa", + "Queston", + "Quick", + "Quickel", + "Quickman", + "Quigley", + "Quill", + "Quillan", + "Quillon", + "Quin", + "Quinby", + "Quince", + "Quincey", + "Quincy", + "Quinlan", + "Quinn", + "Quint", + "Quinta", + "Quintana", + "Quintessa", + "Quintie", + "Quintilla", + "Quintin", + "Quintina", + "Quinton", + "Quintus", + "Quirita", + "Quirk", + "Quita", + "Quiteri", + "Quiteria", + "Quiteris", + "Quitt", + "Qulllon", + "Raab", + "Raama", + "Raasch", + "Rab", + "Rabah", + "Rabassa", + "Rabbi", + "Rabelais", + "Rabi", + "Rabiah", + "Rabin", + "Rabjohn", + "Rabkin", + "Rabush", + "Race", + "Rachaba", + "Rachael", + "Rachel", + "Rachele", + "Rachelle", + "Racklin", + "Rad", + "Radack", + "Radborne", + "Radbourne", + "Radbun", + "Radburn", + "Radcliffe", + "Raddatz", + "Raddi", + "Raddie", + "Raddy", + "Radferd", + "Radford", + "Radie", + "Radke", + "Radley", + "Radloff", + "Radman", + "Radmen", + "Radmilla", + "Radu", + "Rae", + "Raeann", + "Raf", + "Rafa", + "Rafael", + "Rafaela", + "Rafaelia", + "Rafaelita", + "Rafaelle", + "Rafaellle", + "Rafaello", + "Rafaelof", + "Rafat", + "Rafe", + "Raff", + "Raffaello", + "Raffarty", + "Rafferty", + "Raffin", + "Raffo", + "Rafi", + "Rafiq", + "Rafter", + "Ragan", + "Ragen", + "Ragg", + "Ragland", + "Ragnar", + "Ragouzis", + "Ragucci", + "Rahal", + "Rahel", + "Rahm", + "Rahman", + "Rahmann", + "Rahr", + "Rai", + "Raila", + "Raimes", + "Raimondo", + "Raimund", + "Raimundo", + "Raina", + "Rainah", + "Raine", + "Rainer", + "Raines", + "Rainger", + "Rainie", + "Rains", + "Rainwater", + "Rajewski", + "Raji", + "Rajiv", + "Rakel", + "Rakia", + "Ralaigh", + "Raleigh", + "Ralf", + "Ralfston", + "Ralina", + "Ralleigh", + "Ralli", + "Ralph", + "Ralston", + "Ram", + "Rama", + "Ramah", + "Raman", + "Ramberg", + "Rambert", + "Rambort", + "Rambow", + "Ramburt", + "Rame", + "Ramey", + "Ramiah", + "Ramin", + "Ramon", + "Ramona", + "Ramonda", + "Ramos", + "Ramsay", + "Ramsdell", + "Ramsden", + "Ramses", + "Ramsey", + "Ramunni", + "Ran", + "Rana", + "Rance", + "Rancell", + "Ranchod", + "Rand", + "Randa", + "Randal", + "Randall", + "Randee", + "Randell", + "Randene", + "Randi", + "Randie", + "Randolf", + "Randolph", + "Randy", + "Ranee", + "Raney", + "Range", + "Rangel", + "Ranger", + "Rani", + "Rania", + "Ranice", + "Ranie", + "Ranique", + "Ranit", + "Ranita", + "Ranite", + "Ranitta", + "Ranjiv", + "Rankin", + "Rann", + "Ranna", + "Ransell", + "Ransom", + "Ransome", + "Ranson", + "Ranzini", + "Rao", + "Raouf", + "Raoul", + "Rap", + "Rape", + "Raphael", + "Raphaela", + "Rapp", + "Raquel", + "Raquela", + "Ras", + "Raseda", + "Raseta", + "Rashida", + "Rashidi", + "Rasia", + "Rask", + "Raskin", + "Raskind", + "Rasla", + "Rasmussen", + "Rastus", + "Rasure", + "Ratcliff", + "Ratcliffe", + "Ratha", + "Rather", + "Ratib", + "Rattan", + "Rattray", + "Rauch", + "Raul", + "Rausch", + "Rauscher", + "Raveaux", + "Raven", + "Ravens", + "Ravi", + "Ravid", + "Raviv", + "Ravo", + "Rawdan", + "Rawden", + "Rawdin", + "Rawdon", + "Rawley", + "Rawlinson", + "Ray", + "Raybin", + "Raybourne", + "Rayburn", + "Raychel", + "Raycher", + "Raye", + "Rayford", + "Rayle", + "Raymond", + "Raymonds", + "Raymund", + "Rayna", + "Raynah", + "Raynard", + "Raynata", + "Raynell", + "Rayner", + "Raynold", + "Raynor", + "Rayshell", + "Razid", + "Rea", + "Reace", + "Read", + "Reade", + "Readus", + "Ready", + "Reagan", + "Reagen", + "Reahard", + "Reames", + "Reamonn", + "Reamy", + "Reave", + "Reba", + "Rebah", + "Rebak", + "Rebane", + "Rebba", + "Rebbecca", + "Rebe", + "Rebeca", + "Rebecca", + "Rebecka", + "Rebeka", + "Rebekah", + "Rebekkah", + "Rebel", + "Rebhun", + "Rech", + "Recha", + "Rechaba", + "Reckford", + "Recor", + "Rector", + "Red", + "Redd", + "Reddin", + "Reddy", + "Redfield", + "Redford", + "Redman", + "Redmer", + "Redmond", + "Redmund", + "Redvers", + "Redwine", + "Ree", + "Reeba", + "Reece", + "Reed", + "Reede", + "Reedy", + "Reeher", + "Reel", + "Reena", + "Rees", + "Reese", + "Reeta", + "Reeva", + "Reeve", + "Reeves", + "Reg", + "Regan", + "Regazzi", + "Regen", + "Reger", + "Reggi", + "Reggie", + "Reggis", + "Reggy", + "Regina", + "Reginald", + "Reginauld", + "Regine", + "Rego", + "Rehm", + "Rehnberg", + "Reich", + "Reiche", + "Reichel", + "Reichert", + "Reid", + "Reidar", + "Reider", + "Reifel", + "Reiko", + "Reilly", + "Reimer", + "Rein", + "Reina", + "Reinald", + "Reinaldo", + "Reinaldos", + "Reine", + "Reiner", + "Reiners", + "Reinert", + "Reinertson", + "Reinhard", + "Reinhardt", + "Reinhart", + "Reinhold", + "Reinke", + "Reinold", + "Reinwald", + "Reis", + "Reisch", + "Reiser", + "Reisfield", + "Reisinger", + "Reisman", + "Reiss", + "Reiter", + "Reitman", + "Reld", + "Rella", + "Rellia", + "Relly", + "Rem", + "Rema", + "Remde", + "Remington", + "Remmer", + "Rempe", + "Remsen", + "Remus", + "Remy", + "Rena", + "Renado", + "Renae", + "Renaldo", + "Renard", + "Renata", + "Renate", + "Renato", + "Renaud", + "Renault", + "Renckens", + "Rene", + "Renee", + "Renell", + "Renelle", + "Reneta", + "Renferd", + "Renfred", + "Reni", + "Renick", + "Renie", + "Renita", + "Reniti", + "Rennane", + "Renner", + "Rennie", + "Rennold", + "Renny", + "Rento", + "Rentsch", + "Rentschler", + "Renwick", + "Renzo", + "Reo", + "Resa", + "Rese", + "Reseda", + "Resee", + "Reseta", + "Resor", + "Ress", + "Ressler", + "Reste", + "Restivo", + "Reta", + "Retha", + "Rett", + "Rettig", + "Rettke", + "Reube", + "Reuben", + "Reuven", + "Revell", + "Reviel", + "Reviere", + "Revkah", + "Rew", + "Rex", + "Rexana", + "Rexanna", + "Rexanne", + "Rexer", + "Rexferd", + "Rexford", + "Rexfourd", + "Rey", + "Reyna", + "Reynard", + "Reynold", + "Reynolds", + "Rezzani", + "Rhea", + "Rheba", + "Rhee", + "Rheims", + "Rheingold", + "Rheinlander", + "Rheta", + "Rhett", + "Rhetta", + "Rhiamon", + "Rhiana", + "Rhianna", + "Rhianon", + "Rhine", + "Rhines", + "Rhoades", + "Rhoads", + "Rhoda", + "Rhodes", + "Rhodia", + "Rhodie", + "Rhody", + "Rhona", + "Rhonda", + "Rhu", + "Rhynd", + "Rhyne", + "Rhyner", + "Rhys", + "Ri", + "Ria", + "Riana", + "Riancho", + "Riane", + "Rianna", + "Riannon", + "Rianon", + "Riba", + "Ribal", + "Ribaudo", + "Ribble", + "Ric", + "Rica", + "Ricard", + "Ricarda", + "Ricardama", + "Ricardo", + "Ricca", + "Riccardo", + "Riccio", + "Rice", + "Rich", + "Richara", + "Richard", + "Richarda", + "Richardo", + "Richards", + "Richardson", + "Richart", + "Richel", + "Richela", + "Richella", + "Richelle", + "Richer", + "Richers", + "Richey", + "Richia", + "Richie", + "Richlad", + "Richma", + "Richmal", + "Richman", + "Richmond", + "Richmound", + "Richter", + "Richy", + "Rici", + "Rick", + "Rickard", + "Rickart", + "Ricker", + "Rickert", + "Ricketts", + "Rickey", + "Ricki", + "Rickie", + "Ricky", + "Rico", + "Ricoriki", + "Rida", + "Riddle", + "Rider", + "Ridglea", + "Ridglee", + "Ridgley", + "Ridinger", + "Ridley", + "Rie", + "Riebling", + "Riedel", + "Riegel", + "Rieger", + "Riehl", + "Riella", + "Ries", + "Riesman", + "Riess", + "Rieth", + "Riffle", + "Rifkin", + "Rigby", + "Rigdon", + "Riggall", + "Riggins", + "Riggs", + "Riha", + "Rihana", + "Rik", + "Rika", + "Riker", + "Riki", + "Rikki", + "Rilda", + "Riley", + "Rillings", + "Rillis", + "Rima", + "Rimas", + "Rimma", + "Rimola", + "Rina", + "Rinaldo", + "Rind", + "Rinee", + "Ring", + "Ringe", + "Ringler", + "Ringo", + "Ringsmuth", + "Rinna", + "Rintoul", + "Riobard", + "Riocard", + "Rior", + "Riordan", + "Riorsson", + "Rip", + "Ripleigh", + "Riplex", + "Ripley", + "Ripp", + "Risa", + "Rise", + "Risley", + "Rissa", + "Risser", + "Rist", + "Risteau", + "Rita", + "Ritch", + "Ritchie", + "Riti", + "Ritter", + "Ritz", + "Riva", + "Rivalee", + "Rivard", + "River", + "Rivera", + "Rivers", + "Rives", + "Rivi", + "Rivkah", + "Rivy", + "Rizas", + "Rizika", + "Rizzi", + "Rizzo", + "Ro", + "Roach", + "Roana", + "Roane", + "Roanna", + "Roanne", + "Roarke", + "Roath", + "Rob", + "Robaina", + "Robb", + "Robbert", + "Robbi", + "Robbie", + "Robbin", + "Robbins", + "Robby", + "Robbyn", + "Robena", + "Robenia", + "Robers", + "Roberson", + "Robert", + "Roberta", + "Roberto", + "Roberts", + "Robertson", + "Robet", + "Robi", + "Robillard", + "Robin", + "Robina", + "Robinet", + "Robinett", + "Robinetta", + "Robinette", + "Robinia", + "Robins", + "Robinson", + "Robison", + "Robson", + "Roby", + "Robyn", + "Rocca", + "Rocco", + "Roch", + "Roche", + "Rochell", + "Rochella", + "Rochelle", + "Rochemont", + "Rocher", + "Rochester", + "Rochette", + "Rochkind", + "Rochus", + "Rock", + "Rockafellow", + "Rockefeller", + "Rockel", + "Rocker", + "Rockey", + "Rockie", + "Rockwell", + "Rockwood", + "Rocky", + "Rocray", + "Rod", + "Roda", + "Rodd", + "Roddie", + "Roddy", + "Rodenhouse", + "Roderic", + "Roderica", + "Roderich", + "Roderick", + "Roderigo", + "Rodge", + "Rodger", + "Rodgers", + "Rodi", + "Rodie", + "Rodina", + "Rodl", + "Rodman", + "Rodmann", + "Rodmun", + "Rodmur", + "Rodney", + "Rodolfo", + "Rodolph", + "Rodolphe", + "Rodrich", + "Rodrick", + "Rodrigo", + "Rodriguez", + "Rodrique", + "Roe", + "Roede", + "Roee", + "Roehm", + "Roer", + "Roeser", + "Rog", + "Roger", + "Rogerio", + "Rogers", + "Rogerson", + "Rogovy", + "Rogozen", + "Rohn", + "Roi", + "Roice", + "Roid", + "Rois", + "Rojas", + "Rokach", + "Rola", + "Rolan", + "Roland", + "Rolanda", + "Rolando", + "Rolandson", + "Roldan", + "Roley", + "Rolf", + "Rolfe", + "Rolfston", + "Rolland", + "Rollet", + "Rollie", + "Rollin", + "Rollins", + "Rollo", + "Rolo", + "Rolph", + "Roma", + "Romain", + "Romaine", + "Romalda", + "Roman", + "Romanas", + "Romano", + "Rombert", + "Rome", + "Romelda", + "Romelle", + "Romeo", + "Romeon", + "Romeu", + "Romeyn", + "Romie", + "Romilda", + "Romilly", + "Romina", + "Romine", + "Romito", + "Romney", + "Romo", + "Romola", + "Romona", + "Romonda", + "Romulus", + "Romy", + "Ron", + "Rona", + "Ronal", + "Ronald", + "Ronalda", + "Ronda", + "Rondi", + "Rondon", + "Ronel", + "Ronen", + "Ronica", + "Ronn", + "Ronna", + "Ronnholm", + "Ronni", + "Ronnica", + "Ronnie", + "Ronny", + "Roobbie", + "Rooke", + "Rooker", + "Rooney", + "Roos", + "Roose", + "Roosevelt", + "Root", + "Roots", + "Roper", + "Roque", + "Rora", + "Rori", + "Rorie", + "Rorke", + "Rorry", + "Rorrys", + "Rory", + "Ros", + "Rosa", + "Rosabel", + "Rosabella", + "Rosabelle", + "Rosalba", + "Rosalee", + "Rosaleen", + "Rosalia", + "Rosalie", + "Rosalind", + "Rosalinda", + "Rosalinde", + "Rosaline", + "Rosalyn", + "Rosalynd", + "Rosamond", + "Rosamund", + "Rosana", + "Rosane", + "Rosanna", + "Rosanne", + "Rosario", + "Rosati", + "Rosco", + "Roscoe", + "Rose", + "Roseann", + "Roseanna", + "Roseanne", + "Rosecan", + "Rosel", + "Roselane", + "Roselani", + "Roselba", + "Roselia", + "Roselin", + "Roseline", + "Rosella", + "Roselle", + "Roselyn", + "Rosemare", + "Rosemari", + "Rosemaria", + "Rosemarie", + "Rosemary", + "Rosemonde", + "Rosen", + "Rosena", + "Rosenbaum", + "Rosenberg", + "Rosenberger", + "Rosenblast", + "Rosenblatt", + "Rosenblum", + "Rosene", + "Rosenfeld", + "Rosenkrantz", + "Rosenkranz", + "Rosenquist", + "Rosenstein", + "Rosenthal", + "Rosenwald", + "Rosenzweig", + "Rosetta", + "Rosette", + "Roshan", + "Roshelle", + "Rosie", + "Rosina", + "Rosinski", + "Rosio", + "Rosita", + "Roskes", + "Roslyn", + "Rosmarin", + "Rosmunda", + "Rosner", + "Rosol", + "Ross", + "Rosse", + "Rossen", + "Rossi", + "Rossie", + "Rossing", + "Rossner", + "Rossuck", + "Rossy", + "Rostand", + "Roswald", + "Roswell", + "Rosy", + "Rotberg", + "Roter", + "Roth", + "Rothberg", + "Rothenberg", + "Rother", + "Rothmuller", + "Rothschild", + "Rothstein", + "Rothwell", + "Roti", + "Rotman", + "Rotow", + "Roumell", + "Rourke", + "Routh", + "Rouvin", + "Roux", + "Rovelli", + "Rovit", + "Rovner", + "Row", + "Rowan", + "Rowe", + "Rowell", + "Rowen", + "Rowena", + "Rowland", + "Rowley", + "Rowney", + "Rox", + "Roxana", + "Roxane", + "Roxanna", + "Roxanne", + "Roxi", + "Roxie", + "Roxine", + "Roxy", + "Roy", + "Royal", + "Royall", + "Roybn", + "Royce", + "Royd", + "Roydd", + "Royden", + "Roye", + "Royo", + "Roz", + "Rozalie", + "Rozalin", + "Rozamond", + "Rozanna", + "Rozanne", + "Roze", + "Rozek", + "Rozele", + "Rozella", + "Rozelle", + "Rozina", + "Rriocard", + "Ru", + "Rubbico", + "Rube", + "Rubel", + "Ruben", + "Rubens", + "Rubenstein", + "Ruberta", + "Rubetta", + "Rubi", + "Rubia", + "Rubie", + "Rubin", + "Rubina", + "Rubinstein", + "Rubio", + "Ruby", + "Rucker", + "Ruckman", + "Rudd", + "Ruddie", + "Ruddy", + "Rudelson", + "Ruder", + "Rudich", + "Rudie", + "Rudiger", + "Rudin", + "Rudman", + "Rudolf", + "Rudolfo", + "Rudolph", + "Rudwik", + "Rudy", + "Rudyard", + "Rue", + "Ruel", + "Ruella", + "Ruelle", + "Ruelu", + "Rufe", + "Rufena", + "Ruff", + "Ruffi", + "Ruffin", + "Ruffina", + "Ruffo", + "Rufford", + "Rufina", + "Ruford", + "Rufus", + "Rugen", + "Rugg", + "Ruggiero", + "Ruhl", + "Ruhnke", + "Ruiz", + "Rumery", + "Rumilly", + "Rumney", + "Rumpf", + "Runck", + "Rundgren", + "Runkel", + "Runkle", + "Runstadler", + "Rupert", + "Ruperta", + "Ruperto", + "Ruphina", + "Ruprecht", + "Rurik", + "Rus", + "Ruscher", + "Ruscio", + "Rusel", + "Rusell", + "Rusert", + "Rush", + "Rushing", + "Ruskin", + "Russ", + "Russel", + "Russell", + "Russi", + "Russia", + "Russian", + "Russo", + "Russom", + "Russon", + "Rust", + "Rustice", + "Rusticus", + "Rustie", + "Rustin", + "Rusty", + "Rutan", + "Rutger", + "Ruth", + "Ruthann", + "Ruthanne", + "Ruthe", + "Rutherford", + "Rutherfurd", + "Ruthi", + "Ruthie", + "Ruthven", + "Ruthy", + "Rutledge", + "Rutter", + "Ruttger", + "Ruvolo", + "Ruy", + "Ruyle", + "Ruzich", + "Ryan", + "Ryann", + "Rycca", + "Rydder", + "Ryder", + "Rye", + "Ryle", + "Ryley", + "Ryon", + "Rysler", + "Ryter", + "Ryun", + "Saba", + "Sabah", + "Sabba", + "Sabec", + "Sabella", + "Sabelle", + "Saber", + "Saberhagen", + "Saberio", + "Sabian", + "Sabina", + "Sabine", + "Sabino", + "Sabir", + "Sabra", + "Sabrina", + "Sabsay", + "Sabu", + "Sacci", + "Sacha", + "Sachi", + "Sachiko", + "Sachs", + "Sachsse", + "Sacken", + "Sackey", + "Sackman", + "Sacks", + "Sacksen", + "Sackville", + "Sacttler", + "Sad", + "Sada", + "Saddler", + "Sadella", + "Sadick", + "Sadie", + "Sadira", + "Sadirah", + "Sadiras", + "Sadler", + "Sadoc", + "Sadoff", + "Sadonia", + "Sadowski", + "Sadye", + "Saeger", + "Saffian", + "Saffier", + "Saffren", + "Safier", + "Safir", + "Safire", + "Safko", + "Sage", + "Sager", + "Sagerman", + "Saidee", + "Saidel", + "Saideman", + "Saied", + "Saiff", + "Sailesh", + "Saimon", + "Saint", + "Sair", + "Saire", + "Saito", + "Sajovich", + "Sakhuja", + "Sakmar", + "Sakovich", + "Saks", + "Sal", + "Salahi", + "Salaidh", + "Salamanca", + "Salamone", + "Salangi", + "Salangia", + "Salas", + "Salazar", + "Salba", + "Salbu", + "Salchunas", + "Sale", + "Saleem", + "Salem", + "Salema", + "Saleme", + "Salena", + "Salene", + "Salesin", + "Salim", + "Salina", + "Salinas", + "Salisbarry", + "Salisbury", + "Salita", + "Sall", + "Sallee", + "Salli", + "Sallie", + "Sally", + "Sallyann", + "Sallyanne", + "Salman", + "Salmon", + "Saloma", + "Salome", + "Salomi", + "Salomie", + "Salomo", + "Salomon", + "Salomone", + "Salot", + "Salsbury", + "Salter", + "Saltsman", + "Saltzman", + "Salvador", + "Salvadore", + "Salvatore", + "Salvay", + "Salvidor", + "Salvucci", + "Salzhauer", + "Sam", + "Sama", + "Samal", + "Samala", + "Samale", + "Samalla", + "Samantha", + "Samanthia", + "Samara", + "Samaria", + "Samau", + "Samella", + "Samford", + "Sami", + "Samira", + "Sammer", + "Sammie", + "Sammons", + "Sammy", + "Samp", + "Sampson", + "Sams", + "Samson", + "Samuel", + "Samuela", + "Samuele", + "Samuella", + "Samuelson", + "Samul", + "Samy", + "Sanalda", + "Sanbo", + "Sanborn", + "Sanborne", + "Sanburn", + "Sancha", + "Sanchez", + "Sancho", + "Sand", + "Sandberg", + "Sande", + "Sandeep", + "Sandell", + "Sander", + "Sanders", + "Sanderson", + "Sandi", + "Sandie", + "Sandler", + "Sandon", + "Sandor", + "Sandra", + "Sandro", + "Sandry", + "Sands", + "Sandstrom", + "Sandy", + "Sandye", + "Sanferd", + "Sanfo", + "Sanford", + "Sanfourd", + "Sanfred", + "Sang", + "Sanger", + "Sanjay", + "Sanjiv", + "Sankaran", + "Sankey", + "Sansbury", + "Sansen", + "Sanson", + "Sansone", + "Santa", + "Santana", + "Santiago", + "Santini", + "Santoro", + "Santos", + "Sanyu", + "Sapers", + "Saphra", + "Sapienza", + "Sapowith", + "Sapphera", + "Sapphira", + "Sapphire", + "Sara", + "Sara-Ann", + "Saraann", + "Sarad", + "Sarah", + "Saraiya", + "Sarajane", + "Sarazen", + "Sarchet", + "Sardella", + "Saree", + "Sarena", + "Sarene", + "Saretta", + "Sarette", + "Sarge", + "Sargent", + "Sari", + "Sarid", + "Sarilda", + "Sarina", + "Sarine", + "Sarita", + "Sarkaria", + "Sarnoff", + "Sarson", + "Sartin", + "Sascha", + "Sasha", + "Sashenka", + "Sasnett", + "Sass", + "Sassan", + "Sateia", + "Sathrum", + "Sato", + "Satterfield", + "Satterlee", + "Saturday", + "Saucy", + "Sauder", + "Saudra", + "Sauer", + "Sauers", + "Saul", + "Sauls", + "Saum", + "Sauncho", + "Saunder", + "Saunders", + "Saunderson", + "Saundra", + "Sausa", + "Sauveur", + "Savadove", + "Savage", + "Saval", + "Savanna", + "Savannah", + "Savdeep", + "Savell", + "Savick", + "Savil", + "Savill", + "Saville", + "Savina", + "Savior", + "Savitt", + "Savory", + "Saw", + "Sawtelle", + "Sawyer", + "Sawyere", + "Sawyor", + "Sax", + "Saxe", + "Saxen", + "Saxena", + "Saxon", + "Say", + "Sayce", + "Sayed", + "Sayer", + "Sayers", + "Sayette", + "Sayles", + "Saylor", + "Sayre", + "Sayres", + "Scales", + "Scammon", + "Scandura", + "Scarface", + "Scarito", + "Scarlet", + "Scarlett", + "Scarrow", + "Scever", + "Scevo", + "Scevor", + "Scevour", + "Schaab", + "Schaaff", + "Schach", + "Schacker", + "Schaefer", + "Schaeffer", + "Schafer", + "Schaffel", + "Schaffer", + "Schalles", + "Schaper", + "Schapira", + "Scharaga", + "Scharf", + "Scharff", + "Schargel", + "Schatz", + "Schaumberger", + "Schear", + "Schechinger", + "Schechter", + "Scheck", + "Schecter", + "Scheer", + "Scheers", + "Scheider", + "Scheld", + "Schell", + "Schellens", + "Schenck", + "Scherle", + "Scherman", + "Schertz", + "Schick", + "Schiff", + "Schiffman", + "Schifra", + "Schild", + "Schilit", + "Schilling", + "Schilt", + "Schindler", + "Schinica", + "Schiro", + "Schlenger", + "Schlesinger", + "Schlessel", + "Schlessinger", + "Schlicher", + "Schlosser", + "Schluter", + "Schmeltzer", + "Schmidt", + "Schmitt", + "Schmitz", + "Schnabel", + "Schnapp", + "Schnell", + "Schnorr", + "Schnur", + "Schnurr", + "Schober", + "Schoenberg", + "Schoenburg", + "Schoenfelder", + "Schoening", + "Schofield", + "Scholem", + "Scholz", + "Schonfeld", + "Schonfield", + "Schonthal", + "Schoof", + "Schott", + "Schou", + "Schouten", + "Schrader", + "Schram", + "Schramke", + "Schreck", + "Schreib", + "Schreibe", + "Schreiber", + "Schreibman", + "Schrick", + "Schriever", + "Schroder", + "Schroeder", + "Schroer", + "Schroth", + "Schubert", + "Schug", + "Schuh", + "Schulein", + "Schuler", + "Schulman", + "Schultz", + "Schulz", + "Schulze", + "Schuman", + "Schumer", + "Schurman", + "Schuster", + "Schuyler", + "Schwab", + "Schwartz", + "Schwarz", + "Schweiker", + "Schweitzer", + "Schwejda", + "Schwenk", + "Schwerin", + "Schwing", + "Schwinn", + "Schwitzer", + "Scibert", + "Sclar", + "Sclater", + "Scoles", + "Scopp", + "Scornik", + "Scot", + "Scoter", + "Scotney", + "Scott", + "Scotti", + "Scottie", + "Scotty", + "Scoville", + "Screens", + "Scribner", + "Scriven", + "Scrivenor", + "Scrivens", + "Scrivings", + "Scrogan", + "Scrope", + "Sculley", + "Scully", + "Scurlock", + "Scutt", + "Seabrook", + "Seabrooke", + "Seabury", + "Seaddon", + "Seaden", + "Seadon", + "Seafowl", + "Seagrave", + "Seagraves", + "Seale", + "Seaman", + "Seamus", + "Sean", + "Seana", + "Searby", + "Searcy", + "Searle", + "Sears", + "Season", + "Seaton", + "Seaver", + "Seavey", + "Seavir", + "Sebastian", + "Sebastiano", + "Sebastien", + "Sebbie", + "Secor", + "Secrest", + "Secunda", + "Secundas", + "Seda", + "Sedberry", + "Sedda", + "Sedgewake", + "Sedgewick", + "Sedgewinn", + "Sedlik", + "See", + "Seebeck", + "Seed", + "Seedman", + "Seel", + "Seely", + "Seem", + "Seema", + "Seen", + "Seena", + "Seessel", + "Seeto", + "Seften", + "Sefton", + "Seftton", + "Segal", + "Segalman", + "Seiber", + "Seibold", + "Seidel", + "Seiden", + "Seidler", + "Seidule", + "Seif", + "Seigel", + "Seigler", + "Seiter", + "Seitz", + "Seka", + "Seko", + "Sekofski", + "Sekyere", + "Sela", + "Selassie", + "Selby", + "Selda", + "Seldan", + "Selden", + "Seldon", + "Seldun", + "Selemas", + "Selena", + "Selene", + "Selestina", + "Seleta", + "Selfridge", + "Selhorst", + "Selia", + "Selie", + "Selig", + "Seligman", + "Seligmann", + "Selima", + "Selimah", + "Selina", + "Selinda", + "Seline", + "Selinski", + "Sell", + "Sella", + "Selle", + "Sellers", + "Sellma", + "Sello", + "Sells", + "Selma", + "Selmner", + "Selmore", + "Selry", + "Seltzer", + "Selway", + "Selwin", + "Selwyn", + "Semela", + "Semele", + "Semmes", + "Sena", + "Senalda", + "Sender", + "Senecal", + "Senhauser", + "Senior", + "Senn", + "Sension", + "Senskell", + "Senzer", + "Seow", + "Sephira", + "Seppala", + "September", + "Septima", + "Sera", + "Serafina", + "Serafine", + "Seraphim", + "Seraphina", + "Seraphine", + "Serena", + "Serene", + "Serg", + "Serge", + "Sergeant", + "Sergei", + "Sergent", + "Sergias", + "Sergio", + "Sergius", + "Sergo", + "Sergu", + "Serica", + "Serilda", + "Serle", + "Serles", + "Seroka", + "Serra", + "Serrano", + "Serrell", + "Servais", + "Server", + "Servetnick", + "Service", + "Sessler", + "Seta", + "Seth", + "Sethi", + "Sethrida", + "Seto", + "Seton", + "Settera", + "Settle", + "Seumas", + "Sev", + "Seve", + "Severen", + "Severin", + "Severn", + "Severson", + "Sevik", + "Seward", + "Sewel", + "Sewell", + "Sewellyn", + "Sewole", + "Sewoll", + "Sexton", + "Seyler", + "Seymour", + "Seys", + "Sezen", + "Shabbir", + "Shaddock", + "Shadow", + "Shae", + "Shaefer", + "Shaeffer", + "Shaer", + "Shafer", + "Shaff", + "Shaffer", + "Shaffert", + "Shah", + "Shaia", + "Shaikh", + "Shaina", + "Shaine", + "Shakespeare", + "Shakti", + "Shalna", + "Shalne", + "Shalom", + "Shama", + "Shamma", + "Shamrao", + "Shamus", + "Shana", + "Shanahan", + "Shanan", + "Shanda", + "Shandee", + "Shandeigh", + "Shandie", + "Shandra", + "Shandy", + "Shane", + "Shaner", + "Shani", + "Shanie", + "Shank", + "Shanks", + "Shanleigh", + "Shanley", + "Shanly", + "Shanna", + "Shannah", + "Shannan", + "Shannen", + "Shanney", + "Shannon", + "Shanon", + "Shanta", + "Shantee", + "Shantha", + "Shaper", + "Shapiro", + "Shara", + "Sharai", + "Shargel", + "Shari", + "Sharia", + "Sharity", + "Sharl", + "Sharla", + "Sharleen", + "Sharlene", + "Sharline", + "Sharma", + "Sharman", + "Sharon", + "Sharona", + "Sharos", + "Sharp", + "Sharpe", + "Sharron", + "Sharyl", + "Shatzer", + "Shaughn", + "Shaughnessy", + "Shaum", + "Shaun", + "Shauna", + "Shaver", + "Shaw", + "Shawn", + "Shawna", + "Shawnee", + "Shay", + "Shaya", + "Shayla", + "Shaylah", + "Shaylyn", + "Shaylynn", + "Shayn", + "Shayna", + "Shayne", + "Shea", + "Sheaff", + "Shear", + "Sheba", + "Shedd", + "Sheeb", + "Sheedy", + "Sheehan", + "Sheela", + "Sheelagh", + "Sheelah", + "Sheena", + "Sheepshanks", + "Sheeran", + "Sheeree", + "Sheets", + "Sheff", + "Sheffie", + "Sheffield", + "Sheffy", + "Sheila", + "Sheilah", + "Shel", + "Shela", + "Shelagh", + "Shelah", + "Shelba", + "Shelbi", + "Shelburne", + "Shelby", + "Shelden", + "Sheldon", + "Sheley", + "Shelia", + "Sheline", + "Shell", + "Shellans", + "Shelley", + "Shelli", + "Shellie", + "Shelly", + "Shelman", + "Shelton", + "Shem", + "Shena", + "Shenan", + "Sheng", + "Shep", + "Shepard", + "Shepherd", + "Shepley", + "Sheply", + "Shepp", + "Sheppard", + "Shepperd", + "Sher", + "Sherar", + "Sherard", + "Sherborn", + "Sherborne", + "Sherburn", + "Sherburne", + "Shere", + "Sheree", + "Sherer", + "Shererd", + "Sherfield", + "Sheri", + "Sheridan", + "Sherie", + "Sherill", + "Sherilyn", + "Sherj", + "Sherl", + "Sherline", + "Sherlock", + "Sherlocke", + "Sherm", + "Sherman", + "Shermie", + "Shermy", + "Sherourd", + "Sherr", + "Sherrard", + "Sherrer", + "Sherri", + "Sherrie", + "Sherrill", + "Sherris", + "Sherrod", + "Sherry", + "Sherurd", + "Sherwin", + "Sherwood", + "Sherwynd", + "Sherye", + "Sheryl", + "Sheryle", + "Shetrit", + "Shevlo", + "Shewchuk", + "Shewmaker", + "Sheya", + "Shiau", + "Shieh", + "Shiekh", + "Shields", + "Shien", + "Shiff", + "Shifra", + "Shifrah", + "Shig", + "Shih", + "Shiller", + "Shimberg", + "Shimkus", + "Shina", + "Shinberg", + "Shing", + "Shipley", + "Shipman", + "Shipp", + "Shippee", + "Shir", + "Shira", + "Shirah", + "Shirberg", + "Shiri", + "Shirk", + "Shirl", + "Shirlee", + "Shirleen", + "Shirlene", + "Shirley", + "Shirlie", + "Shirline", + "Shiroma", + "Shishko", + "Shiverick", + "Shivers", + "Shlomo", + "Shoemaker", + "Shoifet", + "Sholeen", + "Sholem", + "Sholes", + "Sholley", + "Sholom", + "Shore", + "Shornick", + "Short", + "Shorter", + "Shoshana", + "Shoshanna", + "Shotton", + "Showker", + "Shreeves", + "Shreve", + "Shrier", + "Shriner", + "Shriver", + "Shu", + "Shue", + "Shugart", + "Shulamith", + "Shulem", + "Shuler", + "Shulins", + "Shull", + "Shulman", + "Shulock", + "Shult", + "Shultz", + "Shum", + "Shuma", + "Shuman", + "Shumway", + "Shuping", + "Shurlock", + "Shurlocke", + "Shurwood", + "Shushan", + "Shute", + "Shutz", + "Shwalb", + "Shyamal", + "Si", + "Siana", + "Sianna", + "Sib", + "Sibbie", + "Sibby", + "Sibeal", + "Sibel", + "Sibell", + "Sibella", + "Sibelle", + "Siberson", + "Sibie", + "Sibilla", + "Sible", + "Siblee", + "Sibley", + "Sibyl", + "Sibylla", + "Sibylle", + "Sibyls", + "Sicard", + "Sices", + "Siclari", + "Sicular", + "Sid", + "Sida", + "Siddon", + "Siddra", + "Sidell", + "Sidhu", + "Sidky", + "Sidman", + "Sidnee", + "Sidney", + "Sidoma", + "Sidon", + "Sidoney", + "Sidonia", + "Sidonie", + "Sidonius", + "Sidonnie", + "Sidoon", + "Sidra", + "Sidran", + "Sidras", + "Sidwel", + "Sidwell", + "Sidwohl", + "Sieber", + "Siegel", + "Siegfried", + "Siegler", + "Sielen", + "Sieracki", + "Sierra", + "Siesser", + "Sievert", + "Siffre", + "Sig", + "Sigfrid", + "Sigfried", + "Sigismond", + "Sigismondo", + "Sigismund", + "Sigismundo", + "Sigler", + "Sigmund", + "Signe", + "Sigrid", + "Sigsmond", + "Sigvard", + "Sihon", + "Sihonn", + "Sihun", + "Sihunn", + "Sik", + "Sikata", + "Sikes", + "Sikko", + "Sikorski", + "Sil", + "Silas", + "Silber", + "Silberman", + "Silda", + "Silden", + "Sile", + "Sileas", + "Silin", + "Sill", + "Sillsby", + "Silma", + "Siloa", + "Siloam", + "Siloum", + "Silsby", + "Silsbye", + "Silva", + "Silvain", + "Silvan", + "Silvana", + "Silvano", + "Silvanus", + "Silver", + "Silverman", + "Silvers", + "Silverstein", + "Silverts", + "Silvester", + "Silvestro", + "Silvia", + "Silvie", + "Silvio", + "Sim", + "Sima", + "Simah", + "Simdars", + "Simeon", + "Simmie", + "Simmonds", + "Simmons", + "Simon", + "Simona", + "Simone", + "Simonetta", + "Simonette", + "Simonne", + "Simons", + "Simonsen", + "Simpkins", + "Simpson", + "Sims", + "Simsar", + "Simson", + "Sinai", + "Sinclair", + "Sinclare", + "Sindee", + "Sine", + "Sinegold", + "Singband", + "Singer", + "Singh", + "Singhal", + "Singleton", + "Sink", + "Sinnard", + "Siobhan", + "Sion", + "Sioux", + "Siouxie", + "Sipple", + "Sirkin", + "Sirmons", + "Sirois", + "Sirotek", + "Sisak", + "Sisco", + "Sisely", + "Sisile", + "Siskind", + "Sissel", + "Sissie", + "Sisson", + "Sissy", + "Sisto", + "Sitarski", + "Sitnik", + "Sitra", + "Siubhan", + "Siusan", + "Sivia", + "Sivie", + "Siward", + "Sjoberg", + "Skantze", + "Skardol", + "Skees", + "Skeie", + "Skell", + "Skelly", + "Skelton", + "Skerl", + "Skiba", + "Skier", + "Skiest", + "Skilken", + "Skill", + "Skillern", + "Skinner", + "Skip", + "Skipp", + "Skipper", + "Skippie", + "Skippy", + "Skipton", + "Sklar", + "Skolnik", + "Skricki", + "Skurnik", + "Skutchan", + "Skvorak", + "Sky", + "Skye", + "Skyla", + "Skylar", + "Skyler", + "Slaby", + "Slack", + "Slade", + "Sladen", + "Slater", + "Slaughter", + "Slavic", + "Slavin", + "Slayton", + "Sldney", + "Slemmer", + "Sletten", + "Slifka", + "Slinkman", + "Sliwa", + "Sloan", + "Sloane", + "Sloatman", + "Slocum", + "Slosberg", + "Slotnick", + "Sluiter", + "Sly", + "Slyke", + "Smail", + "Small", + "Smalley", + "Smallman", + "Smart", + "Smiga", + "Smiley", + "Smith", + "Smitt", + "Smitty", + "Smoot", + "Smukler", + "Snapp", + "Snashall", + "Sneed", + "Snell", + "Snider", + "Snoddy", + "Snodgrass", + "Snook", + "Snow", + "Snowber", + "Snowman", + "Snyder", + "So", + "Soane", + "Sobel", + "Soble", + "Socha", + "Socher", + "Sochor", + "Socrates", + "Soelch", + "Sofer", + "Sofia", + "Sofie", + "Sofko", + "Soinski", + "Sokil", + "Sokul", + "Sol", + "Sola", + "Solana", + "Solange", + "Solberg", + "Solenne", + "Solis", + "Solita", + "Solitta", + "Soll", + "Sollars", + "Solley", + "Sollie", + "Sollows", + "Solly", + "Solnit", + "Soloma", + "Soloman", + "Solomon", + "Solon", + "Soluk", + "Som", + "Somerset", + "Somerville", + "Sommer", + "Sommers", + "Son", + "Sondra", + "Soneson", + "Song", + "Soni", + "Sonia", + "Sonja", + "Sonni", + "Sonnie", + "Sonnnie", + "Sonny", + "Sonstrom", + "Sontag", + "Sontich", + "Sonya", + "Soo", + "Soph", + "Sopher", + "Sophey", + "Sophi", + "Sophia", + "Sophie", + "Sophronia", + "Sophy", + "Soracco", + "Soraya", + "Sorce", + "Sorcha", + "Sorci", + "Sorcim", + "Sorel", + "Soren", + "Sorensen", + "Sorenson", + "Sorilda", + "Sorkin", + "Sorrows", + "Sosanna", + "Sosna", + "Sosthena", + "Sosthenna", + "Sosthina", + "Sothena", + "Sotos", + "Sou", + "Soule", + "Soulier", + "Sousa", + "Southard", + "Southworth", + "Soutor", + "Souvaine", + "Souza", + "Sowell", + "Sower", + "Spada", + "Spain", + "Spalding", + "Spalla", + "Spancake", + "Spanjian", + "Spanos", + "Sparhawk", + "Spark", + "Sparke", + "Sparkie", + "Sparks", + "Sparky", + "Sparrow", + "Spatola", + "Spatz", + "Spaulding", + "Spear", + "Spearing", + "Spearman", + "Spears", + "Specht", + "Spector", + "Spence", + "Spencer", + "Spense", + "Spenser", + "Sperling", + "Speroni", + "Sperry", + "Spevek", + "Spiegel", + "Spiegelman", + "Spiegleman", + "Spieler", + "Spielman", + "Spiers", + "Spike", + "Spillar", + "Spindell", + "Spiro", + "Spiros", + "Spitzer", + "Spohr", + "Spooner", + "Spoor", + "Spracklen", + "Sprage", + "Spragens", + "Sprague", + "Spratt", + "Spring", + "Springer", + "Sproul", + "Sprung", + "Spurgeon", + "Squier", + "Squire", + "Squires", + "Srini", + "Staal", + "Stace", + "Stacee", + "Stacey", + "Staci", + "Stacia", + "Stacie", + "Stacy", + "Stafani", + "Staffan", + "Staffard", + "Stafford", + "Staford", + "Stag", + "Stagg", + "Stahl", + "Stalder", + "Staley", + "Stalk", + "Stalker", + "Stallworth", + "Stamata", + "Stambaugh", + "Stan", + "Stander", + "Standford", + "Standice", + "Standing", + "Standish", + "Standley", + "Standush", + "Stanfield", + "Stanfill", + "Stanford", + "Stanhope", + "Stanislas", + "Stanislaus", + "Stanislaw", + "Stanleigh", + "Stanley", + "Stanly", + "Stannfield", + "Stannwood", + "Stanton", + "Stanway", + "Stanwin", + "Stanwinn", + "Stanwood", + "Stanzel", + "Star", + "Starbuck", + "Stargell", + "Starinsky", + "Stark", + "Starkey", + "Starks", + "Starla", + "Starlene", + "Starlin", + "Starling", + "Starobin", + "Starr", + "Stasny", + "Staten", + "Statis", + "Stauder", + "Stauffer", + "Stav", + "Stavro", + "Stavros", + "Staw", + "Stclair", + "Stead", + "Steady", + "Stearn", + "Stearne", + "Stearns", + "Steck", + "Steddman", + "Stedman", + "Stedmann", + "Stedt", + "Steel", + "Steele", + "Steen", + "Steep", + "Steere", + "Stefa", + "Stefan", + "Stefanac", + "Stefania", + "Stefanie", + "Stefano", + "Steffane", + "Steffen", + "Steffi", + "Steffie", + "Steffin", + "Steffy", + "Stegman", + "Stein", + "Steinberg", + "Steiner", + "Steinke", + "Steinman", + "Steinway", + "Stella", + "Stelle", + "Stelmach", + "Stelu", + "Stempien", + "Stempson", + "Stenger", + "Stent", + "Stepha", + "Stephan", + "Stephana", + "Stephani", + "Stephania", + "Stephanie", + "Stephannie", + "Stephanus", + "Stephen", + "Stephenie", + "Stephens", + "Stephenson", + "Stephi", + "Stephie", + "Stephine", + "Sterling", + "Stern", + "Sternberg", + "Sterne", + "Sterner", + "Sternick", + "Sternlight", + "Sterrett", + "Stesha", + "Stets", + "Stetson", + "Stevana", + "Steve", + "Steven", + "Stevena", + "Stevens", + "Stevenson", + "Stevie", + "Stevy", + "Stew", + "Steward", + "Stewardson", + "Stewart", + "Stich", + "Stichter", + "Stickney", + "Stiegler", + "Stieglitz", + "Stier", + "Stig", + "Stila", + "Stiles", + "Still", + "Stilla", + "Stillas", + "Stillman", + "Stillmann", + "Stilu", + "Stilwell", + "Stimson", + "Stine", + "Stinky", + "Stinson", + "Stirling", + "Stoat", + "Stochmal", + "Stock", + "Stockmon", + "Stockton", + "Stockwell", + "Stoddard", + "Stoddart", + "Stodder", + "Stoeber", + "Stoecker", + "Stoffel", + "Stokes", + "Stoll", + "Stoller", + "Stolzer", + "Stone", + "Stoneham", + "Stoneman", + "Stonwin", + "Stoops", + "Storer", + "Storfer", + "Storm", + "Stormi", + "Stormie", + "Stormy", + "Stortz", + "Story", + "Storz", + "Stouffer", + "Stoughton", + "Stout", + "Stovall", + "Stover", + "Strade", + "Strader", + "Strage", + "Strain", + "Strait", + "Stralka", + "Strander", + "Strang", + "Stranger", + "Stratton", + "Straub", + "Straus", + "Strauss", + "Strawn", + "Streeter", + "Streetman", + "Streeto", + "Strenta", + "Strep", + "Strephon", + "Strephonn", + "Strepphon", + "Stretch", + "Stricklan", + "Strickland", + "Strickler", + "Strickman", + "Stringer", + "Strohbehn", + "Strohben", + "Strohl", + "Stromberg", + "Strong", + "Stronski", + "Stroud", + "Stroup", + "Struve", + "Stryker", + "Stu", + "Stuart", + "Stubbs", + "Stubstad", + "Stucker", + "Stuckey", + "Studdard", + "Studley", + "Studner", + "Studnia", + "Stulin", + "Stultz", + "Stuppy", + "Sturdivant", + "Sturges", + "Sturrock", + "Stutman", + "Stutsman", + "Stutzman", + "Styles", + "Su", + "Suanne", + "Subak", + "Subir", + "Sublett", + "Suchta", + "Suckow", + "Sucy", + "Sudbury", + "Sudderth", + "Sudhir", + "Sudnor", + "Sue", + "Suellen", + "Suelo", + "Sugar", + "Sugden", + "Sugihara", + "Suh", + "Suhail", + "Suilmann", + "Suk", + "Sukey", + "Sukhum", + "Suki", + "Sukin", + "Sula", + "Sulamith", + "Sullivan", + "Sully", + "Sum", + "Sumer", + "Sumerlin", + "Summer", + "Summers", + "Summons", + "Sumner", + "Sunda", + "Sunday", + "Sundberg", + "Sunderland", + "Sundin", + "Sundstrom", + "Suneya", + "Sung", + "Sunil", + "Sunny", + "Sunshine", + "Sup", + "Supat", + "Supen", + "Supple", + "Sura", + "Surbeck", + "Surovy", + "Survance", + "Susan", + "Susana", + "Susanetta", + "Susann", + "Susanna", + "Susannah", + "Susanne", + "Susette", + "Susi", + "Susie", + "Sussi", + "Sussman", + "Sussna", + "Susumu", + "Susy", + "Suter", + "Sutherlan", + "Sutherland", + "Sutphin", + "Sutton", + "Suu", + "Suzan", + "Suzann", + "Suzanna", + "Suzanne", + "Suzetta", + "Suzette", + "Suzi", + "Suzie", + "Suzy", + "Suzzy", + "Sven", + "Svend", + "Svensen", + "Sverre", + "Svetlana", + "Svoboda", + "Swagerty", + "Swain", + "Swaine", + "Swainson", + "Swamy", + "Swan", + "Swane", + "Swanhilda", + "Swanhildas", + "Swann", + "Swanson", + "Swart", + "Swarts", + "Swartz", + "Swayder", + "Swayne", + "Sweatt", + "Swec", + "Swee", + "Sweeney", + "Sweet", + "Swen", + "Swenson", + "Swetiana", + "Swetlana", + "Sweyn", + "Swiercz", + "Swift", + "Swigart", + "Swihart", + "Swinton", + "Swirsky", + "Swisher", + "Swithbart", + "Swithbert", + "Swithin", + "Switzer", + "Swope", + "Swor", + "Swords", + "Sy", + "Sybil", + "Sybila", + "Sybilla", + "Sybille", + "Sybley", + "Sybyl", + "Syck", + "Syd", + "Sydel", + "Sydelle", + "Sydney", + "Sykes", + "Syl", + "Sylas", + "Sylvan", + "Sylvanus", + "Sylvester", + "Sylvia", + "Sylvie", + "Syman", + "Symer", + "Symon", + "Symons", + "Synn", + "Syst", + "Syverson", + "TEirtza", + "Taam", + "Tab", + "Tabatha", + "Tabb", + "Tabbatha", + "Tabber", + "Tabbi", + "Tabbie", + "Tabbitha", + "Tabby", + "Taber", + "Tabib", + "Tabina", + "Tabitha", + "Tabor", + "Tabshey", + "Tace", + "Tacita", + "Tacklind", + "Tacy", + "Tacye", + "Tad", + "Tada", + "Tadashi", + "Tadd", + "Taddeo", + "Taddeusz", + "Tade", + "Tadeas", + "Tadeo", + "Tades", + "Tadich", + "Tadio", + "Taffy", + "Taft", + "Tager", + "Taggart", + "Tahmosh", + "Tai", + "Tailor", + "Taima", + "Taimi", + "Tait", + "Taite", + "Tak", + "Taka", + "Takakura", + "Takara", + "Takashi", + "Takeo", + "Takeshi", + "Takken", + "Tal", + "Tala", + "Talanian", + "Talanta", + "Talbert", + "Talbot", + "Talbott", + "Tali", + "Talia", + "Talich", + "Talie", + "Tallbot", + "Tallbott", + "Talley", + "Tallia", + "Tallie", + "Tallou", + "Tallu", + "Tallula", + "Tallulah", + "Tally", + "Talmud", + "Talya", + "Talyah", + "Tam", + "Tama", + "Tamah", + "Tamanaha", + "Tamar", + "Tamara", + "Tamarah", + "Tamarra", + "Tamaru", + "Tamas", + "Tamberg", + "Tamer", + "Tamera", + "Tami", + "Tamiko", + "Tamis", + "Tamma", + "Tammany", + "Tammara", + "Tammi", + "Tammie", + "Tammy", + "Tamqrah", + "Tamra", + "Tamsky", + "Tan", + "Tana", + "Tanah", + "Tanaka", + "Tanberg", + "Tandi", + "Tandie", + "Tandy", + "Tanhya", + "Tani", + "Tania", + "Tanitansy", + "Tankoos", + "Tann", + "Tannen", + "Tannenbaum", + "Tannenwald", + "Tanner", + "Tanney", + "Tannie", + "Tanny", + "Tansey", + "Tansy", + "Tanya", + "Tapes", + "Tara", + "Tarabar", + "Tarah", + "Taran", + "Tarazi", + "Tare", + "Tareyn", + "Targett", + "Tarkany", + "Taro", + "Tarr", + "Tarra", + "Tarrah", + "Tarrance", + "Tarrant", + "Tarrel", + "Tarrsus", + "Tarryn", + "Tarsus", + "Tarsuss", + "Tartaglia", + "Tartan", + "Tarton", + "Tarttan", + "Taryn", + "Taryne", + "Tasha", + "Tasia", + "Tasiana", + "Tat", + "Tate", + "Tati", + "Tatia", + "Tatiana", + "Tatianas", + "Tatiania", + "Tatianna", + "Tatman", + "Tattan", + "Tatum", + "Taub", + "Tav", + "Taveda", + "Tavey", + "Tavi", + "Tavia", + "Tavie", + "Tavis", + "Tavish", + "Tavy", + "Tawney", + "Tawnya", + "Tawsha", + "Tay", + "Tayib", + "Tayler", + "Taylor", + "Tayyebeb", + "Tchao", + "Teador", + "Teagan", + "Teage", + "Teague", + "Teahan", + "Teak", + "Tearle", + "Tecla", + "Tecu", + "Ted", + "Tedd", + "Tedda", + "Tedder", + "Teddi", + "Teddie", + "Teddman", + "Teddy", + "Tedi", + "Tedie", + "Tedman", + "Tedmann", + "Tedmund", + "Tedra", + "Tedric", + "Teece", + "Teena", + "Teerell", + "Teeter", + "Teevens", + "Teferi", + "Tega", + "Tegan", + "Teillo", + "Teilo", + "Tekla", + "Telfer", + "Telford", + "Telfore", + "Tella", + "Tellford", + "Tem", + "Tema", + "Temp", + "Tempa", + "Tempest", + "Templa", + "Templas", + "Temple", + "Templer", + "Templeton", + "Templia", + "Ten", + "Tena", + "Tench", + "Tenenbaum", + "Tengdin", + "Tengler", + "Tenn", + "Tenner", + "Tennes", + "Tenney", + "Tennies", + "Teodoor", + "Teodor", + "Teodora", + "Teodorico", + "Teodoro", + "Teplica", + "Teplitz", + "Tepper", + "Tera", + "Terbecki", + "Terchie", + "Terena", + "Terence", + "Terencio", + "Teresa", + "Terese", + "Teresina", + "Teresita", + "Teressa", + "Terhune", + "Teri", + "Teria", + "Teriann", + "Terina", + "Terle", + "Ternan", + "Terpstra", + "Terr", + "Terra", + "Terrance", + "Terrel", + "Terrell", + "Terrena", + "Terrence", + "Terrene", + "Terri", + "Terrie", + "Terrijo", + "Terrill", + "Terrilyn", + "Terris", + "Terriss", + "Territus", + "Terry", + "Terrye", + "Terryl", + "Terryn", + "Tersina", + "Terti", + "Tertia", + "Tertias", + "Tertius", + "Teryl", + "Teryn", + "Terza", + "Terzas", + "Tesler", + "Tess", + "Tessa", + "Tessi", + "Tessie", + "Tessler", + "Tessy", + "Teteak", + "Teufert", + "Teuton", + "Tevis", + "Tewell", + "Tewfik", + "Tews", + "Thacher", + "Thacker", + "Thackeray", + "Thad", + "Thaddaus", + "Thaddeus", + "Thaddus", + "Thadeus", + "Thagard", + "Thain", + "Thaine", + "Thais", + "Thalassa", + "Thalia", + "Tham", + "Thamora", + "Thamos", + "Thanasi", + "Thane", + "Thanh", + "Thanos", + "Thant", + "Thapa", + "Thar", + "Tharp", + "Thatch", + "Thatcher", + "Thaxter", + "Thay", + "Thayer", + "Thayne", + "The", + "Thea", + "Theadora", + "Theall", + "Thebault", + "Thecla", + "Theda", + "Thedric", + "Thedrick", + "Theis", + "Thekla", + "Thelma", + "Thema", + "Themis", + "Thenna", + "Theo", + "Theobald", + "Theodor", + "Theodora", + "Theodore", + "Theodoric", + "Theodosia", + "Theola", + "Theona", + "Theone", + "Thera", + "Theran", + "Theresa", + "Therese", + "Theresina", + "Theresita", + "Theressa", + "Therine", + "Theron", + "Therron", + "Thesda", + "Thessa", + "Theta", + "Thetes", + "Thetis", + "Thetisa", + "Thetos", + "Theurer", + "Theurich", + "Thevenot", + "Thia", + "Thibaud", + "Thibault", + "Thibaut", + "Thielen", + "Thier", + "Thierry", + "Thilda", + "Thilde", + "Thill", + "Thin", + "Thinia", + "Thirion", + "Thirza", + "Thirzi", + "Thirzia", + "Thisbe", + "Thisbee", + "Thissa", + "Thistle", + "Thoer", + "Thom", + "Thoma", + "Thomajan", + "Thomas", + "Thomasa", + "Thomasin", + "Thomasina", + "Thomasine", + "Thomey", + "Thompson", + "Thomsen", + "Thomson", + "Thor", + "Thora", + "Thorbert", + "Thordia", + "Thordis", + "Thorfinn", + "Thorin", + "Thorlay", + "Thorley", + "Thorlie", + "Thorma", + "Thorman", + "Thormora", + "Thorn", + "Thornburg", + "Thorncombe", + "Thorndike", + "Thorne", + "Thorner", + "Thornie", + "Thornton", + "Thorny", + "Thorpe", + "Thorr", + "Thorrlow", + "Thorstein", + "Thorsten", + "Thorvald", + "Thorwald", + "Thrasher", + "Three", + "Threlkeld", + "Thrift", + "Thun", + "Thunell", + "Thurber", + "Thurlough", + "Thurlow", + "Thurman", + "Thurmann", + "Thurmond", + "Thurnau", + "Thursby", + "Thurstan", + "Thurston", + "Thury", + "Thynne", + "Tia", + "Tiana", + "Tibbetts", + "Tibbitts", + "Tibbs", + "Tibold", + "Tica", + "Tice", + "Tichon", + "Tichonn", + "Ticknor", + "Ticon", + "Tidwell", + "Tiebold", + "Tiebout", + "Tiedeman", + "Tiemroth", + "Tien", + "Tiena", + "Tierell", + "Tiernan", + "Tierney", + "Tiersten", + "Tiertza", + "Tierza", + "Tifanie", + "Tiff", + "Tiffa", + "Tiffani", + "Tiffanie", + "Tiffanle", + "Tiffany", + "Tiffi", + "Tiffie", + "Tiffy", + "Tiga", + "Tigges", + "Tila", + "Tilda", + "Tilden", + "Tildi", + "Tildie", + "Tildy", + "Tiler", + "Tilford", + "Till", + "Tilla", + "Tillford", + "Tillfourd", + "Tillie", + "Tillinger", + "Tillio", + "Tillion", + "Tillman", + "Tillo", + "Tilly", + "Tilney", + "Tiloine", + "Tim", + "Tima", + "Timi", + "Timmi", + "Timmie", + "Timmons", + "Timms", + "Timmy", + "Timofei", + "Timon", + "Timoteo", + "Timothea", + "Timothee", + "Timotheus", + "Timothy", + "Tina", + "Tinaret", + "Tindall", + "Tine", + "Tingey", + "Tingley", + "Tini", + "Tiny", + "Tinya", + "Tiossem", + "Tiphane", + "Tiphani", + "Tiphanie", + "Tiphany", + "Tippets", + "Tips", + "Tipton", + "Tirrell", + "Tirza", + "Tirzah", + "Tisbe", + "Tisbee", + "Tisdale", + "Tish", + "Tisha", + "Tisman", + "Tita", + "Titania", + "Tito", + "Titos", + "Titus", + "Tizes", + "Tjaden", + "Tjader", + "Tjon", + "Tletski", + "Toback", + "Tobe", + "Tobey", + "Tobi", + "Tobiah", + "Tobias", + "Tobie", + "Tobin", + "Tobit", + "Toby", + "Tobye", + "Tocci", + "Tod", + "Todd", + "Toddie", + "Toddy", + "Todhunter", + "Toffey", + "Toffic", + "Toft", + "Toh", + "Toiboid", + "Toinette", + "Tol", + "Toland", + "Tolkan", + "Toll", + "Tolland", + "Tolley", + "Tolliver", + "Tollman", + "Tollmann", + "Tolmach", + "Tolman", + "Tolmann", + "Tom", + "Toma", + "Tomas", + "Tomasina", + "Tomasine", + "Tomaso", + "Tomasz", + "Tombaugh", + "Tomchay", + "Tome", + "Tomi", + "Tomkiel", + "Tomkin", + "Tomkins", + "Tomlin", + "Tomlinson", + "Tommi", + "Tommie", + "Tommy", + "Tompkins", + "Toms", + "Toney", + "Tongue", + "Toni", + "Tonia", + "Tonie", + "Tonina", + "Tonjes", + "Tonkin", + "Tonl", + "Tonneson", + "Tonnie", + "Tonry", + "Tony", + "Tonya", + "Tonye", + "Toogood", + "Toole", + "Tooley", + "Toolis", + "Toomay", + "Toombs", + "Toomin", + "Toor", + "Tootsie", + "Topliffe", + "Topper", + "Topping", + "Tor", + "Torbart", + "Torbert", + "Tore", + "Torey", + "Torhert", + "Tori", + "Torie", + "Torin", + "Tormoria", + "Torosian", + "Torp", + "Torr", + "Torrance", + "Torras", + "Torray", + "Torre", + "Torrell", + "Torrence", + "Torres", + "Torrey", + "Torrie", + "Torrin", + "Torrlow", + "Torruella", + "Torry", + "Torto", + "Tortosa", + "Tory", + "Toscano", + "Tosch", + "Toshiko", + "Toth", + "Touber", + "Toulon", + "Tound", + "Tova", + "Tove", + "Towbin", + "Tower", + "Towers", + "Towill", + "Towland", + "Town", + "Towne", + "Towney", + "Townie", + "Townsend", + "Townshend", + "Towny", + "Towrey", + "Towroy", + "Toy", + "Trabue", + "Tracay", + "Trace", + "Tracee", + "Tracey", + "Traci", + "Tracie", + "Tracy", + "Trager", + "Trahern", + "Trahurn", + "Trainer", + "Trainor", + "Trakas", + "Trammel", + "Tran", + "Tranquada", + "Trant", + "Trask", + "Tratner", + "Trauner", + "Trautman", + "Travax", + "Traver", + "Travers", + "Travis", + "Travus", + "Traweek", + "Tray", + "Treacy", + "Treat", + "Trefler", + "Trefor", + "Treharne", + "Treiber", + "Trela", + "Trella", + "Trellas", + "Trelu", + "Tremain", + "Tremaine", + "Tremann", + "Tremayne", + "Trembly", + "Tremml", + "Trenna", + "Trent", + "Trenton", + "Tresa", + "Trescha", + "Trescott", + "Tressa", + "Tressia", + "Treulich", + "Trev", + "Treva", + "Trevah", + "Trevar", + "Trever", + "Trevethick", + "Trevor", + "Trevorr", + "Trey", + "Tri", + "Trici", + "Tricia", + "Trilbee", + "Trilbi", + "Trilbie", + "Trilby", + "Triley", + "Trill", + "Trillbee", + "Trillby", + "Trilley", + "Trilly", + "Trimble", + "Trimmer", + "Trin", + "Trina", + "Trinatte", + "Trinee", + "Trinetta", + "Trinette", + "Trini", + "Trinia", + "Trinidad", + "Trinity", + "Trinl", + "Triny", + "Trip", + "Triplett", + "Tripp", + "Tris", + "Trisa", + "Trish", + "Trisha", + "Trista", + "Tristam", + "Tristan", + "Tristas", + "Tristis", + "Tristram", + "Trix", + "Trixi", + "Trixie", + "Trixy", + "Trocki", + "Trojan", + "Trometer", + "Tronna", + "Troth", + "Trotta", + "Trotter", + "Trout", + "Trovillion", + "Trow", + "Troxell", + "Troy", + "Troyes", + "Trstram", + "Trubow", + "Truc", + "Truda", + "Trude", + "Trudey", + "Trudi", + "Trudie", + "Trudnak", + "Trudy", + "True", + "Trueblood", + "Truelove", + "Trueman", + "Truitt", + "Trula", + "Trumaine", + "Truman", + "Trumann", + "Truscott", + "Trust", + "Trutko", + "Tryck", + "Trygve", + "Tsai", + "Tsan", + "Tse", + "Tseng", + "Tshombe", + "Tsuda", + "Tsui", + "Tu", + "Tubb", + "Tuchman", + "Tuck", + "Tucker", + "Tuckie", + "Tucky", + "Tuddor", + "Tudela", + "Tudor", + "Tuesday", + "Tufts", + "Tugman", + "Tuinenga", + "Tull", + "Tulley", + "Tullius", + "Tullus", + "Tullusus", + "Tully", + "Tumer", + "Tuneberg", + "Tung", + "Tunnell", + "Tupler", + "Tuppeny", + "Turino", + "Turk", + "Turley", + "Turmel", + "Turnbull", + "Turne", + "Turner", + "Turnheim", + "Turoff", + "Turpin", + "Turrell", + "Turro", + "Turtle", + "Tut", + "Tutankhamen", + "Tutt", + "Tuttle", + "Tutto", + "Twedy", + "Twelve", + "Twila", + "Twitt", + "Twum", + "Twyla", + "Ty", + "Tybald", + "Tybalt", + "Tybi", + "Tybie", + "Tychon", + "Tychonn", + "Tye", + "Tyika", + "Tyler", + "Tymes", + "Tymon", + "Tymothy", + "Tynan", + "Tyne", + "Tyra", + "Tyre", + "Tyree", + "Tyrone", + "Tyrrell", + "Tyrus", + "Tyson", + "Tzong", + "Ubald", + "Uball", + "Ubana", + "Ube", + "Uchida", + "Uchish", + "Uda", + "Udale", + "Udall", + "Udela", + "Udele", + "Udell", + "Udella", + "Udelle", + "Uel", + "Uela", + "Uella", + "Ugo", + "Uird", + "Uis", + "Uke", + "Ul", + "Ula", + "Ulah", + "Ulane", + "Ulani", + "Ulberto", + "Ulda", + "Ule", + "Ulick", + "Ulises", + "Ulita", + "Ulla", + "Ulland", + "Ullman", + "Ullund", + "Ullyot", + "Ulphi", + "Ulphia", + "Ulphiah", + "Ulric", + "Ulrica", + "Ulrich", + "Ulrick", + "Ulrika", + "Ulrikaumeko", + "Ulrike", + "Ultan", + "Ultann", + "Ultima", + "Ultun", + "Ulu", + "Ulund", + "Ulysses", + "Umberto", + "Ume", + "Umeh", + "Umeko", + "Ummersen", + "Umont", + "Un", + "Una", + "Unders", + "Underwood", + "Undine", + "Undis", + "Undry", + "Une", + "Ungley", + "Uni", + "Unity", + "Unni", + "Uno", + "Upali", + "Uphemia", + "Upshaw", + "Upton", + "Urana", + "Urania", + "Uranie", + "Urata", + "Urba", + "Urbai", + "Urbain", + "Urban", + "Urbana", + "Urbani", + "Urbanna", + "Urbannai", + "Urbannal", + "Urbano", + "Urbanus", + "Urbas", + "Uri", + "Uria", + "Uriah", + "Urial", + "Urian", + "Urias", + "Uriel", + "Urien", + "Uriia", + "Uriiah", + "Uriisa", + "Urina", + "Urion", + "Urissa", + "Urita", + "Urquhart", + "Ursa", + "Ursal", + "Ursala", + "Ursas", + "Ursel", + "Ursi", + "Ursola", + "Urson", + "Ursula", + "Ursulette", + "Ursulina", + "Ursuline", + "Ury", + "Usanis", + "Ushijima", + "Uta", + "Utas", + "Ute", + "Utham", + "Uthrop", + "Utica", + "Uticas", + "Utimer", + "Utley", + "Utta", + "Uttasta", + "Utter", + "Uttica", + "Uuge", + "Uund", + "Uwton", + "Uyekawa", + "Uzia", + "Uzial", + "Uziel", + "Uzzi", + "Uzzia", + "Uzzial", + "Uzziel", + "Va", + "Vaas", + "Vaasta", + "Vachel", + "Vachell", + "Vachil", + "Vachill", + "Vacla", + "Vaclav", + "Vaclava", + "Vacuva", + "Vada", + "Vaden", + "Vadim", + "Vadnee", + "Vaenfila", + "Vahe", + "Vaientina", + "Vail", + "Vaios", + "Vaish", + "Val", + "Vala", + "Valaree", + "Valaria", + "Valda", + "Valdas", + "Valdemar", + "Valdes", + "Valdis", + "Vale", + "Valeda", + "Valenba", + "Valencia", + "Valene", + "Valenka", + "Valenta", + "Valente", + "Valentia", + "Valentijn", + "Valentin", + "Valentina", + "Valentine", + "Valentino", + "Valenza", + "Valer", + "Valera", + "Valeria", + "Valerian", + "Valerie", + "Valerio", + "Valerlan", + "Valerle", + "Valery", + "Valerye", + "Valeta", + "Valiant", + "Valida", + "Valina", + "Valle", + "Valleau", + "Vallery", + "Valley", + "Valli", + "Vallie", + "Vallo", + "Vallonia", + "Vally", + "Valma", + "Valonia", + "Valoniah", + "Valora", + "Valorie", + "Valry", + "Valtin", + "Van", + "VanHook", + "Vance", + "Vanda", + "Vanden", + "Vander", + "Vanderhoek", + "Vandervelde", + "Vandyke", + "Vanessa", + "Vange", + "Vanhomrigh", + "Vani", + "Vania", + "Vanna", + "Vanni", + "Vannie", + "Vanny", + "Vano", + "Vanthe", + "Vanya", + "Vanzant", + "Varden", + "Vardon", + "Vareck", + "Vargas", + "Varhol", + "Varian", + "Varick", + "Varien", + "Varini", + "Varion", + "Varipapa", + "Varney", + "Varrian", + "Vary", + "Vas", + "Vashtee", + "Vashti", + "Vashtia", + "Vasileior", + "Vasilek", + "Vasili", + "Vasiliki", + "Vasilis", + "Vasiliu", + "Vasily", + "Vasos", + "Vasquez", + "Vassar", + "Vassaux", + "Vassell", + "Vassili", + "Vassily", + "Vasta", + "Vastah", + "Vastha", + "Vasti", + "Vasya", + "Vasyuta", + "Vaughan", + "Vaughn", + "Vaules", + "Veal", + "Veator", + "Veats", + "Veda", + "Vedetta", + "Vedette", + "Vedi", + "Vedis", + "Veedis", + "Velasco", + "Velda", + "Veleda", + "Velick", + "Veljkov", + "Velleman", + "Velma", + "Velvet", + "Vena", + "Venable", + "Venator", + "Venditti", + "Veneaux", + "Venetia", + "Venetis", + "Venezia", + "Venice", + "Venita", + "Venn", + "Veno", + "Venola", + "Venterea", + "Vento", + "Ventre", + "Ventura", + "Venu", + "Venus", + "Venuti", + "Ver", + "Vera", + "Verada", + "Veradi", + "Veradia", + "Veradis", + "Verbenia", + "Verda", + "Verdha", + "Verdi", + "Verdie", + "Vere", + "Verena", + "Verene", + "Verge", + "Verger", + "Vergil", + "Vergne", + "Vergos", + "Veriee", + "Verile", + "Verina", + "Verine", + "Verity", + "Verla", + "Verlee", + "Verlie", + "Vern", + "Verna", + "Verne", + "Vernen", + "Verner", + "Verneuil", + "Verney", + "Vernice", + "Vernier", + "Vernita", + "Vernon", + "Vernor", + "Veron", + "Veronica", + "Veronika", + "Veronike", + "Veronique", + "Verras", + "Vershen", + "Vescuso", + "Vesta", + "Veta", + "Vetter", + "Vevay", + "Vevina", + "Vevine", + "Vey", + "Vezza", + "Vharat", + "Vi", + "Viafore", + "Vial", + "Vic", + "Viccora", + "Vick", + "Vickey", + "Vicki", + "Vickie", + "Vicky", + "Victoir", + "Victor", + "Victoria", + "Victorie", + "Victorine", + "Victory", + "Vida", + "Vidal", + "Vidda", + "Viddah", + "Vidovic", + "Vidovik", + "Viehmann", + "Viens", + "Vierno", + "Vieva", + "Vig", + "Vigen", + "Viglione", + "Vigor", + "Viguerie", + "Viki", + "Viking", + "Vikki", + "Vikky", + "Vilberg", + "Vilhelmina", + "Villada", + "Villiers", + "Vilma", + "Vin", + "Vina", + "Vinaya", + "Vince", + "Vincelette", + "Vincent", + "Vincenta", + "Vincentia", + "Vincents", + "Vincenty", + "Vincenz", + "Vine", + "Vinia", + "Vinita", + "Vinn", + "Vinna", + "Vinni", + "Vinnie", + "Vinny", + "Vins", + "Vinson", + "Viola", + "Violante", + "Viole", + "Violet", + "Violeta", + "Violetta", + "Violette", + "Vipul", + "Viquelia", + "Viradis", + "Virendra", + "Virg", + "Virge", + "Virgel", + "Virgie", + "Virgil", + "Virgilia", + "Virgilio", + "Virgin", + "Virgina", + "Virginia", + "Virginie", + "Virgy", + "Viridi", + "Viridis", + "Viridissa", + "Virnelli", + "Viscardi", + "Vish", + "Vita", + "Vitale", + "Vitalis", + "Vite", + "Vitek", + "Vitia", + "Vitkun", + "Vito", + "Vitoria", + "Vittoria", + "Vittorio", + "Vitus", + "Viv", + "Viva", + "Viveca", + "Vivi", + "Vivia", + "Vivian", + "Viviana", + "Viviane", + "Vivianna", + "Vivianne", + "Vivica", + "Vivie", + "Vivien", + "Viviene", + "Vivienne", + "Viviyan", + "Vivl", + "Vivle", + "Vivyan", + "Vivyanne", + "Vizza", + "Vizzone", + "Vlad", + "Vlada", + "Vladamar", + "Vladamir", + "Vladi", + "Vladimar", + "Vladimir", + "Voccola", + "Voe", + "Vogel", + "Vogele", + "Vogeley", + "Vola", + "Volding", + "Voleta", + "Voletta", + "Volin", + "Volkan", + "Volnak", + "Volnay", + "Volney", + "Volny", + "Volotta", + "Volpe", + "Voltmer", + "Voltz", + "Von", + "Vona", + "Vonni", + "Vonnie", + "Vonny", + "Vookles", + "Voorhis", + "Vorfeld", + "Vories", + "Vorster", + "Voss", + "Votaw", + "Vowel", + "Vrablik", + "Vtarj", + "Vtehsta", + "Vudimir", + "Vullo", + "Vyky", + "Vyner", + "Vyse", + "Waal", + "Wachtel", + "Wachter", + "Wack", + "Waddell", + "Waddington", + "Waddle", + "Wade", + "Wadell", + "Wadesworth", + "Wadleigh", + "Wadlinger", + "Wadsworth", + "Waechter", + "Waers", + "Wager", + "Wagner", + "Wagoner", + "Wagshul", + "Wagstaff", + "Wahkuna", + "Wahl", + "Wahlstrom", + "Wailoo", + "Wain", + "Waine", + "Wainwright", + "Wait", + "Waite", + "Waiter", + "Wake", + "Wakeen", + "Wakefield", + "Wakerly", + "Waki", + "Walburga", + "Walcoff", + "Walcott", + "Walczak", + "Wald", + "Waldack", + "Waldemar", + "Walden", + "Waldman", + "Waldner", + "Waldo", + "Waldon", + "Waldos", + "Waldron", + "Wales", + "Walford", + "Waligore", + "Walke", + "Walker", + "Walkling", + "Wall", + "Wallace", + "Wallach", + "Wallache", + "Wallack", + "Wallas", + "Waller", + "Walley", + "Wallford", + "Walli", + "Wallie", + "Walling", + "Wallinga", + "Wallis", + "Walliw", + "Wallraff", + "Walls", + "Wally", + "Walrath", + "Walsh", + "Walston", + "Walt", + "Walter", + "Walters", + "Walther", + "Waltner", + "Walton", + "Walworth", + "Waly", + "Wampler", + "Wamsley", + "Wan", + "Wanda", + "Wandie", + "Wandis", + "Wandy", + "Wane", + "Waneta", + "Wanfried", + "Wang", + "Wanids", + "Wanonah", + "Wanyen", + "Wappes", + "Warchaw", + "Ward", + "Warde", + "Warden", + "Warder", + "Wardieu", + "Wardlaw", + "Wardle", + "Ware", + "Wareing", + "Warenne", + "Warfeld", + "Warfield", + "Warfold", + "Warford", + "Warfore", + "Warfourd", + "Warga", + "Warila", + "Waring", + "Warms", + "Warner", + "Warp", + "Warram", + "Warren", + "Warrenne", + "Warrick", + "Warrin", + "Warring", + "Warthman", + "Warton", + "Wartow", + "Warwick", + "Wash", + "Washburn", + "Washington", + "Washko", + "Wasserman", + "Wasson", + "Wassyngton", + "Wat", + "Watanabe", + "Waterer", + "Waterman", + "Waters", + "Watkin", + "Watkins", + "Watson", + "Watt", + "Wattenberg", + "Watters", + "Watts", + "Waugh", + "Wauters", + "Wavell", + "Waverley", + "Waverly", + "Wawro", + "Waxler", + "Waxman", + "Way", + "Waylan", + "Wayland", + "Waylen", + "Waylin", + "Waylon", + "Waynant", + "Wayne", + "Wayolle", + "Weaks", + "Wearing", + "Weasner", + "Weatherby", + "Weatherley", + "Weathers", + "Weaver", + "Web", + "Webb", + "Webber", + "Weber", + "Webster", + "Wedurn", + "Weed", + "Weeks", + "Wehner", + "Wehrle", + "Wei", + "Weibel", + "Weidar", + "Weide", + "Weider", + "Weidman", + "Weidner", + "Weig", + "Weight", + "Weigle", + "Weihs", + "Weikert", + "Weil", + "Weiler", + "Weiman", + "Wein", + "Weinberg", + "Weiner", + "Weinert", + "Weingarten", + "Weingartner", + "Weinhardt", + "Weinman", + "Weinreb", + "Weinrich", + "Weinshienk", + "Weinstein", + "Weinstock", + "Weintrob", + "Weir", + "Weirick", + "Weisbart", + "Weisberg", + "Weisbrodt", + "Weisburgh", + "Weiser", + "Weisler", + "Weisman", + "Weismann", + "Weiss", + "Weissberg", + "Weissman", + "Weissmann", + "Weitman", + "Weitzman", + "Weixel", + "Weksler", + "Welbie", + "Welby", + "Welch", + "Welcher", + "Welcome", + "Welcy", + "Weld", + "Weldon", + "Welford", + "Welker", + "Welles", + "Wellesley", + "Wellington", + "Wells", + "Welsh", + "Welton", + "Wenda", + "Wendall", + "Wendalyn", + "Wende", + "Wendel", + "Wendelin", + "Wendelina", + "Wendeline", + "Wendell", + "Wendi", + "Wendie", + "Wendin", + "Wendolyn", + "Wendt", + "Wendy", + "Wendye", + "Wenger", + "Wengert", + "Wenn", + "Wennerholn", + "Wenoa", + "Wenona", + "Wenonah", + "Wentworth", + "Wenz", + "Wera", + "Werbel", + "Werby", + "Werner", + "Wernher", + "Wernick", + "Wernsman", + "Werra", + "Wershba", + "Wertheimer", + "Wertz", + "Wes", + "Wesa", + "Wescott", + "Wesla", + "Wesle", + "Weslee", + "Wesley", + "Wessling", + "West", + "Westberg", + "Westbrook", + "Westbrooke", + "Wester", + "Westerfield", + "Westfahl", + "Westfall", + "Westhead", + "Westland", + "Westleigh", + "Westley", + "Westlund", + "Westmoreland", + "Westney", + "Weston", + "Westphal", + "Wetzel", + "Wetzell", + "Wexler", + "Wey", + "Weyermann", + "Weylin", + "Weywadt", + "Whale", + "Whalen", + "Whall", + "Whallon", + "Whang", + "Wharton", + "Whatley", + "Wheaton", + "Wheeler", + "Wheelwright", + "Whelan", + "Whetstone", + "Whiffen", + "Whiney", + "Whipple", + "Whit", + "Whitaker", + "Whitby", + "Whitcher", + "Whitcomb", + "White", + "Whitebook", + "Whitehouse", + "Whitehurst", + "Whitelaw", + "Whiteley", + "Whitford", + "Whiting", + "Whitman", + "Whitnell", + "Whitney", + "Whitson", + "Whittaker", + "Whittemore", + "Whitten", + "Whitver", + "Whorton", + "Whyte", + "Wiatt", + "Wiburg", + "Wichern", + "Wichman", + "Wickham", + "Wickman", + "Wickner", + "Wicks", + "Widera", + "Wie", + "Wiebmer", + "Wieche", + "Wiedmann", + "Wiencke", + "Wiener", + "Wier", + "Wieren", + "Wiersma", + "Wiese", + "Wiggins", + "Wight", + "Wightman", + "Wil", + "Wilber", + "Wilbert", + "Wilbur", + "Wilburn", + "Wilburt", + "Wilcox", + "Wilda", + "Wilde", + "Wildee", + "Wilden", + "Wilder", + "Wildermuth", + "Wildon", + "Wileen", + "Wilek", + "Wilen", + "Wiles", + "Wiley", + "Wilfred", + "Wilfreda", + "Wilfrid", + "Wilhelm", + "Wilhelmina", + "Wilhelmine", + "Wilhide", + "Wilie", + "Wilinski", + "Wilkens", + "Wilkey", + "Wilkie", + "Wilkins", + "Wilkinson", + "Wilkison", + "Will", + "Willa", + "Willabella", + "Willamina", + "Willard", + "Willcox", + "Willdon", + "Willem", + "Willet", + "Willett", + "Willetta", + "Willette", + "Willey", + "Willi", + "William", + "Williams", + "Williamsen", + "Williamson", + "Willie", + "Willin", + "Willing", + "Willis", + "Willman", + "Willmert", + "Willms", + "Willner", + "Willock", + "Willow", + "Wills", + "Willtrude", + "Willumsen", + "Willy", + "Willyt", + "Wilma", + "Wilmar", + "Wilmer", + "Wilmette", + "Wilmott", + "Wilona", + "Wilonah", + "Wilone", + "Wilow", + "Wilscam", + "Wilser", + "Wilsey", + "Wilson", + "Wilt", + "Wilterdink", + "Wilton", + "Wiltsey", + "Wiltshire", + "Wiltz", + "Wimsatt", + "Win", + "Wina", + "Wincer", + "Winchell", + "Winchester", + "Wind", + "Windham", + "Windsor", + "Windy", + "Windzer", + "Winebaum", + "Winer", + "Winfield", + "Winfred", + "Winfrid", + "Wing", + "Wini", + "Winifield", + "Winifred", + "Winikka", + "Winn", + "Winna", + "Winnah", + "Winne", + "Winni", + "Winnick", + "Winnie", + "Winnifred", + "Winny", + "Winograd", + "Winola", + "Winona", + "Winonah", + "Winou", + "Winser", + "Winshell", + "Winslow", + "Winson", + "Winsor", + "Winston", + "Winstonn", + "Winter", + "Winterbottom", + "Winters", + "Winther", + "Winthorpe", + "Winthrop", + "Winton", + "Winwaloe", + "Winzler", + "Wira", + "Wirth", + "Wise", + "Wiseman", + "Wiskind", + "Wisnicki", + "Wistrup", + "Wit", + "Witcher", + "Witha", + "Witherspoon", + "Witkin", + "Witt", + "Witte", + "Wittenburg", + "Wittie", + "Witty", + "Wivestad", + "Wivina", + "Wivinah", + "Wivinia", + "Wixted", + "Woehick", + "Woermer", + "Wohlen", + "Wohlert", + "Wojak", + "Wojcik", + "Wolbrom", + "Wolcott", + "Wolenik", + "Wolf", + "Wolfe", + "Wolff", + "Wolfgang", + "Wolfgram", + "Wolfie", + "Wolford", + "Wolfort", + "Wolfram", + "Wolfson", + "Wolfy", + "Wolgast", + "Wolk", + "Woll", + "Wollis", + "Wolpert", + "Wolsky", + "Womack", + "Won", + "Wonacott", + "Wong", + "Woo", + "Wood", + "Woodall", + "Woodberry", + "Woodcock", + "Woodford", + "Woodhead", + "Woodhouse", + "Woodie", + "Woodley", + "Woodman", + "Woodring", + "Woodrow", + "Woodruff", + "Woods", + "Woodson", + "Woodsum", + "Woodward", + "Woody", + "Woolcott", + "Wooldridge", + "Woolley", + "Woolson", + "Wooster", + "Wootan", + "Woothen", + "Wootten", + "Worden", + "Worl", + "Worlock", + "Worrell", + "Worsham", + "Worth", + "Worthington", + "Worthy", + "Wrand", + "Wren", + "Wrench", + "Wrennie", + "Wright", + "Wrightson", + "Wrigley", + "Wsan", + "Wu", + "Wulf", + "Wulfe", + "Wun", + "Wunder", + "Wurst", + "Wurster", + "Wurtz", + "Wyatan", + "Wyatt", + "Wyck", + "Wycoff", + "Wye", + "Wylde", + "Wylen", + "Wyler", + "Wylie", + "Wylma", + "Wyly", + "Wymore", + "Wyn", + "Wyndham", + "Wyne", + "Wynn", + "Wynne", + "Wynnie", + "Wynny", + "Wyon", + "Wystand", + "Xantha", + "Xanthe", + "Xanthus", + "Xavier", + "Xaviera", + "Xavler", + "Xena", + "Xenia", + "Xeno", + "Xenophon", + "Xenos", + "Xerxes", + "Xever", + "Ximena", + "Ximenes", + "Ximenez", + "Xylia", + "Xylina", + "Xylon", + "Xymenes", + "Yaakov", + "Yablon", + "Yacano", + "Yacov", + "Yaeger", + "Yael", + "Yager", + "Yahiya", + "Yaker", + "Yale", + "Yalonda", + "Yam", + "Yamauchi", + "Yanaton", + "Yance", + "Yancey", + "Yancy", + "Yand", + "Yank", + "Yankee", + "Yann", + "Yarak", + "Yard", + "Yardley", + "Yaron", + "Yarvis", + "Yasmeen", + "Yasmin", + "Yasmine", + "Yasu", + "Yasui", + "Yate", + "Yates", + "Yatzeck", + "Yaya", + "Yazbak", + "Yeargain", + "Yearwood", + "Yeaton", + "Yecies", + "Yee", + "Yeh", + "Yehudi", + "Yehudit", + "Yelena", + "Yelich", + "Yelmene", + "Yemane", + "Yeo", + "Yeorgi", + "Yerga", + "Yerkovich", + "Yerxa", + "Yesima", + "Yeta", + "Yetac", + "Yetah", + "Yetta", + "Yetti", + "Yettie", + "Yetty", + "Yeung", + "Yevette", + "Yi", + "Yila", + "Yim", + "Yirinec", + "Ylla", + "Ynes", + "Ynez", + "Yoho", + "Yoko", + "Yokoyama", + "Yokum", + "Yolanda", + "Yolande", + "Yolane", + "Yolanthe", + "Yona", + "Yonah", + "Yonatan", + "Yong", + "Yonina", + "Yonit", + "Yonita", + "Yoo", + "Yoong", + "Yordan", + "Yorgen", + "Yorgo", + "Yorgos", + "Yorick", + "York", + "Yorke", + "Yorker", + "Yoshi", + "Yoshiko", + "Yoshio", + "Youlton", + "Young", + "Younger", + "Youngman", + "Youngran", + "Yousuf", + "Yovonnda", + "Ysabel", + "Yseult", + "Yseulta", + "Yseulte", + "Yuhas", + "Yuille", + "Yuji", + "Yuk", + "Yukio", + "Yul", + "Yule", + "Yulma", + "Yuma", + "Yumuk", + "Yun", + "Yunfei", + "Yung", + "Yunick", + "Yup", + "Yuri", + "Yuria", + "Yurik", + "Yursa", + "Yurt", + "Yusem", + "Yusuk", + "Yuu", + "Yuzik", + "Yves", + "Yvette", + "Yvon", + "Yvonne", + "Yvonner", + "Yvor", + "Zabrina", + "Zabrine", + "Zacarias", + "Zaccaria", + "Zacek", + "Zach", + "Zachar", + "Zacharia", + "Zachariah", + "Zacharias", + "Zacharie", + "Zachary", + "Zacherie", + "Zachery", + "Zack", + "Zackariah", + "Zacks", + "Zadack", + "Zadoc", + "Zahara", + "Zahavi", + "Zaid", + "Zailer", + "Zak", + "Zakaria", + "Zakarias", + "Zalea", + "Zales", + "Zaller", + "Zalucki", + "Zamir", + "Zamora", + "Zampardi", + "Zampino", + "Zandra", + "Zandt", + "Zane", + "Zaneski", + "Zaneta", + "Zannini", + "Zantos", + "Zanze", + "Zara", + "Zaragoza", + "Zarah", + "Zared", + "Zaremski", + "Zarger", + "Zaria", + "Zarla", + "Zashin", + "Zaslow", + "Zasuwa", + "Zavala", + "Zavras", + "Zawde", + "Zea", + "Zealand", + "Zeb", + "Zeba", + "Zebada", + "Zebadiah", + "Zebapda", + "Zebe", + "Zebedee", + "Zebulen", + "Zebulon", + "Zechariah", + "Zeculon", + "Zed", + "Zedekiah", + "Zeeba", + "Zeena", + "Zehe", + "Zeidman", + "Zeiger", + "Zeiler", + "Zeitler", + "Zeke", + "Zel", + "Zela", + "Zelazny", + "Zelda", + "Zelde", + "Zelig", + "Zelikow", + "Zelle", + "Zellner", + "Zelma", + "Zelten", + "Zena", + "Zenas", + "Zenda", + "Zendah", + "Zenger", + "Zenia", + "Zennas", + "Zennie", + "Zenobia", + "Zeph", + "Zephan", + "Zephaniah", + "Zeralda", + "Zerelda", + "Zerk", + "Zerla", + "Zerlina", + "Zerline", + "Zeta", + "Zetana", + "Zetes", + "Zetta", + "Zeus", + "Zhang", + "Zia", + "Ziagos", + "Zicarelli", + "Ziegler", + "Zielsdorf", + "Zigmund", + "Zigrang", + "Ziguard", + "Zilber", + "Zildjian", + "Zilla", + "Zillah", + "Zilvia", + "Zima", + "Zimmer", + "Zimmerman", + "Zimmermann", + "Zina", + "Zinah", + "Zinck", + "Zindman", + "Zingale", + "Zingg", + "Zink", + "Zinn", + "Zinnes", + "Zins", + "Zipah", + "Zipnick", + "Zippel", + "Zippora", + "Zipporah", + "Zirkle", + "Zischke", + "Zita", + "Zitah", + "Zitella", + "Zitvaa", + "Ziwot", + "Zoa", + "Zoara", + "Zoarah", + "Zoba", + "Zobe", + "Zobias", + "Zobkiw", + "Zoe", + "Zoeller", + "Zoellick", + "Zoes", + "Zoha", + "Zohar", + "Zohara", + "Zoi", + "Zoie", + "Zoila", + "Zoilla", + "Zola", + "Zoldi", + "Zoller", + "Zollie", + "Zolly", + "Zolnay", + "Zolner", + "Zoltai", + "Zonda", + "Zondra", + "Zonnya", + "Zora", + "Zorah", + "Zorana", + "Zorina", + "Zorine", + "Zosema", + "Zosi", + "Zosima", + "Zoubek", + "Zrike", + "Zsa", + "Zsa Zsa", + "Zsazsa", + "Zsolway", + "Zubkoff", + "Zucker", + "Zuckerman", + "Zug", + "Zulch", + "Zuleika", + "Zulema", + "Zullo", + "Zumstein", + "Zumwalt", + "Zurek", + "Zurheide", + "Zurkow", + "Zurn", + "Zusman", + "Zuzana", + "Zwart", + "Zweig", + "Zwick", + "Zwiebel", + "Zysk" +] diff --git a/common/name_generator.gd.uid b/common/name_generator.gd.uid new file mode 100644 index 00000000..51bfa0e1 --- /dev/null +++ b/common/name_generator.gd.uid @@ -0,0 +1 @@ +uid://fr6cpihs6dcx diff --git a/common/nodes/choice_node/choice_node.gd b/common/nodes/choice_node/choice_node.gd new file mode 100644 index 00000000..3d363f6a --- /dev/null +++ b/common/nodes/choice_node/choice_node.gd @@ -0,0 +1,117 @@ +class_name ChoiceNode extends InspectableNode + +const MAX_CHOICES: int = 8 + +var _external_options: Array[Dictionary] = [] + + +func initialize_properties() -> void: + define_main_property("choice", "context", false, null, {"export": false, "exposed": true}) + define_property( + "choices", + [], + "collection", + { + "collection": "option", + "exposed": false, + "visible_in_graph": true, + }, + ) + # Listen for connection changes on the choices property + var choices_prop: Property = get_property("choices") + if choices_prop: + choices_prop.connection_changed.connect(_on_choices_connection_changed) + + +func get_type() -> String: + return "choice" + + +func get_color() -> Color: + return Color("e89145") + + +func _on_property_changed(pname: String, _old_value: Variant, _new_value: Variant) -> void: + if pname == "choices": + rebuild_preview() + + +func _on_choices_connection_changed() -> void: + _sync_external_options() + rebuild_preview() + + +func get_external_list_items(property_name: String) -> Array[Dictionary]: + if property_name == "choices": + _sync_external_options() + return _external_options + return [] + + +## Synchronize external (read-only) option items from connected OptionNodes. +func _sync_external_options() -> void: + _external_options.clear() + var choices_prop: Property = get_property("choices") + if not choices_prop: + return + + for conn: Dictionary in choices_prop.connected_from: + var source_node_id: String = conn.get("node_id", "") + if source_node_id.is_empty(): + continue + # Find the source node in the storyline + var source_node: InspectableNode = _find_node_by_id(source_node_id) + if not source_node or not (source_node is OptionNode): + continue + var ext_option: Dictionary = { + "external": true, + "source_node_id": source_node_id, + "name": _get_option_node_name(source_node), + "text": source_node.get_property_value("text"), + "enabled": source_node.get_property_value("enabled"), + "one_shot": source_node.get_property_value("one_shot"), + "condition": source_node.get_property_value("condition"), + } + _external_options.append(ext_option) + + +func _find_node_by_id(node_id: String) -> InspectableNode: + if storyline_id.is_empty(): + return null + var storyline: StorylineDocument = ProjectManager.current_project.get_storyline(storyline_id) + if not storyline: + return null + return storyline.get_node(node_id) + + +func _get_option_node_name(node: InspectableNode) -> String: + var text_val: Variant = node.get_property_value("text") + if text_val is Dictionary: + var text_dict: Dictionary = text_val + if text_dict.has("value"): + var t: Variant = text_dict.get("value", "") + if not str(t).is_empty(): + return str(t) + return str(node.get_property_value("id")) + + +## Returns the total number of choices (internal + external). +func get_total_choice_count() -> int: + var internal_count: int = 0 + var choices_val: Variant = get_property_value("choices") + if choices_val is Array: + var choices_arr: Array = choices_val + internal_count = choices_arr.size() + return internal_count + _external_options.size() + + +## Returns the combined list of internal choice data + external option data. +func get_all_choices() -> Array: + var result: Array = [] + var choices_val: Variant = get_property_value("choices") + if choices_val is Array: + var choices_arr: Array = choices_val + result.append_array(choices_arr) + for ext: Dictionary in _external_options: + result.append(ext) + return result diff --git a/common/nodes/choice_node/choice_node.gd.uid b/common/nodes/choice_node/choice_node.gd.uid new file mode 100644 index 00000000..6868add6 --- /dev/null +++ b/common/nodes/choice_node/choice_node.gd.uid @@ -0,0 +1 @@ +uid://b0puytkqisvv7 diff --git a/common/nodes/choice_node/index.gd b/common/nodes/choice_node/index.gd new file mode 100644 index 00000000..cbc42845 --- /dev/null +++ b/common/nodes/choice_node/index.gd @@ -0,0 +1,22 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("choice_node.gd") + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "choice", + "type": ObjectType.NODE, + "display_name": "Choice", + "category": "Flow", + "script": NODE_SCRIPT, + "color": Color("e89145") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/choice_node/index.gd.uid b/common/nodes/choice_node/index.gd.uid new file mode 100644 index 00000000..57661286 --- /dev/null +++ b/common/nodes/choice_node/index.gd.uid @@ -0,0 +1 @@ +uid://df2o1g07s8iyi diff --git a/common/nodes/node_bucket.gd b/common/nodes/node_bucket.gd new file mode 100644 index 00000000..a55d9ab5 --- /dev/null +++ b/common/nodes/node_bucket.gd @@ -0,0 +1,46 @@ +extends Bucket + +const DEFAULT_NODES_LOCATION: String = "res://common/nodes/" + + +func create_node(descriptor_name: String, history: CommandManager) -> InspectableNode: + var descriptor: GraphNodeDescriptor = get_descriptor(descriptor_name) as GraphNodeDescriptor + if descriptor: + return descriptor.instantiate_node(history) + push_warning("No node descriptor registered for '%s'." % descriptor_name) + return null + + +func _search_types() -> void: + var directories: Array = DirAccess.get_directories_at(DEFAULT_NODES_LOCATION) + for dir_path: String in directories: + var index_path: String = DEFAULT_NODES_LOCATION.path_join(dir_path).path_join("index.gd") + if not FileAccess.file_exists(index_path): + continue + var index_script: GDScript = load(index_path) + if index_script == null: + push_warning("Failed to load node indexer at %s" % index_path) + continue + var indexer: MonologueIndexer = index_script.new() + var descriptor: GraphNodeDescriptor = _descriptor_from_indexer(indexer) + if descriptor: + register_descriptor(descriptor) + + +func _descriptor_from_indexer(indexer: MonologueIndexer) -> GraphNodeDescriptor: + var metadata: Dictionary = indexer.get_metadata() + if metadata.get("type") != MonologueIndexer.ObjectType.NODE: + return null + var descriptor_name: String = metadata.get("name", "") + if descriptor_name.is_empty(): + return null + var script_resource: Script + if indexer.has_method("get_node_script"): + script_resource = indexer.call("get_node_script") + elif metadata.has("script"): + var raw_script: GDScript = metadata.get("script") + script_resource = raw_script + if script_resource == null: + push_warning("Indexer for '%s' missing script reference." % descriptor_name) + return null + return GraphNodeDescriptor.new(descriptor_name, script_resource, metadata) diff --git a/common/nodes/node_bucket.gd.uid b/common/nodes/node_bucket.gd.uid new file mode 100644 index 00000000..ee32760d --- /dev/null +++ b/common/nodes/node_bucket.gd.uid @@ -0,0 +1 @@ +uid://clgkk52nqoxyj diff --git a/common/nodes/node_descriptor.gd b/common/nodes/node_descriptor.gd new file mode 100644 index 00000000..9a0e66eb --- /dev/null +++ b/common/nodes/node_descriptor.gd @@ -0,0 +1,33 @@ +class_name GraphNodeDescriptor extends BucketDescriptor + +var node_script: GDScript +var icon: Texture2D +var color: Color = Color.WHITE + + +func _init(p_name: String = "", p_script: Script = null, metadata: Dictionary = {}) -> void: + super._init(p_name, metadata) + node_script = p_script + + var raw_icon: Variant = metadata.get("icon") + if raw_icon is Texture2D: + icon = raw_icon + + var raw_color: Variant = metadata.get("color") + if raw_color is Color: + color = raw_color + + +func instantiate_node(history: CommandManager) -> InspectableNode: + if node_script == null: + push_warning("Descriptor '%s' missing script for instantiation." % name) + return null + var instance: InspectableNode = node_script.new(history) + return instance + + +func to_metadata() -> Dictionary: + var base: Dictionary = super.to_metadata() + base["color"] = color + base["icon"] = icon + return base diff --git a/common/nodes/node_descriptor.gd.uid b/common/nodes/node_descriptor.gd.uid new file mode 100644 index 00000000..13620592 --- /dev/null +++ b/common/nodes/node_descriptor.gd.uid @@ -0,0 +1 @@ +uid://c1fyc8bo63wvk diff --git a/common/nodes/option_node/index.gd b/common/nodes/option_node/index.gd new file mode 100644 index 00000000..bfc90515 --- /dev/null +++ b/common/nodes/option_node/index.gd @@ -0,0 +1,24 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("option_node.gd") +const ICON_PATH: String = "res://ui/assets/icons/action.svg" + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "option", + "type": ObjectType.NODE, + "display_name": "Option", + "category": "Flow", + "script": NODE_SCRIPT, + "icon": ICON_PATH, + "color": Color("e89145") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/option_node/index.gd.uid b/common/nodes/option_node/index.gd.uid new file mode 100644 index 00000000..585e10fd --- /dev/null +++ b/common/nodes/option_node/index.gd.uid @@ -0,0 +1 @@ +uid://d03dms5els2nw diff --git a/common/nodes/option_node/option_node.gd b/common/nodes/option_node/option_node.gd new file mode 100644 index 00000000..1b84f4c6 --- /dev/null +++ b/common/nodes/option_node/option_node.gd @@ -0,0 +1,15 @@ +class_name OptionNode extends InspectableNode + + +func initialize_properties() -> void: + define_main_property("option", "option", false, null, {"export": true, "exposed": false}) + define_property("text", {}, "translatable", {"multiline": true}) + define_property("correspondent", "", "dropdown", {"source": "characters", "visible_in_graph": false}) + define_property("enabled", true, "bool", {"visible_in_graph": false}, "Advanced") + define_property("one_shot", false, "bool", {"visible_in_graph": false}, "Advanced") + define_property("enable_condition", false, "bool", {"visible_in_graph": false}, "Advanced") + define_property("condition", {}, "condition", {"visible_in_graph": false}, "Advanced") + + +func get_type() -> String: + return "option" diff --git a/common/nodes/option_node/option_node.gd.uid b/common/nodes/option_node/option_node.gd.uid new file mode 100644 index 00000000..586910eb --- /dev/null +++ b/common/nodes/option_node/option_node.gd.uid @@ -0,0 +1 @@ +uid://cdf6p5e5fypd7 diff --git a/common/nodes/root_node/index.gd b/common/nodes/root_node/index.gd new file mode 100644 index 00000000..de9eae0c --- /dev/null +++ b/common/nodes/root_node/index.gd @@ -0,0 +1,24 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("uid://csx2ec4ra5m8k") +const ICON_PATH: String = "res://ui/assets/icons/root.svg" + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "root", + "type": ObjectType.NODE, + "display_name": "Root", + "category": "Flow", + "script": NODE_SCRIPT, + "icon": ICON_PATH, + "color": Color("ffffff") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/root_node/index.gd.uid b/common/nodes/root_node/index.gd.uid new file mode 100644 index 00000000..a6d662ca --- /dev/null +++ b/common/nodes/root_node/index.gd.uid @@ -0,0 +1 @@ +uid://c5djdhldg6qbu diff --git a/common/nodes/root_node/root_node.gd b/common/nodes/root_node/root_node.gd new file mode 100644 index 00000000..e0839976 --- /dev/null +++ b/common/nodes/root_node/root_node.gd @@ -0,0 +1,9 @@ +class_name RootNode extends InspectableNode + + +func initialize_properties() -> void: + define_main_property("root", "context", false, null, {"exposed": false}) + + +func get_type() -> String: + return "root" diff --git a/nodes/root_node/root_node.gd.uid b/common/nodes/root_node/root_node.gd.uid similarity index 100% rename from nodes/root_node/root_node.gd.uid rename to common/nodes/root_node/root_node.gd.uid diff --git a/common/nodes/sentence_node/index.gd b/common/nodes/sentence_node/index.gd new file mode 100644 index 00000000..4e1a7d93 --- /dev/null +++ b/common/nodes/sentence_node/index.gd @@ -0,0 +1,22 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("uid://bm2we18ivulms") + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "sentence", + "type": ObjectType.NODE, + "display_name": "Sentence", + "category": "Narration", + "script": NODE_SCRIPT, + "color": Color("af85fdff") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/sentence_node/index.gd.uid b/common/nodes/sentence_node/index.gd.uid new file mode 100644 index 00000000..7b83af61 --- /dev/null +++ b/common/nodes/sentence_node/index.gd.uid @@ -0,0 +1 @@ +uid://b165nmgmvcms diff --git a/common/nodes/sentence_node/sentence_node.gd b/common/nodes/sentence_node/sentence_node.gd new file mode 100644 index 00000000..e6be47b5 --- /dev/null +++ b/common/nodes/sentence_node/sentence_node.gd @@ -0,0 +1,13 @@ +class_name SentenceNode extends InspectableNode + + +func initialize_properties() -> void: + define_main_property("sentence", "context", false, null, {"export": true}) + define_property("line", {}, "translatable", { PropertySettings.KEY_MULTILINE: true }) + define_property("speaker", "", "dropdown", {"source": "characters"}, "Speaker") + define_property("display_name", {}, "translatable", {"visible_in_graph": false}, "Speaker") + define_property("voiceline", "", "file", {"visible_in_graph": false}, "Speaker") + + +func get_type() -> String: + return "sentence" diff --git a/nodes/sentence_node/sentence_node.gd.uid b/common/nodes/sentence_node/sentence_node.gd.uid similarity index 100% rename from nodes/sentence_node/sentence_node.gd.uid rename to common/nodes/sentence_node/sentence_node.gd.uid diff --git a/common/nodes/text_node/index.gd b/common/nodes/text_node/index.gd new file mode 100644 index 00000000..fb74c619 --- /dev/null +++ b/common/nodes/text_node/index.gd @@ -0,0 +1,22 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("uid://8hoiik6d0qim") + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "text", + "type": ObjectType.NODE, + "display_name": "Text", + "category": "Narration", + "script": NODE_SCRIPT, + "color": Color("af85fdff") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/text_node/index.gd.uid b/common/nodes/text_node/index.gd.uid new file mode 100644 index 00000000..97e0fbb6 --- /dev/null +++ b/common/nodes/text_node/index.gd.uid @@ -0,0 +1 @@ +uid://b67i013h21w4e diff --git a/common/nodes/text_node/text_node.gd b/common/nodes/text_node/text_node.gd new file mode 100644 index 00000000..1a5dd71e --- /dev/null +++ b/common/nodes/text_node/text_node.gd @@ -0,0 +1,9 @@ +class_name TextNode extends InspectableNode + + +func initialize_properties() -> void: + define_main_property("text", "text", true, "", {"exposed": false}) + + +func get_type() -> String: + return "text" diff --git a/common/nodes/text_node/text_node.gd.uid b/common/nodes/text_node/text_node.gd.uid new file mode 100644 index 00000000..f33d766f --- /dev/null +++ b/common/nodes/text_node/text_node.gd.uid @@ -0,0 +1 @@ +uid://8hoiik6d0qim diff --git a/common/nodes/zoo_node/index.gd b/common/nodes/zoo_node/index.gd new file mode 100644 index 00000000..72bfbc1d --- /dev/null +++ b/common/nodes/zoo_node/index.gd @@ -0,0 +1,22 @@ +extends MonologueIndexer + +const NODE_SCRIPT := preload("uid://hoqcsoq3aicf") + + +func get_scene() -> PackedScene: + return null + + +func get_metadata() -> Dictionary: + return { + "name": "zoo", + "type": ObjectType.NODE, + "display_name": "Zoo", + "category": "_technical", + "script": NODE_SCRIPT, + "color": Color("af85fdff") + } + + +func get_node_script() -> Script: + return NODE_SCRIPT diff --git a/common/nodes/zoo_node/index.gd.uid b/common/nodes/zoo_node/index.gd.uid new file mode 100644 index 00000000..fd289604 --- /dev/null +++ b/common/nodes/zoo_node/index.gd.uid @@ -0,0 +1 @@ +uid://b8cq4ohmsg3xr diff --git a/common/nodes/zoo_node/zoo_node.gd b/common/nodes/zoo_node/zoo_node.gd new file mode 100644 index 00000000..8d233e73 --- /dev/null +++ b/common/nodes/zoo_node/zoo_node.gd @@ -0,0 +1,111 @@ +class_name ZooNode extends InspectableNode + + +func initialize_properties() -> void: + define_main_property("zoo", "context", false, null, {"exposed": false, "export": true}) + + # Text + define_property("text", "", "text", {}, "Text") + define_property("textarea", "", "textarea", {}, "Text") + define_property( + "translatable", + {"en": ""}, + "translatable", + {}, + "Text" + ) + define_property( + "translatable_multiline", + {"en": ""}, + "translatable", + {PropertySettings.KEY_MULTILINE: true}, + "Text" + ) + + # Values + define_property("bool", false, "bool", {}, "Values") + define_property("color2", "#3b5dc9", "color", {}, "Values") + define_property( + "dynamic_dropdown", + "string", + "dropdown", + { + "required": true, + "options": ["bool", "string", "int", "float"] + }, + "Values" + ) + define_property( + "dynamic", + "abc", + "dynamic", + { + "case_property": "dynamic_dropdown", + "cases": { + "bool": {"type": "bool", "default": false}, + "string": {"type": "text", "default": "", "coerce": "string"}, + "int": {"type": "int", "default": 0, "coerce": "int"}, + "float": {"type": "float", "default": 0.0, "coerce": "float"} + } + }, + "Values" + ) + define_property("float", 10.0, "float", {}, "Values") + define_property("int", 10, "int", {}, "Values") + define_property("slider", 10.0, "slider", {}, "Values") + define_property("vector2", [0.0, 0.0], "vector2", {}, "Values") + define_property("bezier", [0.25, 0.10, 0.25, 1.0], "bezier", {}, "Values") + + + # References + define_property("file", "", "file", {}, "References") + define_property( + "dropdown", + "", + "dropdown", + {"source": "characters"}, + "References" + ) + + # List + define_property( + "collection", + [], + "collection", + {PropertySettings.KEY_COLLECTION: "characters"}, + "List" + ) + define_property( + "collection_dropdown", + "", + "dropdown", + {PropertySettings.KEY_SOURCE: "self:collection"}, + "List" + ) + define_property( + "list", + [], + "list", + {PropertySettings.KEY_ITEM_TYPE: "text"}, + "List" + ) + define_property( + "list_dropdown", + "", + "dropdown", + {PropertySettings.KEY_SOURCE: "self:list"}, + "List" + ) + + # Condition + define_property( + "condition", + {}, + "condition", + {}, + "Condition" + ) + + +func get_type() -> String: + return "zoo" diff --git a/common/nodes/zoo_node/zoo_node.gd.uid b/common/nodes/zoo_node/zoo_node.gd.uid new file mode 100644 index 00000000..ceccc041 --- /dev/null +++ b/common/nodes/zoo_node/zoo_node.gd.uid @@ -0,0 +1 @@ +uid://hoqcsoq3aicf diff --git a/common/property.gd b/common/property.gd new file mode 100644 index 00000000..70ea14e9 --- /dev/null +++ b/common/property.gd @@ -0,0 +1,221 @@ +class_name Property extends RefCounted + +signal value_changed(old_value: Variant, new_value: Variant) +signal connection_changed() + +var name: String = "" +var value: Variant = 0 +var type: String = "" +## Base settings: defaults + descriptor defaults + constructor overrides. Read-only after init. +var _base_settings: Dictionary = {} +## Runtime overrides set by the user (serialized as "_editor_settings" in JSON). +var _overrides: Dictionary = {} +var descriptor: FieldDescriptor +var _bindings: Array[FieldBinding] = [] + +const DEFAULT_SETTINGS: Dictionary = { + "visible_in_graph": true, + "visible_in_inspector": true, + "editable": true, + "read_only": false, + "exposable": true, + "exposed": false, + "export": false, + "category": "General", + "label": "", +} + +## Tracks input connections to this property. +## Each entry: {"node_id": String, "property_name": String, "item_id"?: String} +var connected_from: Array[Dictionary] = [] + +## Tracks output connections from this property. +## Each entry: {"node_id": String, "property_name": String, "item_id"?: String} +var connected_to: Array[Dictionary] = [] + +## Property Settings: +## - visible_in_graph: Whether property shows as a row in graph node view +## - visible_in_inspector: Whether property shows in inspector panel +## - editable: Whether property value can be edited (enforced in inspector) +## - exposed: Whether property has input port (left side) for receiving connections +## - export: Whether property has output port (right side) for sending connections +## - is_main_property: Whether this is the main connectable property of the node + + +func _init(pname: String, pvalue: Variant, ptype: String, psettings: Dictionary = {}) -> void: + name = pname + value = pvalue + if pvalue is Callable: + value = pvalue.call() + + type = ptype + descriptor = FieldBucket.get_descriptor(ptype) + _base_settings = DEFAULT_SETTINGS.duplicate(true) + if descriptor and descriptor.default_settings: + _base_settings.merge(descriptor.default_settings, true) + if psettings: + _base_settings.merge(psettings, true) + if not _base_settings.get("category"): + _base_settings["category"] = DEFAULT_SETTINGS["category"] + if _base_settings.get("label", "") == "": + _base_settings.erase("label") + + +func bind_field(field: Field, target_owner: InspectableObject = null, deferred: bool = false) -> FieldBinding: + if deferred: + bind_field.call_deferred(field, target_owner, false) + return + + if not is_instance_valid(field) or not field.is_inside_tree(): + Log.error("Failed to bind field.") + return null + + var binding: FieldBinding = FieldBucket.bind(self, field, target_owner) + if binding: + _bindings.append(binding) + return binding + + +func set_value(new_value: Variant) -> void: + if typeof(value) == typeof(new_value) and value == new_value: + return + var old_value: Variant = value + value = new_value + value_changed.emit(old_value, new_value) + + +func get_value() -> Variant: + return value + + +func set_settings_value(sname: String, svalue: Variant) -> void: + _overrides[sname] = svalue + + +func get_settings() -> Dictionary: + var merged_settings: Dictionary = _overrides.duplicate(true) + merged_settings.merge(_base_settings) + return merged_settings + + +func has_settings(skey: Variant) -> bool: + return get_settings().has(skey) + + +func get_settings_value(skey: String, default_value: Variant = null) -> Variant: + return get_settings().get(skey, default_value) + + +func get_display_name() -> String: + var label: String = get_settings_value("label", "") + if label.is_empty(): + label = name + return Util.to_readable_name(label) + + +func get_category() -> String: + return get_settings_value("category", "General") + + +func is_input_connected() -> bool: + return connected_from.size() > 0 + + +func is_output_connected() -> bool: + return connected_to.size() > 0 + + +func is_port_connected() -> bool: + return is_input_connected() or is_output_connected() + + +## Returns true if this property should appear as a row in the graph node view. +## A property is visible when visible_in_graph is true, or it has at least one port. +func is_visible_in_graph() -> bool: + var has_input: bool = get_settings_value("exposed", false) == true + var has_output: bool = get_settings_value("export", false) == true + if not get_settings_value("visible_in_graph", true) and not (has_input or has_output): + return false + return true + + +func add_connection_from(node_id: String, property_name: String) -> void: + var conn: Dictionary = {"node_id": node_id, "property_name": property_name} + if conn not in connected_from: + connected_from.append(conn) + connection_changed.emit() + + +func add_connection_to(node_id: String, property_name: String) -> void: + var conn: Dictionary = {"node_id": node_id, "property_name": property_name} + if conn not in connected_to: + connected_to.append(conn) + connection_changed.emit() + + +func remove_connection_from(node_id: String, property_name: String) -> void: + var old_size: int = connected_from.size() + connected_from.assign(connected_from.filter( + func(c: Dictionary) -> bool: return not (c["node_id"] == node_id and c["property_name"] == property_name) + )) + if connected_from.size() != old_size: + connection_changed.emit() + + +func remove_connection_to(node_id: String, property_name: String) -> void: + var old_size: int = connected_to.size() + connected_to.assign(connected_to.filter( + func(c: Dictionary) -> bool: return not (c["node_id"] == node_id and c["property_name"] == property_name) + )) + if connected_to.size() != old_size: + connection_changed.emit() + + +func clear_connections() -> void: + var had_connections: bool = not connected_from.is_empty() or not connected_to.is_empty() + connected_from.clear() + connected_to.clear() + if had_connections: + connection_changed.emit() + + +func refresh_bindings() -> void: + _bindings = _bindings.filter(func(binding: FieldBinding) -> bool: return binding and binding.is_active()) + for binding: FieldBinding in _bindings: + binding.refresh() + + +func get_descriptor() -> FieldDescriptor: + if descriptor == null: + descriptor = FieldBucket.get_descriptor(type) + return descriptor + + +func _to_dict() -> Variant: + var dict: Dictionary = { + "value": get_value(), + } + + if not connected_from.is_empty(): + dict["from_node"] = connected_from + if not connected_to.is_empty(): + dict["to_node"] = connected_to + + if not _overrides.is_empty(): + dict["_editor_settings"] = _overrides + + return dict + + +func _from_dict(raw: Dictionary) -> void: + if raw is not Dictionary: + return + + value = raw.get("value", value) + _overrides = raw.get("_editor_settings", _overrides) + if raw.get("from_node"): + var from_arr: Array = raw.get("from_node", []) + connected_from.assign(from_arr) + if raw.get("to_node"): + var to_arr: Array = raw.get("to_node", []) + connected_to.assign(to_arr) diff --git a/common/property.gd.uid b/common/property.gd.uid new file mode 100644 index 00000000..a56a8f95 --- /dev/null +++ b/common/property.gd.uid @@ -0,0 +1 @@ +uid://cvxcfitjpar4y diff --git a/common/property_settings.gd b/common/property_settings.gd new file mode 100644 index 00000000..a2b6a3b1 --- /dev/null +++ b/common/property_settings.gd @@ -0,0 +1,98 @@ +## Centralizes all known Property settings as typed KEY_* constants. +## Use these constants instead of magic strings to avoid typos and ease refactoring. +class_name PropertySettings + +# Display + +## Whether the property appears as a row inside the graph node view. +const KEY_VISIBLE_IN_GRAPH: String = "visible_in_graph" +## Whether the property appears in the inspector panel. +const KEY_VISIBLE_IN_INSPECTOR: String = "visible_in_inspector" +## Inspector category this property belongs to (default: "General"). +const KEY_CATEGORY: String = "category" +## Human-readable label override. Falls back to the property name when empty. +const KEY_LABEL: String = "label" +## Tooltip text shown on the property label in the inspector. +const KEY_TOOLTIP: String = "tooltip" +## Whether the property container expands horizontally (default: true). +const KEY_EXPAND: String = "expand" +## Whether the property container renders without a visible background panel. +const KEY_FLAT: String = "flat" + +# Editing + +## Whether the property is editable in the inspector. +## If false (and not read_only / not a port), the property is hidden entirely. +const KEY_EDITABLE: String = "editable" +## Whether the property is visible in the inspector but cannot be modified. +## The field is shown in a disabled/frozen state. Takes precedence over KEY_EDITABLE +## for visibility: a read_only property is always shown even if editable is false. +const KEY_READ_ONLY: String = "read_only" +## Whether the value must be unique among sibling items in the same ListField. +## Validated on commit. Typically applied to the "name" property of collection items. +const KEY_UNIQUE: String = "unique" +## Whether the value is required (non-empty / non-zero). Validated on commit. +const KEY_REQUIRED: String = "required" +## Whether the list item that carries this property is protected from deletion. +const KEY_PROTECT: String = "protect" + +# Validation + +## Dictionary of field-type-specific validation rules applied during commit. +## Example: { "min_length": 1, "max_length": 64 } +const KEY_VALIDATION: String = "validation" + +# Ports + +## Whether an input port (left side) is shown, allowing incoming connections. +const KEY_EXPOSED: String = "exposed" +## Whether an output port (right side) is shown, allowing outgoing connections. +const KEY_EXPORT: String = "export" +## Whether the user can toggle the input port via the inspector. +const KEY_EXPOSABLE: String = "exposable" +## Marks this as the primary connectable context property of the node. +const KEY_IS_MAIN_PROPERTY: String = "is_main_property" + +# Field-type-specific + +## (list) Name of the collection registry to query for list items. +const KEY_COLLECTION: String = "collection" +## (list) Field type used by each standard list item. +const KEY_ITEM_TYPE: String = "item_type" +## (port) Visual size of the port slot ("normal" or "large"). +const KEY_PORT_SIZE: String = "port_size" +## (dropdown) Static array of option strings offered to the user. +const KEY_OPTIONS: String = "options" +## (dropdown) Dynamic source: a collection name or "self:" path. +const KEY_SOURCE: String = "source" +## (translatable) When true, renders a multi-line TextEdit instead of a LineEdit. +const KEY_MULTILINE: String = "multiline" +## (text / translatable) Placeholder ghost text shown when the field is empty. +const KEY_PLACEHOLDER: String = "placeholder" +## (textarea) Number of visible text rows used to compute minimum height. +const KEY_ROWS: String = "rows" +## (dynamic) Name of the sibling property whose value selects the active variant. +const KEY_CASE_PROPERTY: String = "case_property" +## (dynamic) Mapping from case-value string to { type, default, coerce? } dict. +const KEY_CASES: String = "cases" +## (file) Array of file filter strings passed to the file dialog (e.g. ["*.png", "*.jpg"]). +## An empty array means no filter (all files are shown). +const KEY_FILE_FILTERS: String = "file_filters" + +# Defaults + +## Default values for all base settings. Field-type-specific keys are omitted +## because they only apply to specific field types and have no meaningful global default. +const DEFAULTS: Dictionary = { + KEY_VISIBLE_IN_GRAPH: true, + KEY_VISIBLE_IN_INSPECTOR: true, + KEY_EDITABLE: true, + KEY_READ_ONLY: false, + KEY_EXPOSABLE: true, + KEY_EXPOSED: false, + KEY_EXPORT: false, + KEY_CATEGORY: "General", + KEY_LABEL: "", + KEY_EXPAND: true, + KEY_FLAT: false, +} diff --git a/common/property_settings.gd.uid b/common/property_settings.gd.uid new file mode 100644 index 00000000..61c16478 --- /dev/null +++ b/common/property_settings.gd.uid @@ -0,0 +1 @@ +uid://0lbmdmvci58 diff --git a/common/ui/advanced_hbox_container.gd b/common/ui/advanced_hbox_container.gd new file mode 100644 index 00000000..605648e8 --- /dev/null +++ b/common/ui/advanced_hbox_container.gd @@ -0,0 +1,103 @@ +class_name AdvancedHBoxContainer extends Container + +@export var ratio: Array[float] = [1.0]: + set(v): + ratio = v + queue_sort() + +@export var force_ratio: bool = false: + set(v): + force_ratio = v + queue_sort() + +func _notification(what: int) -> void: + if what == NOTIFICATION_SORT_CHILDREN: + _sort() + elif what == NOTIFICATION_THEME_CHANGED: + queue_sort() + +func _sort() -> void: + var children: Array[Control] = [] + for child in get_children(): + if child is Control and child.visible: + children.append(child) + + if children.is_empty(): + return + + var sep: int = get_theme_constant("separation", "HBoxContainer") + var available: float = size.x - sep * (children.size() - 1) + + var ratios: Array[float] = [] + for i in children.size(): + if i < ratio.size(): + ratios.append(maxf(ratio[i], 0.0)) + else: + ratios.append(maxf(ratio[-1] if not ratio.is_empty() else 1.0, 0.0)) + + var total: float = 0.0 + for r: float in ratios: + total += r + if total <= 0.0: + return + + var final_sizes: Array[float] = [] + final_sizes.resize(children.size()) + + if force_ratio: + for i in children.size(): + final_sizes[i] = ratios[i] / total * available + else: + var locked: Array[bool] = [] + locked.resize(children.size()) + for i in children.size(): + locked[i] = false + + var remaining_space: float = available + var remaining_ratio: float = total + + var changed: bool = true + while changed: + changed = false + for i in children.size(): + if locked[i]: + continue + var allocated: float = ratios[i] / remaining_ratio * remaining_space + var min_w: float = children[i].get_combined_minimum_size().x + if allocated < min_w: + final_sizes[i] = min_w + remaining_space -= min_w + remaining_ratio -= ratios[i] + locked[i] = true + changed = true + + for i in children.size(): + if not locked[i]: + final_sizes[i] = ratios[i] / remaining_ratio * remaining_space if remaining_ratio > 0.0 else 0.0 + + var x_offset: float = 0.0 + for i in children.size(): + children[i].position = Vector2(x_offset, 0.0) + children[i].set_size.call_deferred(Vector2(final_sizes[i], size.y)) + x_offset += final_sizes[i] + sep + +func _get_minimum_size() -> Vector2: + var children: Array[Control] = [] + for child in get_children(): + if child is Control and child.visible: + children.append(child) + + if children.is_empty(): + return Vector2.ZERO + + var sep: int = get_theme_constant("separation", "HBoxContainer") + var min_w: float = 0.0 + var max_h: float = 0.0 + for child: Control in children: + if not force_ratio: + min_w += child.get_combined_minimum_size().x + max_h = maxf(max_h, child.get_combined_minimum_size().y) + + if not force_ratio: + min_w += sep * (children.size() - 1) + return Vector2(min_w, max_h) diff --git a/common/ui/advanced_hbox_container.gd.uid b/common/ui/advanced_hbox_container.gd.uid new file mode 100644 index 00000000..77efd4dd --- /dev/null +++ b/common/ui/advanced_hbox_container.gd.uid @@ -0,0 +1 @@ +uid://bq6d80i3hqu41 diff --git a/common/ui/button_close.tscn b/common/ui/button_close.tscn new file mode 100644 index 00000000..83938832 --- /dev/null +++ b/common/ui/button_close.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://dspmmme0jspdx"] + +[ext_resource type="Texture2D" uid="uid://kk5em170gn2b" path="res://ui/assets/icons/ui_close.svg" id="1_ai8vd"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tlp5i"] + +[node name="Button" type="Button"] +anchors_preset = -1 +anchor_right = 0.017 +anchor_bottom = 0.03 +offset_right = 0.199999 +theme_override_constants/icon_max_width = 0 +theme_override_styles/normal = SubResource("StyleBoxEmpty_tlp5i") +icon = ExtResource("1_ai8vd") +flat = true diff --git a/common/ui/button_delete.tscn b/common/ui/button_delete.tscn new file mode 100644 index 00000000..2b52e5bc --- /dev/null +++ b/common/ui/button_delete.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://dfwf55ovgwir3"] + +[ext_resource type="Texture2D" uid="uid://hmjhxdsk3pwj" path="res://ui/assets/icons/trash.svg" id="1_xsgmh"] + +[node name="DeleteButton" type="Button"] +custom_minimum_size = Vector2(29, 29) +offset_right = 29.0 +offset_bottom = 29.0 +size_flags_vertical = 0 +theme_type_variation = &"ButtonWarning" +icon = ExtResource("1_xsgmh") +icon_alignment = 1 +expand_icon = true diff --git a/common/ui/buttons/close_button.tscn b/common/ui/buttons/close_button.tscn deleted file mode 100644 index cfbcc8d2..00000000 --- a/common/ui/buttons/close_button.tscn +++ /dev/null @@ -1,11 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dspmmme0jspdx"] - -[ext_resource type="Texture2D" uid="uid://c7vdr4e0mxst6" path="res://ui/assets/icons/close_icon.png" id="1_jkx7d"] - -[node name="CloseButton" type="TextureButton"] -offset_right = 16.0 -offset_bottom = 16.0 -size_flags_vertical = 3 -mouse_default_cursor_shape = 2 -texture_normal = ExtResource("1_jkx7d") -stretch_mode = 3 diff --git a/common/ui/buttons/delete_button.tscn b/common/ui/buttons/delete_button.tscn deleted file mode 100644 index e2a40158..00000000 --- a/common/ui/buttons/delete_button.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dfwf55ovgwir3"] - -[ext_resource type="Texture2D" uid="uid://hmjhxdsk3pwj" path="res://ui/assets/icons/trash.svg" id="1_xsgmh"] - -[node name="DeleteButton" type="Button"] -custom_minimum_size = Vector2(34, 29) -offset_right = 34.0 -offset_bottom = 29.0 -size_flags_vertical = 0 -theme_type_variation = &"ButtonWarning" -icon = ExtResource("1_xsgmh") -expand_icon = true diff --git a/common/ui/console/console.gd b/common/ui/console/console.gd new file mode 100644 index 00000000..ab154533 --- /dev/null +++ b/common/ui/console/console.gd @@ -0,0 +1,24 @@ +class_name Console extends PanelContainer + +@onready var rtl: RichTextLabel = %Log + + +func _ready() -> void: + EventBus.show_console.connect(_on_event_show_console) + Log.log_message.connect(_on_log_message) + + visible = ConfigManager.get_config("show_console") + + +func _on_event_show_console(_visible: bool) -> void: + visible = ConfigManager.get_config("show_console") + + +func _on_log_message(_message: String, bbcode_message: String) -> void: + rtl.append_text(bbcode_message) + rtl.newline() + + +func _on_close_button_pressed() -> void: + ConfigManager.set_config("show_console", false) + hide() diff --git a/common/ui/console/console.gd.uid b/common/ui/console/console.gd.uid new file mode 100644 index 00000000..f0853c4f --- /dev/null +++ b/common/ui/console/console.gd.uid @@ -0,0 +1 @@ +uid://deismshtx4vjv diff --git a/common/ui/console/console.tscn b/common/ui/console/console.tscn new file mode 100644 index 00000000..45b7c799 --- /dev/null +++ b/common/ui/console/console.tscn @@ -0,0 +1,56 @@ +[gd_scene format=3 uid="uid://dqdrf7wtegye6"] + +[ext_resource type="Script" uid="uid://deismshtx4vjv" path="res://common/ui/console/console.gd" id="1_td4wj"] +[ext_resource type="Texture2D" uid="uid://b0umav8s8l2qa" path="res://ui/assets/icons/cross.svg" id="2_s7pde"] + +[node name="Console" type="PanelContainer" unique_id=588975871] +anchors_preset = -1 +anchor_right = 0.95400006 +anchor_bottom = 0.4 +offset_right = -0.60009766 +script = ExtResource("1_td4wj") + +[node name="VBox" type="VBoxContainer" parent="." unique_id=267045498] +layout_mode = 2 + +[node name="Header" type="HBoxContainer" parent="VBox" unique_id=65218473] +layout_mode = 2 + +[node name="FileButton" type="Button" parent="VBox/Header" unique_id=936758374] +layout_mode = 2 +text = "File" + +[node name="ViewButton" type="Button" parent="VBox/Header" unique_id=961613032] +layout_mode = 2 +text = "View" + +[node name="EditButton" type="Button" parent="VBox/Header" unique_id=402743143] +layout_mode = 2 +text = "Edit +" + +[node name="Spacer" type="HBoxContainer" parent="VBox/Header" unique_id=2082345106] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="CloseButton" type="Button" parent="VBox/Header" unique_id=1972092265] +layout_mode = 2 +theme_type_variation = &"IconButton" +icon = ExtResource("2_s7pde") + +[node name="PanelContainer" type="PanelContainer" parent="VBox" unique_id=1470435110] +layout_mode = 2 +size_flags_vertical = 3 +theme_type_variation = &"ConsolePanel" + +[node name="Log" type="RichTextLabel" parent="VBox/PanelContainer" unique_id=428105729] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +focus_mode = 2 +bbcode_enabled = true +scroll_following = true +selection_enabled = true + +[connection signal="pressed" from="VBox/Header/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/common/ui/counter/counter.tscn b/common/ui/counter/counter.tscn new file mode 100644 index 00000000..40e3b960 --- /dev/null +++ b/common/ui/counter/counter.tscn @@ -0,0 +1,72 @@ +[gd_scene format=3 uid="uid://wiapsnoaoc44"] + +[ext_resource type="Script" uid="uid://4lke0olu80se" path="res://common/ui/counter/spinbox.gd" id="1_j7qhj"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_4hddc"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_107wb"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_jeakv"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_rwqsu"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_i3s76"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_a42se"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_x4l3i"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_vh5iv"] + +[sub_resource type="Texture2DRD" id="Texture2DRD_ae1hp"] + +[node name="Counter" type="PanelContainer" unique_id=472066151] +theme_type_variation = &"SpinBoxPanel" +script = ExtResource("1_j7qhj") + +[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=731064991] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="DecreaseButton" type="Button" parent="HBoxContainer" unique_id=2052931204] +custom_minimum_size = Vector2(31, 0) +layout_mode = 2 +theme_type_variation = &"SpinBoxButtonLeft" +text = "-" + +[node name="VSeparator" type="VSeparator" parent="HBoxContainer" unique_id=1344024937] +layout_mode = 2 + +[node name="SpinBox" type="SpinBox" parent="HBoxContainer" unique_id=27536238] +custom_minimum_size = Vector2(75, 0) +layout_mode = 2 +theme_override_constants/buttons_vertical_separation = 0 +theme_override_constants/field_and_buttons_separation = 0 +theme_override_constants/buttons_width = 0 +theme_override_constants/set_min_buttons_width_from_icons = 0 +theme_override_icons/updown = SubResource("Texture2DRD_4hddc") +theme_override_icons/up = SubResource("Texture2DRD_107wb") +theme_override_icons/up_hover = SubResource("Texture2DRD_jeakv") +theme_override_icons/up_pressed = SubResource("Texture2DRD_rwqsu") +theme_override_icons/up_disabled = SubResource("Texture2DRD_i3s76") +theme_override_icons/down = SubResource("Texture2DRD_a42se") +theme_override_icons/down_hover = SubResource("Texture2DRD_x4l3i") +theme_override_icons/down_pressed = SubResource("Texture2DRD_vh5iv") +theme_override_icons/down_disabled = SubResource("Texture2DRD_ae1hp") +alignment = 1 +update_on_text_changed = true +select_all_on_focus = true + +[node name="VSeparator2" type="VSeparator" parent="HBoxContainer" unique_id=1639739216] +layout_mode = 2 + +[node name="IncreaseButton" type="Button" parent="HBoxContainer" unique_id=1775304848] +custom_minimum_size = Vector2(31, 0) +layout_mode = 2 +theme_type_variation = &"SpinBoxButtonRight" +text = "+" + +[connection signal="pressed" from="HBoxContainer/DecreaseButton" to="." method="_on_decrease_button_pressed"] +[connection signal="focus_exited" from="HBoxContainer/SpinBox" to="." method="_on_focus_exited"] +[connection signal="value_changed" from="HBoxContainer/SpinBox" to="." method="_on_value_changed"] +[connection signal="pressed" from="HBoxContainer/IncreaseButton" to="." method="_on_increase_button_pressed"] diff --git a/common/ui/counter/spinbox.gd b/common/ui/counter/spinbox.gd new file mode 100644 index 00000000..e9edc6f4 --- /dev/null +++ b/common/ui/counter/spinbox.gd @@ -0,0 +1,57 @@ +extends PanelContainer + +signal value_changed(value: Variant) + +@export var as_integer: bool = true +@export var min_value: float = -9999999999 +@export var max_value: float = 9999999999 +@export var step: float = 1 +@export var suffix: String + +@onready var spin_box: SpinBox = $HBoxContainer/SpinBox + +var value: Variant: + get(): + return int(spin_box.value) if as_integer else spin_box.value + set(value): + spin_box.value = value + + +func _ready() -> void: + var line_edit: LineEdit = spin_box.get_line_edit() + line_edit.connect("focus_exited", _on_focus_exited) + line_edit.connect("text_submitted", _on_text_submitted) + line_edit.theme_type_variation = "SpinBoxLineEdit" + _update_settings() + + +func _update_settings() -> void: + spin_box.min_value = min_value + spin_box.max_value = max_value + spin_box.step = step + spin_box.suffix = suffix + + +func _on_focus_exited() -> void: + _on_text_submitted(int(spin_box.value) if as_integer else spin_box.value) + + +func _on_text_submitted(_new_value: Variant) -> void: + value_changed.emit(int(spin_box.value) if as_integer else spin_box.value) + + +func _on_value_changed(_value: float) -> void: + if as_integer: + value_changed.emit(int(value)) + else: + value_changed.emit(value) + + +func _on_decrease_button_pressed() -> void: + spin_box.value -= spin_box.step + _on_focus_exited.call_deferred() + + +func _on_increase_button_pressed() -> void: + spin_box.value += spin_box.step + _on_focus_exited.call_deferred() diff --git a/common/ui/custom_spinbox/custom_spinbox.gd.uid b/common/ui/counter/spinbox.gd.uid similarity index 100% rename from common/ui/custom_spinbox/custom_spinbox.gd.uid rename to common/ui/counter/spinbox.gd.uid diff --git a/common/ui/custom_spinbox/custom_spinbox.gd b/common/ui/custom_spinbox/custom_spinbox.gd deleted file mode 100644 index 4143dcea..00000000 --- a/common/ui/custom_spinbox/custom_spinbox.gd +++ /dev/null @@ -1,57 +0,0 @@ -extends PanelContainer - -signal value_changed(value: Variant) - -@export var as_integer: bool = true -@export var min_value: float = -9999999999 -@export var max_value: float = 9999999999 -@export var step: float = 1 -@export var suffix: String - -@onready var spin_box = $HBoxContainer/SpinBox - -var value: Variant: - get(): - return int(spin_box.value) if as_integer else spin_box.value - set(value): - spin_box.value = value - - -func _ready(): - var line_edit: LineEdit = spin_box.get_line_edit() - line_edit.connect("focus_exited", _on_focus_exited) - line_edit.connect("text_submitted", _on_text_submitted) - line_edit.theme_type_variation = "SpinBoxLineEdit" - _update_settings() - - -func _update_settings(): - spin_box.min_value = min_value - spin_box.max_value = max_value - spin_box.step = step - spin_box.suffix = suffix - - -func _on_focus_exited() -> void: - _on_text_submitted(int(spin_box.value) if as_integer else spin_box.value) - - -func _on_text_submitted(_new_value: Variant) -> void: - value_changed.emit(int(spin_box.value) if as_integer else spin_box.value) - - -func _on_value_changed(_value: float) -> void: - if as_integer: - value_changed.emit(int(value)) - else: - value_changed.emit(value) - - -func _on_decrease_button_pressed() -> void: - spin_box.value -= spin_box.step - _on_focus_exited.call_deferred() - - -func _on_increase_button_pressed() -> void: - spin_box.value += spin_box.step - _on_focus_exited.call_deferred() diff --git a/common/ui/custom_spinbox/custom_spinbox.tscn b/common/ui/custom_spinbox/custom_spinbox.tscn deleted file mode 100644 index b37e8600..00000000 --- a/common/ui/custom_spinbox/custom_spinbox.tscn +++ /dev/null @@ -1,72 +0,0 @@ -[gd_scene load_steps=11 format=3 uid="uid://wiapsnoaoc44"] - -[ext_resource type="Script" uid="uid://4lke0olu80se" path="res://common/ui/custom_spinbox/custom_spinbox.gd" id="1_1slqw"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_i3s76"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_a42se"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_x4l3i"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_vh5iv"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_ae1hp"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_rwqsu"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_jeakv"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_107wb"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_4hddc"] - -[node name="CustomSpinBox" type="PanelContainer"] -theme_type_variation = &"SpinBoxPanel" -script = ExtResource("1_1slqw") - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="DecreaseButton" type="Button" parent="HBoxContainer"] -custom_minimum_size = Vector2(31, 0) -layout_mode = 2 -theme_type_variation = &"SpinBoxButtonLeft" -text = "-" - -[node name="VSeparator" type="VSeparator" parent="HBoxContainer"] -layout_mode = 2 - -[node name="SpinBox" type="SpinBox" parent="HBoxContainer"] -custom_minimum_size = Vector2(75, 0) -layout_mode = 2 -theme_override_constants/set_min_buttons_width_from_icons = 0 -theme_override_constants/buttons_width = 0 -theme_override_constants/field_and_buttons_separation = 0 -theme_override_constants/buttons_vertical_separation = 0 -theme_override_icons/up_disabled = SubResource("Texture2DRD_i3s76") -theme_override_icons/down = SubResource("Texture2DRD_a42se") -theme_override_icons/down_hover = SubResource("Texture2DRD_x4l3i") -theme_override_icons/down_pressed = SubResource("Texture2DRD_vh5iv") -theme_override_icons/down_disabled = SubResource("Texture2DRD_ae1hp") -theme_override_icons/up_pressed = SubResource("Texture2DRD_rwqsu") -theme_override_icons/up_hover = SubResource("Texture2DRD_jeakv") -theme_override_icons/up = SubResource("Texture2DRD_107wb") -theme_override_icons/updown = SubResource("Texture2DRD_4hddc") -alignment = 1 -update_on_text_changed = true -select_all_on_focus = true - -[node name="VSeparator2" type="VSeparator" parent="HBoxContainer"] -layout_mode = 2 - -[node name="IncreaseButton" type="Button" parent="HBoxContainer"] -custom_minimum_size = Vector2(31, 0) -layout_mode = 2 -theme_type_variation = &"SpinBoxButtonRight" -text = "+" - -[connection signal="pressed" from="HBoxContainer/DecreaseButton" to="." method="_on_decrease_button_pressed"] -[connection signal="focus_exited" from="HBoxContainer/SpinBox" to="." method="_on_focus_exited"] -[connection signal="value_changed" from="HBoxContainer/SpinBox" to="." method="_on_value_changed"] -[connection signal="pressed" from="HBoxContainer/IncreaseButton" to="." method="_on_increase_button_pressed"] diff --git a/common/ui/editor/editor_list_section.gd b/common/ui/editor/editor_list_section.gd new file mode 100644 index 00000000..cebd54d0 --- /dev/null +++ b/common/ui/editor/editor_list_section.gd @@ -0,0 +1,71 @@ +extends PanelContainer + +@export var section_icon: Texture2D + +var _property: Property +var _list_field: BaseListField +var _property_owner: InspectableObject + +@onready var vbox := %VBox +@onready var search_bar: LineEdit = %SearchLine +@onready var add_button: Button = $VBox/ToolBar/AddButton + + +func _ready() -> void: + search_bar.placeholder_text = "Filter %s" % name.to_lower() + if add_button: + add_button.pressed.connect(_on_add_button_pressed) + + +func clear() -> void: + for child: Node in vbox.get_children(): + child.queue_free() + + +func load_items(property: Property, property_owner: InspectableObject = null) -> void: + clear() + _property = property + _property_owner = property_owner + + if not _property: + return + + _list_field = FieldBucket.safe_create_field(_property.type) + if _list_field is BaseListField: + _property.bind_field(_list_field, _property_owner) + vbox.add_child(_list_field) + + +func _on_add_button_pressed() -> void: + if _list_field is BaseListField: + _list_field.add_item() + + +func _on_search_line_text_changed(new_text: String) -> void: + _list_field.show_all_items() + if new_text.is_empty(): + return + + var search_keys: Array[String] = ["name", "display_name", "nicknames", "description"] + var item_list: Array = _list_field.get_items() + + for item: Variant in item_list: + var hide_item: bool = true + var item_idx: int = item_list.find(item) + if item is InspectableObject: + for prop: Property in item.get_properties(): + if not prop.name in search_keys: + continue + + var value: Variant = prop.get_value() + if value is not String: + continue + + if new_text.to_lower() in value.to_lower(): + hide_item = false + elif item is Property: + var value: Variant = (item as Property).get_value() + if new_text.to_lower() in str(value).to_lower(): + hide_item = false + if hide_item: + _list_field.hide_item(item_idx) diff --git a/common/ui/editor/editor_list_section.gd.uid b/common/ui/editor/editor_list_section.gd.uid new file mode 100644 index 00000000..40a85d71 --- /dev/null +++ b/common/ui/editor/editor_list_section.gd.uid @@ -0,0 +1 @@ +uid://yglbu25x1rsy diff --git a/common/ui/editor/editor_list_section.tscn b/common/ui/editor/editor_list_section.tscn new file mode 100644 index 00000000..cb57b5d9 --- /dev/null +++ b/common/ui/editor/editor_list_section.tscn @@ -0,0 +1,41 @@ +[gd_scene format=3 uid="uid://mfdu320oy6ex"] + +[ext_resource type="Script" uid="uid://yglbu25x1rsy" path="res://common/ui/editor/editor_list_section.gd" id="1_coa1v"] +[ext_resource type="Texture2D" uid="uid://hlck6y4i3l5q" path="res://ui/assets/icons/plus.svg" id="3_j4mj2"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_gx6th"] + +[node name="ListSection" type="PanelContainer" unique_id=549163673] +custom_minimum_size = Vector2(250, 0) +offset_right = 250.0 +offset_bottom = 47.0 +theme_override_styles/panel = SubResource("StyleBoxEmpty_gx6th") +script = ExtResource("1_coa1v") + +[node name="VBox" type="VBoxContainer" parent="." unique_id=1289136918] +layout_mode = 2 + +[node name="ToolBar" type="HBoxContainer" parent="VBox" unique_id=624846914] +layout_mode = 2 + +[node name="SearchLine" type="LineEdit" parent="VBox/ToolBar" unique_id=418550898] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Filter" + +[node name="AddButton" type="Button" parent="VBox/ToolBar" unique_id=41512466] +layout_mode = 2 +icon = ExtResource("3_j4mj2") + +[node name="ScrollContainer" type="ScrollContainer" parent="VBox" unique_id=1852215667] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBox" type="VBoxContainer" parent="VBox/ScrollContainer" unique_id=1009544879] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[connection signal="text_changed" from="VBox/ToolBar/SearchLine" to="." method="_on_search_line_text_changed"] diff --git a/common/ui/editor/list_container.gd b/common/ui/editor/list_container.gd new file mode 100644 index 00000000..62875691 --- /dev/null +++ b/common/ui/editor/list_container.gd @@ -0,0 +1,52 @@ +extends BoxContainer + +var _dropdown: OptionButton + + +func _ready() -> void: + child_entered_tree.connect(_on_child_entered_tree) + child_exiting_tree.connect(_on_child_exiting_tree) + update() + +func update() -> void: + var selected_cache: int = 0 + if _dropdown != null: + selected_cache = _dropdown.selected + _dropdown.queue_free() + + _dropdown = OptionButton.new() + _dropdown.item_selected.connect(_on_item_selected) + add_child(_dropdown) + move_child(_dropdown, 0) + + for child: Control in get_children(): + if not child is PanelContainer or child == _dropdown: + continue + + _dropdown.add_item(child.name) + child.size_flags_vertical = Control.SIZE_EXPAND_FILL + _on_item_selected(selected_cache) + +func _on_item_selected(index: int) -> void: + var node_name: String = _dropdown.get_item_text(index) + var node: PanelContainer = get_node_or_null(node_name) + if not node: + return + + for child: Node in get_children(): + if child == _dropdown: + continue + + if child is PanelContainer: + child.hide() + + node.show() + + +func _on_child_entered_tree(node: Node) -> void: + if not node is PanelContainer: return + update() + +func _on_child_exiting_tree(node: Node) -> void: + if not node is PanelContainer: return + update() diff --git a/common/ui/editor/list_container.gd.uid b/common/ui/editor/list_container.gd.uid new file mode 100644 index 00000000..d2ed06e5 --- /dev/null +++ b/common/ui/editor/list_container.gd.uid @@ -0,0 +1 @@ +uid://clpwbag6bi7y3 diff --git a/common/ui/expanded_text_edit/expanded_text_edit_container.gd b/common/ui/expanded_text_edit/expanded_text_edit_container.gd new file mode 100644 index 00000000..bbcb512c --- /dev/null +++ b/common/ui/expanded_text_edit/expanded_text_edit_container.gd @@ -0,0 +1,33 @@ +extends MarginContainer + +@onready var text_edit: TextEdit = %TextEdit +var little_text_edit: TextEdit + + +func _ready() -> void: + hide() + EventBus.expand_text_edit.connect(_on_expand_text_edit) + + +func _on_expand_text_edit(little_te: TextEdit) -> void: + little_text_edit = little_te + text_edit.text = little_text_edit.text + + show() + + +func _on_button_pressed() -> void: + hide() + little_text_edit.focus_exited.emit() + + +func _on_text_edit_text_changed() -> void: + little_text_edit.text = text_edit.text + little_text_edit.text_changed.emit(text_edit.text) + + +func _on_visibility_changed() -> void: + if visible: + EventBus.show_dimmer.emit() + return + EventBus.hide_dimmer.emit() diff --git a/common/layouts/expanded_text_edit/expanded_text_edit_container.gd.uid b/common/ui/expanded_text_edit/expanded_text_edit_container.gd.uid similarity index 100% rename from common/layouts/expanded_text_edit/expanded_text_edit_container.gd.uid rename to common/ui/expanded_text_edit/expanded_text_edit_container.gd.uid diff --git a/common/layouts/expanded_text_edit/expanded_text_edit_container.tscn b/common/ui/expanded_text_edit/expanded_text_edit_container.tscn similarity index 91% rename from common/layouts/expanded_text_edit/expanded_text_edit_container.tscn rename to common/ui/expanded_text_edit/expanded_text_edit_container.tscn index 239bf778..d7a0216c 100644 --- a/common/layouts/expanded_text_edit/expanded_text_edit_container.tscn +++ b/common/ui/expanded_text_edit/expanded_text_edit_container.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=2 format=3 uid="uid://bcs6s2yuf374j"] +[gd_scene format=3 uid="uid://bcs6s2yuf374j"] -[ext_resource type="Script" uid="uid://cfcj8vxsncvae" path="res://common/layouts/expanded_text_edit/expanded_text_edit_container.gd" id="1_mcmmr"] +[ext_resource type="Script" uid="uid://cfcj8vxsncvae" path="res://common/ui/expanded_text_edit/expanded_text_edit_container.gd" id="1_mcmmr"] [node name="MarginContainer" type="MarginContainer"] anchors_preset = 15 diff --git a/common/ui/fields/character_field/assets/portrait_placeholder.svg b/common/ui/fields/character_field/assets/portrait_placeholder.svg deleted file mode 100644 index 35f2b975..00000000 --- a/common/ui/fields/character_field/assets/portrait_placeholder.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/common/ui/fields/character_field/assets/portrait_placeholder.svg.import b/common/ui/fields/character_field/assets/portrait_placeholder.svg.import deleted file mode 100644 index 0a4deb37..00000000 --- a/common/ui/fields/character_field/assets/portrait_placeholder.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://ddah0eo1qhki4" -path="res://.godot/imported/portrait_placeholder.svg-3f112112b6ad9b59dbf2e76175184d46.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://common/ui/fields/character_field/assets/portrait_placeholder.svg" -dest_files=["res://.godot/imported/portrait_placeholder.svg-3f112112b6ad9b59dbf2e76175184d46.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/common/ui/fields/character_field/monologue_character_field.gd b/common/ui/fields/character_field/monologue_character_field.gd deleted file mode 100644 index 8ad612df..00000000 --- a/common/ui/fields/character_field/monologue_character_field.gd +++ /dev/null @@ -1,26 +0,0 @@ -class_name MonologueCharacterField extends MonologueField - - -@onready var name_edit := %NameEdit -@onready var delete_button := $HBoxContainer/VBoxContainer/VBoxContainer/HBoxContainer/DeleteButton - -var character_index: int = -1 -var default_portrait: String = "" -var graph_edit: MonologueGraphEdit - - -func propagate(value: Variant) -> void: - super.propagate(value) - name_edit.text = value.get("Name", "") - - -func _on_name_edit_focus_exited() -> void: - _on_name_edit_text_submitted(name_edit.text) - - -func _on_name_edit_text_submitted(new_text: String) -> void: - field_updated.emit({"Name" = new_text}) - - -func _on_edit_button_pressed() -> void: - GlobalSignal.emit("open_character_edit", [graph_edit, character_index]) diff --git a/common/ui/fields/character_field/monologue_character_field.gd.uid b/common/ui/fields/character_field/monologue_character_field.gd.uid deleted file mode 100644 index a8c217d4..00000000 --- a/common/ui/fields/character_field/monologue_character_field.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cmvo3t5bngju6 diff --git a/common/ui/fields/character_field/monologue_character_field.tscn b/common/ui/fields/character_field/monologue_character_field.tscn deleted file mode 100644 index e7cba915..00000000 --- a/common/ui/fields/character_field/monologue_character_field.tscn +++ /dev/null @@ -1,78 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://rmul5j0wm0l8"] - -[ext_resource type="PackedScene" uid="uid://dfwf55ovgwir3" path="res://common/ui/buttons/delete_button.tscn" id="1_eutnh"] -[ext_resource type="Script" uid="uid://cmvo3t5bngju6" path="res://common/ui/fields/character_field/monologue_character_field.gd" id="1_no1me"] -[ext_resource type="Texture2D" uid="uid://ddah0eo1qhki4" path="res://common/ui/fields/character_field/assets/portrait_placeholder.svg" id="2_jkh4y"] -[ext_resource type="Shader" uid="uid://bsso8dloc4bce" path="res://logic/shaders/texture_rect_clip.tres" id="2_nva25"] - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_q7tom"] -shader = ExtResource("2_nva25") -shader_parameter/corner_scale = 0.1 - -[node name="VBoxContainer" type="VBoxContainer"] -offset_right = 40.0 -offset_bottom = 40.0 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 -script = ExtResource("1_no1me") - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="CenterContainer" type="CenterContainer" parent="HBoxContainer"] -custom_minimum_size = Vector2(100, 100) -layout_mode = 2 - -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/CenterContainer"] -material = SubResource("ShaderMaterial_q7tom") -clip_contents = true -custom_minimum_size = Vector2(100, 100) -layout_mode = 2 -texture = ExtResource("2_jkh4y") -expand_mode = 1 -stretch_mode = 6 - -[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="NameContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="FieldLabel" type="Label" parent="HBoxContainer/VBoxContainer/NameContainer"] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -size_flags_vertical = 0 -text = "Name" - -[node name="InnerVBox" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/NameContainer"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="NameEdit" type="LineEdit" parent="HBoxContainer/VBoxContainer/NameContainer/InnerVBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -alignment = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/VBoxContainer"] -layout_mode = 2 -alignment = 1 - -[node name="EditButton" type="Button" parent="HBoxContainer/VBoxContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -text = "Edit" - -[node name="DeleteButton" parent="HBoxContainer/VBoxContainer/VBoxContainer/HBoxContainer" instance=ExtResource("1_eutnh")] -layout_mode = 2 -size_flags_vertical = 4 - -[connection signal="focus_exited" from="HBoxContainer/VBoxContainer/NameContainer/InnerVBox/NameEdit" to="." method="_on_name_edit_focus_exited"] -[connection signal="text_submitted" from="HBoxContainer/VBoxContainer/NameContainer/InnerVBox/NameEdit" to="." method="_on_name_edit_text_submitted"] -[connection signal="pressed" from="HBoxContainer/VBoxContainer/VBoxContainer/HBoxContainer/EditButton" to="." method="_on_edit_button_pressed"] diff --git a/common/ui/fields/check_box/monologue_check_box.gd b/common/ui/fields/check_box/monologue_check_box.gd deleted file mode 100644 index d3d5c5f0..00000000 --- a/common/ui/fields/check_box/monologue_check_box.gd +++ /dev/null @@ -1,17 +0,0 @@ -class_name MonologueCheckBox extends MonologueField - -@onready var check_box = $VBox/CheckBox -@onready var label = $Label - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - check_box.set_pressed_no_signal(value if (value is bool) else false) - - -func _on_check_box_toggled(toggled_on: bool) -> void: - field_updated.emit(toggled_on) diff --git a/common/ui/fields/check_box/monologue_check_box.gd.uid b/common/ui/fields/check_box/monologue_check_box.gd.uid deleted file mode 100644 index 688b968a..00000000 --- a/common/ui/fields/check_box/monologue_check_box.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://m2a68q1fce52 diff --git a/common/ui/fields/check_box/monologue_check_box.tscn b/common/ui/fields/check_box/monologue_check_box.tscn deleted file mode 100644 index 917031d9..00000000 --- a/common/ui/fields/check_box/monologue_check_box.tscn +++ /dev/null @@ -1,24 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://71sq1ohwv8cn"] - -[ext_resource type="Script" uid="uid://m2a68q1fce52" path="res://common/ui/fields/check_box/monologue_check_box.gd" id="1_awprp"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_8ij76"] - -[node name="MonologueCheckBox" type="HBoxContainer"] -custom_minimum_size = Vector2(0, 32) -offset_right = 81.0 -offset_bottom = 32.0 -theme_type_variation = &"HBoxContainer_Small" -script = ExtResource("1_awprp") - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -alignment = 1 - -[node name="CheckBox" type="CheckBox" parent="VBox"] -layout_mode = 2 -expand_icon = true - -[node name="Label" parent="." instance=ExtResource("2_8ij76")] -layout_mode = 2 - -[connection signal="toggled" from="VBox/CheckBox" to="." method="_on_check_box_toggled"] diff --git a/common/ui/fields/collapsible_field/collapsible_field.gd b/common/ui/fields/collapsible_field/collapsible_field.gd deleted file mode 100644 index 355c820a..00000000 --- a/common/ui/fields/collapsible_field/collapsible_field.gd +++ /dev/null @@ -1,97 +0,0 @@ -class_name CollapsibleField extends VBoxContainer - -signal add_pressed - -@export var show_add_button: bool = false -@export var separate_items: bool = false -@export var expand: bool = false - -@onready var button := $Button -@onready var collapsible_container := $CollapsibleContainer -@onready var vbox := %FieldContainer -@onready var add_button := %AddButton - -@onready var icon_close := preload("res://ui/assets/icons/arrow_right.svg") -@onready var icon_open := preload("res://ui/assets/icons/arrow_down.svg") - - -func _ready() -> void: - button.icon = icon_close - add_button.visible = show_add_button - close() - _update() - - -func add_item(item: Control, force_readable_name: bool = false) -> void: - var existing_children = vbox.get_children().filter(_is_not_being_deleted) - if separate_items and existing_children.size() > 0: - var separator := HSeparator.new() - separator.theme_type_variation = "HDottedSeparator" - vbox.add_child(separator, true) - - item.visibility_changed.connect(_update) - - vbox.add_child(item, force_readable_name) - _update() - - -func _update(): - var can_see: bool = show_add_button - - for child in vbox.get_children(): - if not child.visible: - continue - can_see = true - - if visible != can_see: - visible = can_see - - if expand: - size_flags_vertical = SIZE_EXPAND_FILL - vbox.size_flags_vertical = SIZE_EXPAND_FILL - - -func set_title(text: String) -> void: - button.text = text - - -func get_items() -> Array[Node]: - return vbox.get_children().filter(func(c): return c is not HSeparator) - - -func is_open() -> bool: - return collapsible_container.visible - - -func clear() -> void: - for child in vbox.get_children(): - child.queue_free() - - _update() - - -func _on_button_pressed() -> void: - if is_open(): - close() - else: - open() - - -func open() -> void: - button.icon = icon_open - collapsible_container.show() - button.release_focus() - - -func close() -> void: - button.icon = icon_close - collapsible_container.hide() - button.release_focus() - - -func _on_add_button_pressed() -> void: - add_pressed.emit() - - -func _is_not_being_deleted(node: Node) -> bool: - return not node.is_queued_for_deletion() diff --git a/common/ui/fields/collapsible_field/collapsible_field.gd.uid b/common/ui/fields/collapsible_field/collapsible_field.gd.uid deleted file mode 100644 index 7f7e7dd8..00000000 --- a/common/ui/fields/collapsible_field/collapsible_field.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bgy7bshgkocth diff --git a/common/ui/fields/collapsible_field/collapsible_field.tscn b/common/ui/fields/collapsible_field/collapsible_field.tscn deleted file mode 100644 index 7ee4ade7..00000000 --- a/common/ui/fields/collapsible_field/collapsible_field.tscn +++ /dev/null @@ -1,56 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://hvv74un17dp0"] - -[ext_resource type="Script" uid="uid://bgy7bshgkocth" path="res://common/ui/fields/collapsible_field/collapsible_field.gd" id="1_tmui1"] -[ext_resource type="Texture2D" uid="uid://cb6n6enqfvclw" path="res://ui/assets/icons/arrow_right.svg" id="2_357t1"] - -[node name="CollapsibleField" type="VBoxContainer"] -offset_right = 72.0 -offset_bottom = 89.0 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 -script = ExtResource("1_tmui1") - -[node name="Button" type="Button" parent="."] -layout_mode = 2 -theme_type_variation = &"Button_Flat_NoCorner" -icon = ExtResource("2_357t1") -alignment = 0 -icon_alignment = 2 - -[node name="CollapsibleContainer" type="MarginContainer" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -theme_type_variation = &"MarginContainer_Medium" -theme_override_constants/margin_left = 40 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 - -[node name="PanelContainer" type="PanelContainer" parent="CollapsibleContainer"] -layout_mode = 2 -theme_type_variation = &"CollapsibleFieldPanel" - -[node name="VBox" type="VBoxContainer" parent="CollapsibleContainer/PanelContainer"] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="FieldContainer" type="VBoxContainer" parent="CollapsibleContainer/PanelContainer/VBox"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_type_variation = &"FieldContainer" - -[node name="MarginContainer" type="MarginContainer" parent="CollapsibleContainer/PanelContainer/VBox"] -layout_mode = 2 -theme_type_variation = &"MarginContainer_Medium" - -[node name="AddButton" type="Button" parent="CollapsibleContainer/PanelContainer/VBox/MarginContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 2 -mouse_default_cursor_shape = 2 -theme_type_variation = &"Button_Outline" -text = "+" - -[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] -[connection signal="pressed" from="CollapsibleContainer/PanelContainer/VBox/MarginContainer/AddButton" to="." method="_on_add_button_pressed"] diff --git a/common/ui/fields/dropdown/monolgue_dropdown.gd b/common/ui/fields/dropdown/monolgue_dropdown.gd deleted file mode 100644 index 151a1aca..00000000 --- a/common/ui/fields/dropdown/monolgue_dropdown.gd +++ /dev/null @@ -1,109 +0,0 @@ -class_name MonologueDropdown extends MonologueField - -@export var store_index: bool -## Usefull when items are set after the value is set. -@export var late_items: bool - -@onready var label: Label = $Label -@onready var option_button: OptionButton = $OptionButton - -var backup_value: Variant - - -func _ready() -> void: - option_button.get_popup().transparent_bg = true - - -func disable_items(index_list: PackedInt32Array): - for index in range(1, option_button.item_count): - var is_disabled = index_list.has(index) - option_button.set_item_disabled(index, is_disabled) - validate() - - -func get_items() -> Array[Dictionary]: - var result: Array[Dictionary] = [] - for idx in range(option_button.item_count): - result.append( - { - "id": option_button.get_item_id(idx), - "text": option_button.get_item_text(idx), - "metadata": option_button.get_item_metadata(idx) - } - ) - return result - - -func get_item_idx_from_text(text: String) -> int: - var items = get_items() - for item in items: - if item.text == text: - return items.find(item) - return -1 - - -func propagate(value: Variant) -> void: - super.propagate(value) - backup_value = value - var index = get_item_idx_from_text(value) if value is String else value - if index < 0 or index >= option_button.item_count: # avoid falsy check - option_button.selected = 0 - else: - option_button.selected = index - validate() - - -func set_icons(index_to_texture: Dictionary): - for index in index_to_texture.keys(): - option_button.set_item_icon(index, index_to_texture.get(index)) - - -# `key_text` can contain "/" to navigate inside `data`. -func set_items( - data: Array, - key_text: String = "text", - key_id: String = "EditorIndex", - key_meta: String = "metadata" -) -> void: - option_button.clear() - for idx in range(data.size()): - var item_id = data[idx].get(key_id, -1) - if item_id is String: - item_id = -1 - var item_name = data[idx] - var item_name_path = key_text.split("/") - - for path in item_name_path: - item_name = item_name.get(path) - if item_name == null: - item_name = "undefined" - break - - option_button.add_item(item_name, item_id) - option_button.set_item_metadata(idx, data[idx].get(key_meta, "")) - - if late_items: - propagate(backup_value) - - validate() - - -func set_label_text(text: String) -> void: - label.text = text - - -func validate(): - var is_out = option_button.selected >= option_button.item_count - var is_negative = option_button.selected < 0 - if is_negative or is_out or option_button.is_item_disabled(option_button.selected): - option_button.theme_type_variation = "OptionButton_Error" - else: - option_button.theme_type_variation = "" - - -func _on_item_selected(index: int) -> void: - var value = index - if not store_index: - value = option_button.get_item_text(index) - validate() - field_updated.emit(value) diff --git a/common/ui/fields/dropdown/monolgue_dropdown.gd.uid b/common/ui/fields/dropdown/monolgue_dropdown.gd.uid deleted file mode 100644 index e71d25b9..00000000 --- a/common/ui/fields/dropdown/monolgue_dropdown.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ci7ipnx47b6fx diff --git a/common/ui/fields/dropdown/monologue_dropdown.tscn b/common/ui/fields/dropdown/monologue_dropdown.tscn deleted file mode 100644 index 39011579..00000000 --- a/common/ui/fields/dropdown/monologue_dropdown.tscn +++ /dev/null @@ -1,20 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://csunin0yg3ay0"] - -[ext_resource type="Script" uid="uid://ci7ipnx47b6fx" path="res://common/ui/fields/dropdown/monolgue_dropdown.gd" id="1_jtdjo"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_ahvj0"] - -[node name="MonologueDropdown" type="HBoxContainer"] -offset_right = 7.0 -offset_bottom = 29.0 -script = ExtResource("1_jtdjo") - -[node name="Label" parent="." instance=ExtResource("2_ahvj0")] -layout_mode = 2 - -[node name="OptionButton" type="OptionButton" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 -clip_text = true -allow_reselect = true - -[connection signal="item_selected" from="OptionButton" to="." method="_on_item_selected"] diff --git a/common/ui/fields/field_label.tscn b/common/ui/fields/field_label.tscn deleted file mode 100644 index 31924d94..00000000 --- a/common/ui/fields/field_label.tscn +++ /dev/null @@ -1,7 +0,0 @@ -[gd_scene format=3 uid="uid://x0daq5tsejey"] - -[node name="FieldLabel" type="Label"] -custom_minimum_size = Vector2(100, 31) -size_flags_vertical = 0 -text = "Value" -vertical_alignment = 1 diff --git a/common/ui/fields/file_picker/file_picker.gd b/common/ui/fields/file_picker/file_picker.gd deleted file mode 100644 index 994993ec..00000000 --- a/common/ui/fields/file_picker/file_picker.gd +++ /dev/null @@ -1,78 +0,0 @@ -class_name FilePicker extends MonologueField - -const AUDIO = ["*.mp3,*.ogg,*.wav;Sound Files"] -const IMAGE = ["*.bmp,*.jpg,*.jpeg,*.png,*.svg,*.webp;Image Files"] - -@export var base_path: String -@export var filters: PackedStringArray - -@onready var label: Label = $Label -@onready var line_edit: LineEdit = $VBox/HBox/LineEdit -@onready var picker_button: Button = $VBox/HBox/FilePickerButton -@onready var warn_label: Label = $VBox/WarnLabel - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - line_edit.text = value - validate(value) - - -func validate(path: String) -> bool: - warn_label.hide() - var is_valid = true - path = path.lstrip(" ") - path = path.rstrip(" ") - if path and filters: - var absolute_path = Path.relative_to_absolute(path, base_path) - if not FileAccess.file_exists(absolute_path): - warn_label.show() - warn_label.text = "File path not found!" - is_valid = false - else: - var correct_suffix: bool = false - var file_name: String = absolute_path.get_file() - for filter in filters: - var targets = _split_match(filter) - for target in targets: - if file_name.match(target): - correct_suffix = true - break - if not correct_suffix: - warn_label.show() - var formats = Array(filters).map(_split_match) - var text = ", ".join(formats.map(func(f): return ", ".join(f))) - warn_label.text = "File must match: %s" % text - is_valid = false - return is_valid - - -func _on_file_selected(path: String): - line_edit.text = Path.absolute_to_relative(path, base_path) - _on_focus_exited() - - -func _on_focus_exited() -> void: - _on_text_submitted(line_edit.text) - - -func _on_picker_button_pressed(): - GlobalSignal.emit("open_file_request", [_on_file_selected, filters, base_path.get_base_dir()]) - - -func _on_text_changed(new_text: String) -> void: - validate(new_text) - field_changed.emit(new_text) - - -func _on_text_submitted(file_path: String) -> void: - validate(file_path) - field_updated.emit(file_path) - - -func _split_match(filter: String) -> Array: - return filter.split(";")[0].split(",") diff --git a/common/ui/fields/file_picker/file_picker.gd.uid b/common/ui/fields/file_picker/file_picker.gd.uid deleted file mode 100644 index c3318124..00000000 --- a/common/ui/fields/file_picker/file_picker.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bqlktpo5qx1ps diff --git a/common/ui/fields/file_picker/monologue_file_picker.tscn b/common/ui/fields/file_picker/monologue_file_picker.tscn deleted file mode 100644 index 5e7b6f0f..00000000 --- a/common/ui/fields/file_picker/monologue_file_picker.tscn +++ /dev/null @@ -1,50 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://o5dt5106rohh"] - -[ext_resource type="Script" uid="uid://bqlktpo5qx1ps" path="res://common/ui/fields/file_picker/file_picker.gd" id="1_siiu8"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_hph74"] -[ext_resource type="Texture2D" uid="uid://t1i3wy037vsu" path="res://ui/assets/icons/folder_icon.png" id="2_plad0"] - -[sub_resource type="LabelSettings" id="LabelSettings_nr3ee"] -font_color = Color(0.768627, 0.180392, 0.25098, 1) - -[node name="FilePickerLineEdit" type="HBoxContainer"] -offset_right = 300.0 -offset_bottom = 59.0 -script = ExtResource("1_siiu8") - -[node name="Label" parent="." instance=ExtResource("2_hph74")] -layout_mode = 2 - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBox" type="HBoxContainer" parent="VBox"] -layout_mode = 2 -theme_override_constants/separation = 0 -alignment = 2 - -[node name="LineEdit" type="LineEdit" parent="VBox/HBox"] -layout_mode = 2 -size_flags_horizontal = 3 -structured_text_bidi_override = 2 - -[node name="FilePickerButton" type="Button" parent="VBox/HBox"] -custom_minimum_size = Vector2(33, 25) -layout_mode = 2 -theme_type_variation = &"FlatButton" -icon = ExtResource("2_plad0") -icon_alignment = 1 -expand_icon = true - -[node name="WarnLabel" type="Label" parent="VBox"] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -text = "File path not found!" -label_settings = SubResource("LabelSettings_nr3ee") -autowrap_mode = 2 - -[connection signal="focus_exited" from="VBox/HBox/LineEdit" to="." method="_on_focus_exited"] -[connection signal="text_changed" from="VBox/HBox/LineEdit" to="." method="_on_text_changed"] -[connection signal="text_submitted" from="VBox/HBox/LineEdit" to="." method="_on_text_submitted"] -[connection signal="pressed" from="VBox/HBox/FilePickerButton" to="." method="_on_picker_button_pressed"] diff --git a/common/ui/fields/line_edit/monologue_line_edit.gd b/common/ui/fields/line_edit/monologue_line_edit.gd deleted file mode 100644 index 4dcceceb..00000000 --- a/common/ui/fields/line_edit/monologue_line_edit.gd +++ /dev/null @@ -1,70 +0,0 @@ -class_name MonologueLine extends MonologueField - -@export var copyable: bool -@export var font_size: int = 16 -@export var is_sublabel: bool -@export var sublabel_prefix: String = "↳ " -@export var note_text: String - -var ribbon_scene = preload("res://common/ui/ribbon/ribbon.tscn") -var revert_text: String -var validator: Callable = func(_text): return true - -@onready var copy_button = $HBox/InnerVBox/LineEdit/HBoxContainer/CopyButton -@onready var label = $HBox/FieldLabel -@onready var line_edit = $HBox/InnerVBox/LineEdit -@onready var warning = $HBox/InnerVBox/WarnLabel -@onready var note = $NoteLabel - - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - copy_button.visible = copyable - label.add_theme_font_size_override("font_size", font_size) - line_edit.add_theme_font_size_override("font_size", font_size) - warning.add_theme_font_size_override("font_size", font_size) - warning.hide() - note.visible = !note_text.is_empty() - note.text = note_text - - -func set_label_text(text: String) -> void: - if is_sublabel: - label.custom_minimum_size.x = 140 - add_theme_constant_override("margin_left", 25) - label.add_theme_color_override("font_color", Color("858585")) - label.text = sublabel_prefix + text - else: - label.text = text - - -func set_label_visible(can_see: bool) -> void: - label.visible = can_see - - -func propagate(value: Variant) -> void: - super.propagate(value) - line_edit.text = str(value) - revert_text = line_edit.text - - -func _on_copy_button_pressed() -> void: - DisplayServer.clipboard_set(line_edit.text) - var ribbon = ribbon_scene.instantiate() - ribbon.position = get_viewport().get_mouse_position() - get_window().add_child(ribbon) - - -func _on_focus_exited() -> void: - _on_text_submitted(line_edit.text) - - -func _on_text_changed(new_text: String) -> void: - field_changed.emit(new_text) - - -func _on_text_submitted(new_text: String) -> void: - if validator.call(new_text): - field_updated.emit(new_text) - else: - line_edit.text = revert_text diff --git a/common/ui/fields/line_edit/monologue_line_edit.gd.uid b/common/ui/fields/line_edit/monologue_line_edit.gd.uid deleted file mode 100644 index 27ab279b..00000000 --- a/common/ui/fields/line_edit/monologue_line_edit.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cdx8c5vk7f3jb diff --git a/common/ui/fields/line_edit/monologue_line_edit.tscn b/common/ui/fields/line_edit/monologue_line_edit.tscn deleted file mode 100644 index 8c82947a..00000000 --- a/common/ui/fields/line_edit/monologue_line_edit.tscn +++ /dev/null @@ -1,63 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://bw7thqdhujl41"] - -[ext_resource type="Script" uid="uid://cdx8c5vk7f3jb" path="res://common/ui/fields/line_edit/monologue_line_edit.gd" id="1_toqtt"] -[ext_resource type="Texture2D" uid="uid://dm2u0xqmmcorj" path="res://ui/assets/icons/copy.png" id="2_lbcco"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_v8fwd"] - -[node name="MonologueLineEdit" type="VBoxContainer"] -offset_right = 40.0 -offset_bottom = 40.0 -size_flags_horizontal = 3 -script = ExtResource("1_toqtt") -sublabel_prefix = "┗ " - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="FieldLabel" parent="HBox" instance=ExtResource("2_v8fwd")] -layout_mode = 2 - -[node name="InnerVBox" type="VBoxContainer" parent="HBox"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="LineEdit" type="LineEdit" parent="HBox/InnerVBox"] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="HBox/InnerVBox/LineEdit"] -layout_mode = 1 -anchors_preset = 11 -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -30.0 -grow_horizontal = 0 -grow_vertical = 2 -alignment = 2 - -[node name="CopyButton" type="Button" parent="HBox/InnerVBox/LineEdit/HBoxContainer"] -custom_minimum_size = Vector2(33, 25) -layout_mode = 2 -icon = ExtResource("2_lbcco") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="WarnLabel" type="Label" parent="HBox/InnerVBox"] -layout_mode = 2 -theme_type_variation = &"WarnLabel" -text = "Warning" - -[node name="NoteLabel" type="Label" parent="."] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -theme_type_variation = &"NoteLabel" -theme_override_font_sizes/font_size = 12 -text = "Note: Description" -autowrap_mode = 3 - -[connection signal="focus_exited" from="HBox/InnerVBox/LineEdit" to="." method="_on_focus_exited"] -[connection signal="text_changed" from="HBox/InnerVBox/LineEdit" to="." method="_on_text_changed"] -[connection signal="text_submitted" from="HBox/InnerVBox/LineEdit" to="." method="_on_text_submitted"] -[connection signal="pressed" from="HBox/InnerVBox/LineEdit/HBoxContainer/CopyButton" to="." method="_on_copy_button_pressed"] diff --git a/common/ui/fields/list/monologue_list.gd b/common/ui/fields/list/monologue_list.gd deleted file mode 100644 index 7312cfc7..00000000 --- a/common/ui/fields/list/monologue_list.gd +++ /dev/null @@ -1,136 +0,0 @@ -## A list field that represents a [MonologueGraphEdit] data item. -class_name MonologueList extends MonologueField - -@onready var button := $CollapsibleField/Button -@onready var collapsible_container := $CollapsibleField/CollapsibleContainer -@onready var vbox := $CollapsibleField/CollapsibleContainer/PanelContainer/VBox -@onready -var field_container := $CollapsibleField/CollapsibleContainer/PanelContainer/VBox/FieldContainer - -var delete_scene = preload("res://common/ui/buttons/delete_button.tscn") - -var add_callback: Callable = Constants.empty_callback -var delete_callback: Callable = func(list): return list -var get_callback: Callable = Constants.empty_callback -var data_list: Array = [] -var flat: bool = false -var expand: bool = false - - -func _ready() -> void: - collapsible_field = $CollapsibleField - collapsible_field.add_pressed.connect(_on_add_button_pressed) - collapsible_field.expand = expand - post_ready.call_deferred() - - if flat: - collapsible_field.separate_items = false - button.hide() - collapsible_container.add_theme_constant_override("margin_left", 0) - field_container.add_theme_constant_override("margin_left", 0) - collapsible_field._update() - - -func post_ready() -> void: - if get_parent().get_child_count() <= 1: - collapsible_field.open() - - -## Add a new option node into the list and show its fields in the vbox. -func append_list_item(item) -> void: - var panel := create_flat_item_container() if flat else create_item_container() - var field_box = create_item_vbox(panel) - collapsible_field.add_item(panel, true) - for property_name in item.get_property_names(): - var property = item.get(property_name) - if not property.visible: - continue - var field = item.get(property_name).show(field_box, false) - field.set_label_text(Util.to_key_name(property_name, " ")) - var identifier = item.id.value if "id" in item else item.name.value - - if "custom_delete_button" in item and item.custom_delete_button: - item.custom_delete_button.connect("pressed", _on_delete_button_pressed.bind(identifier)) - return - create_delete_button(field_box, identifier) - - -func clear_list(): - collapsible_field.clear() - data_list = [] - - -func create_item_container() -> PanelContainer: - var item_container = PanelContainer.new() - item_container.theme_type_variation = "ItemContainer" - return item_container - - -func create_flat_item_container() -> PanelContainer: - var item_container = PanelContainer.new() - item_container.theme_type_variation = "ItemContainerFlat" - return item_container - - -func create_item_vbox(panel: PanelContainer) -> VBoxContainer: - var item_vbox = VBoxContainer.new() - panel.add_child(item_vbox, true) - return item_vbox - - -func create_delete_button(field_box: VBoxContainer, id: Variant) -> void: - var delete_button = delete_scene.instantiate() - delete_button.connect("pressed", _on_delete_button_pressed.bind(id)) - - var first_hbox = _find_first_hbox(field_box) - if first_hbox: - first_hbox.add_child(delete_button, true) - else: - delete_button.queue_free() - - -func set_label_text(text: String) -> void: - collapsible_field.set_title(text) - - -func set_label_visible(_can_see: bool) -> void: - pass - - -func propagate(data: Variant) -> void: - super.propagate(data) - clear_list() - data_list = get_callback.call() - for reference in data_list: - append_list_item(reference) - data_list = data_list.map(func(r): return r._to_dict()) - - -func _find_first_hbox(control: Control) -> HBoxContainer: - for child in control.get_children(): - if child is HBoxContainer and child.visible: - return child - else: - var recursive = _find_first_hbox(child) - if recursive: - return recursive - return null - - -func _on_add_button_pressed() -> void: - # the add_callback creates the actual instance in its source node - data_list = get_callback.call() - data_list = data_list.map(func(r): return r._to_dict()) - var new_item = add_callback.call() - data_list.append(new_item._to_dict.call()) - append_list_item(new_item) - field_updated.emit(data_list) - - -func _on_delete_button_pressed(id: Variant) -> void: - for reference in data_list: - if reference.get("ID") == id or reference.get("Name") == id: - data_list.erase(reference) - break - var modified_list = delete_callback.call(data_list) - field_updated.emit(modified_list) diff --git a/common/ui/fields/list/monologue_list.gd.uid b/common/ui/fields/list/monologue_list.gd.uid deleted file mode 100644 index 988801fc..00000000 --- a/common/ui/fields/list/monologue_list.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://u77m3oxit3r8 diff --git a/common/ui/fields/list/monologue_list.tscn b/common/ui/fields/list/monologue_list.tscn deleted file mode 100644 index 4fb5a8c0..00000000 --- a/common/ui/fields/list/monologue_list.tscn +++ /dev/null @@ -1,19 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://ddc27sbeek1p"] - -[ext_resource type="Script" uid="uid://u77m3oxit3r8" path="res://common/ui/fields/list/monologue_list.gd" id="1_8kx3n"] -[ext_resource type="PackedScene" uid="uid://hvv74un17dp0" path="res://common/ui/fields/collapsible_field/collapsible_field.tscn" id="2_jih45"] - -[node name="MonologueList" type="VBoxContainer"] -anchors_preset = -1 -anchor_right = 0.125 -anchor_bottom = 0.062 -offset_bottom = 0.479996 -theme_override_constants/separation = 5 -script = ExtResource("1_8kx3n") - -[node name="CollapsibleField" parent="." instance=ExtResource("2_jih45")] -layout_mode = 2 -show_add_button = true -separate_items = true - -[editable path="CollapsibleField"] diff --git a/common/ui/fields/localizable.gd b/common/ui/fields/localizable.gd deleted file mode 100644 index f692da31..00000000 --- a/common/ui/fields/localizable.gd +++ /dev/null @@ -1,65 +0,0 @@ -## Properties which can have language switching. -class_name Localizable extends Property - -## Stores the value in a locale dictionary. -var raw_data: Dictionary = {} -## The value to be initialized on a new locale. Empty string by default. -var initialized_value: Variant - - -func _init(ui_scene: PackedScene, ui_setters: Dictionary = {}, default: Variant = "") -> void: - super(ui_scene, ui_setters, default) - initialized_value = default - GlobalSignal.add_listener("language_deleted", store_data) - - -## Gets the current language from the language switcher. -func get_locale() -> LanguageOption: - if GlobalVariables.language_switcher: - return GlobalVariables.language_switcher.get_current_language() - else: - return null - - -func get_value() -> Variant: - if GlobalVariables.language_switcher: - return raw_data.get(get_locale().name, initialized_value) - return super.get_value() - - -func set_value(new_value: Variant) -> void: - if GlobalVariables.language_switcher: - var langs = GlobalVariables.language_switcher.get_languages().keys() - if new_value is Dictionary and Util.is_any_inside(new_value.keys(), langs): - raw_data = _from_raw_value(new_value) - else: - raw_data[get_locale().name] = new_value - super.set_value(new_value) - - -func store_data(node_name: String, restoration: Dictionary, _choices: Dictionary) -> void: - if raw_data.has(node_name): - restoration[self] = raw_data.duplicate(true) - - -## Export property value as localized dictionary. -func to_raw_value() -> Variant: - if GlobalVariables.language_switcher: - var new_dict = {} - for key in raw_data: - var option = GlobalVariables.language_switcher.get_by_node_name(key) - if option: - new_dict[str(option)] = raw_data.get(key, initialized_value) - return new_dict - return value - - -## Private method to load property value is a localized dictionary. -func _from_raw_value(string_dict: Dictionary) -> Dictionary: - var language_dict = GlobalVariables.language_switcher.get_languages() - var new_dict = {} - for key in string_dict: - var language_node_name = language_dict.get(key) - if language_node_name: - new_dict[language_node_name] = string_dict.get(key) - return new_dict diff --git a/common/ui/fields/localizable.gd.uid b/common/ui/fields/localizable.gd.uid deleted file mode 100644 index 22b5f7d7..00000000 --- a/common/ui/fields/localizable.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bntwx0k2d3hqy diff --git a/common/ui/fields/monologue_argument.gd b/common/ui/fields/monologue_argument.gd deleted file mode 100644 index 4471f18d..00000000 --- a/common/ui/fields/monologue_argument.gd +++ /dev/null @@ -1,89 +0,0 @@ -## Similar to MonologueVariable but allows reference to existing variables. -class_name MonologueArgument extends MonologueVariable - -var last_boolean: bool -var last_number: float -var last_string: String - - -func _init(node: MonologueGraphNode): - super(node) - type.callers["set_items"][0].append({"id": 3, "text": "Variable"}) - #type.callers["set_icons"][0][3] = load("res://ui/assets/icons/bool_icon.png") - value.connect("preview", record_morph) - - -func change(_old_value: Variant, new_value: Variant, property: String) -> void: - var old_list = bound_node.arguments.value.duplicate(true) - var new_list = bound_node.arguments.value.duplicate(true) - new_list[index][property.capitalize()] = new_value - - # bound_node can be deleted, so we need to use PropertyChange here - graph.undo_redo.create_action("Arguments => %s" % new_value) - var pc = PropertyChange.new("arguments", old_list, new_list) - var ph = PropertyHistory.new(graph, graph.get_path_to(bound_node), [pc]) - graph.undo_redo.add_prepared_history(ph) - graph.undo_redo.commit_action() - - -## Creates a representation of the argument in an HBoxContainer. -func create_representation(parent: Control) -> HBoxContainer: - var representation = HBoxContainer.new() - parent.add_child(representation) - - var name_label = Label.new() - var name_text = name.value if name.value else "argument" - name_label.text = " #%d: %s" % [representation.get_index(), name_text] - representation.add_child(name_label) - - var type_label = Label.new() - type_label.text = "[%s]" % type.value if type.value else "type" - representation.add_child(type_label) - - var value_label = Label.new() - value_label.theme_type_variation = "NodeValue" - value_label.text = ( - str(value.value) if value.value is not String or value.value != "" else "value" - ) - representation.add_child(value_label) - - return representation - - -## Record the argument value so field morphing will populate correct value type. -func record_morph(new_value: Variant) -> void: - match typeof(new_value): - TYPE_BOOL: - last_boolean = new_value - TYPE_INT, TYPE_FLOAT: - last_number = new_value - TYPE_STRING: - last_string = new_value - - -## Reset the value if the argument value is not matching the type. -func reset_value(): - match type.value: - "Boolean": - if value.value is not bool: - value.value = last_boolean - "Integer": - if value.value is not float and value.value is not int: - value.value = last_number - _: - if value.value is not String: - value.value = last_string - - -func _type_morph(selected_type: String = type.value): - if selected_type == "Variable": - value.callers["set_items"] = [graph.variables, "Name", "ID", "Type"] - if graph.variables and value.value is not String: - value.value = graph.variables[0].get("Name") - value.morph(MonologueGraphNode.DROPDOWN) - else: - value.callers.erase("set_items") - super._type_morph(selected_type) - - reset_value() - value.propagate(value.value, false) diff --git a/common/ui/fields/monologue_argument.gd.uid b/common/ui/fields/monologue_argument.gd.uid deleted file mode 100644 index ea371999..00000000 --- a/common/ui/fields/monologue_argument.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://4omqhch6lxme diff --git a/common/ui/fields/monologue_field.gd b/common/ui/fields/monologue_field.gd deleted file mode 100644 index 8c24897b..00000000 --- a/common/ui/fields/monologue_field.gd +++ /dev/null @@ -1,35 +0,0 @@ -## UI control which allow the user to edit a graph node [MonologueProperty]. -class_name MonologueField extends Control - -## Emitted when the field's value is changed but not yet committed. -@warning_ignore("unused_signal") -signal field_changed(value: Variant) - -## Emitted when the field's value is updated/comitted by user input. -@warning_ignore("unused_signal") -signal field_updated(value: Variant) - -var collapsible_field: CollapsibleField: - set = set_collapsible_field - - -## Set the collapsible control that this MonologueField belongs to. -func set_collapsible_field(collapsible: CollapsibleField): - collapsible_field = collapsible - - -## Called by node panel to set field label text, if applicable. -func set_label_text(_text: String) -> void: - pass - - -## Set the field's label visibility. -func set_label_visible(_can_see: bool) -> void: - pass - - -## Meant to propagate the value set in [MonologueProperty] to this Field. -## This method does not emit [signal field_updated]. -func propagate(_value: Variant) -> void: - if collapsible_field: - collapsible_field.open() diff --git a/common/ui/fields/monologue_field.gd.uid b/common/ui/fields/monologue_field.gd.uid deleted file mode 100644 index 020c039d..00000000 --- a/common/ui/fields/monologue_field.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bkhv4rjlyh64j diff --git a/common/ui/fields/monologue_variable.gd b/common/ui/fields/monologue_variable.gd deleted file mode 100644 index be7a9069..00000000 --- a/common/ui/fields/monologue_variable.gd +++ /dev/null @@ -1,75 +0,0 @@ -class_name MonologueVariable extends RefCounted - -var name := Property.new(MonologueGraphNode.LINE) -var type := Property.new(MonologueGraphNode.DROPDOWN, {}, "Boolean") -var value := Property.new(MonologueGraphNode.TOGGLE, {}, false) - -var index: int = -1 -var bound_node: MonologueGraphNode -var graph: MonologueGraphEdit - - -func _init(node: MonologueGraphNode) -> void: - bound_node = node - graph = node.get_graph_edit() - - type.callers["set_items"] = [ - [ - {"id": 0, "text": "Boolean"}, - {"id": 1, "text": "Integer"}, - {"id": 2, "text": "String"}, - ] - ] - type.callers["set_icons"] = [ - { - 0: load("res://ui/assets/icons/bool_icon.png"), - 1: load("res://ui/assets/icons/int_icon.png"), - 2: load("res://ui/assets/icons/str_icon.png"), - } - ] - - type.connect("shown", _type_morph) - type.connect("change", change.bind("type")) - type.connect("display", graph.set_selected.bind(bound_node)) - name.connect("change", change.bind("name")) - name.connect("display", graph.set_selected.bind(bound_node)) - value.connect("change", change.bind("value")) - value.connect("display", graph.set_selected.bind(bound_node)) - - -func change(_old_value: Variant, new_value: Variant, property: String) -> void: - var old_list = bound_node.variables.value.duplicate(true) - var new_list = bound_node.variables.value.duplicate(true) - new_list[index][property.capitalize()] = new_value - - graph.undo_redo.create_action("Variables => %s" % new_value) - graph.undo_redo.add_do_property(bound_node.variables, "value", new_list) - graph.undo_redo.add_do_method(bound_node.variables.propagate.bind(new_list)) - graph.undo_redo.add_undo_property(bound_node.variables, "value", old_list) - graph.undo_redo.add_undo_method(bound_node.variables.propagate.bind(old_list)) - graph.undo_redo.commit_action() - - -func get_property_names() -> PackedStringArray: - return ["name", "type", "value"] - - -func _from_dict(dict: Dictionary) -> void: - _type_morph() - name.value = dict.get("Name") - type.value = dict.get("Type") - value.value = dict.get("Value") - - -func _to_dict() -> Dictionary: - return {"Name": name.value, "Type": type.value, "Value": value.value} - - -func _type_morph(selected_type: String = type.value): - match selected_type: - "Boolean": - value.morph(MonologueGraphNode.TOGGLE) - "Integer": - value.morph(MonologueGraphNode.SPINBOX) - "String": - value.morph(MonologueGraphNode.LINE) diff --git a/common/ui/fields/monologue_variable.gd.uid b/common/ui/fields/monologue_variable.gd.uid deleted file mode 100644 index 47857bfa..00000000 --- a/common/ui/fields/monologue_variable.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bo7lnuuv6ra7k diff --git a/common/ui/fields/portrait_option/abstract_portrait.gd b/common/ui/fields/portrait_option/abstract_portrait.gd deleted file mode 100644 index 6041f04a..00000000 --- a/common/ui/fields/portrait_option/abstract_portrait.gd +++ /dev/null @@ -1,70 +0,0 @@ -class_name AbstractPortraitOption extends RefCounted - -const PORTRAIT_FIELD := preload("res://common/ui/fields/portrait_option/portrait_option.tscn") - -var portrait_name := Property.new(MonologueGraphNode.LINE, {}, "") -var portrait := Property.new(PORTRAIT_FIELD, {}, {}) -var id := Property.new(MonologueGraphNode.LINE, {}, IDGen.generate(5)) -var idx := Property.new(MonologueGraphNode.SPINBOX, {}, 0) - -var custom_delete_button: Button = Button.new() - -var graph: MonologueGraphEdit -var root: PortraitListSection - - -func _init(node: PortraitListSection): - root = node - portrait.connect("change", update_portrait) - portrait_name.connect("change", update_portrait_name) - portrait.setters["graph_edit"] = graph - portrait_name.visible = false - - -func update_portrait(old_value: Variant, new_value: Variant): - var old_list = root.portraits.value.duplicate(true) - var new_list = root.portraits.value.duplicate(true) - - graph.undo_redo.create_action("Portrait %s => %s" % [str(old_value), str(new_value)]) - graph.undo_redo.add_do_property(root.portraits, "value", new_list) - graph.undo_redo.add_do_method(root.portraits.propagate.bind(new_list)) - graph.undo_redo.add_undo_property(root.portraits, "value", old_list) - graph.undo_redo.add_undo_method(root.portraits.propagate.bind(old_list)) - graph.undo_redo.commit_action() - - -func update_portrait_name(old_value: Variant, new_value: Variant): - var old_list = root.portraits.value.duplicate(true) - var new_list = root.portraits.value.duplicate(true) - - old_list[idx.value]["Name"] = old_value - new_list[idx.value]["Name"] = new_value - - graph.undo_redo.create_action("Portrait Name %s => %s" % [str(old_value), str(new_value)]) - graph.undo_redo.add_do_property(root.portraits, "value", new_list) - graph.undo_redo.add_do_method(root.portraits.propagate.bind(new_list)) - graph.undo_redo.add_undo_property(root.portraits, "value", old_list) - graph.undo_redo.add_undo_method(root.portraits.propagate.bind(old_list)) - graph.undo_redo.commit_action() - - -func get_property_names() -> PackedStringArray: - return ["portrait"] - - -func _from_dict(dict: Dictionary) -> void: - if dict.get("ID") is String: - id.value = dict.get("ID", IDGen.generate(5)) - portrait_name.value = dict.get("Name", "") - portrait.value = dict.get("Portrait", {}) - idx.value = dict.get("EditorIndex") - portrait.setters["portrait_index"] = idx.value - - -func _to_dict() -> Dictionary: - return { - "ID": id.value, - "Name": portrait_name.value, - "Portrait": portrait.value, - "EditorIndex": idx.value, - } diff --git a/common/ui/fields/portrait_option/abstract_portrait.gd.uid b/common/ui/fields/portrait_option/abstract_portrait.gd.uid deleted file mode 100644 index 95793685..00000000 --- a/common/ui/fields/portrait_option/abstract_portrait.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cqf51jo7a4llv diff --git a/common/ui/fields/portrait_option/portrait_option.gd b/common/ui/fields/portrait_option/portrait_option.gd deleted file mode 100644 index 2fd14f54..00000000 --- a/common/ui/fields/portrait_option/portrait_option.gd +++ /dev/null @@ -1,98 +0,0 @@ -class_name PortraitOption extends MonologueField - -signal pressed(this_option: PortraitOption) -signal set_to_default(this_option: PortraitOption) -signal name_submitted(this_option: PortraitOption) - -@onready var line_edit: LineEdit = %LineEdit -@onready var btn_star := $MarginContainer/HBoxContainer/HBoxContainer/btnStar -@onready var button := %Button - -@onready var active_stylebox: StyleBoxFlat = StyleBoxFlat.new() -@onready var star_icon := preload("res://ui/assets/icons/star.svg") -@onready var star_full_icon := preload("res://ui/assets/icons/star_full.svg") - -@export var is_default: bool = false - -var is_active: bool = false -var line_edit_unfocus_stylebox := StyleBoxEmpty.new() -var custom_delete_button: Button = Button.new() - - -func _ready() -> void: - line_edit_unfocus() - - active_stylebox.bg_color = Color("d55160") - active_stylebox.set_corner_radius_all(5) - - -func propagate(value: Variant) -> void: - super.propagate(value) - - -func set_option_name(new_name: String) -> void: - line_edit.text = new_name - - -func line_edit_unfocus() -> void: - line_edit.editable = false - line_edit.selecting_enabled = false - line_edit.flat = true - line_edit.mouse_filter = Control.MOUSE_FILTER_PASS - - button.show() - add_theme_stylebox_override("focus", line_edit_unfocus_stylebox) - name_submitted.emit(self) - - -func _on_btn_edit_pressed() -> void: - line_edit.editable = true - line_edit.selecting_enabled = true - line_edit.flat = false - line_edit.mouse_filter = Control.MOUSE_FILTER_STOP - line_edit.grab_focus() - - button.hide() - - -func _on_line_edit_focus_exited() -> void: - line_edit_unfocus() - - -func _on_btn_star_pressed() -> void: - set_default() - - -func set_default() -> void: - is_default = true - btn_star.texture_normal = star_full_icon - set_to_default.emit(self) - - -func release_default() -> void: - is_default = false - btn_star.texture_normal = star_icon - - -func set_active() -> void: - is_active = true - add_theme_stylebox_override("panel", active_stylebox) - - -func release_active() -> void: - is_active = false - remove_theme_stylebox_override("panel") - line_edit_unfocus() - - -func _on_button_pressed() -> void: - pressed.emit(self) - - -func _on_button_gui_input(event: InputEvent) -> void: - if is_active and event is InputEventMouseButton and event.is_pressed() and event.double_click: - _on_btn_edit_pressed() - - -func _on_line_edit_text_submitted(_new_text: String) -> void: - line_edit_unfocus() diff --git a/common/ui/fields/portrait_option/portrait_option.gd.uid b/common/ui/fields/portrait_option/portrait_option.gd.uid deleted file mode 100644 index c2e25a2b..00000000 --- a/common/ui/fields/portrait_option/portrait_option.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://8i3yw0dywx4c diff --git a/common/ui/fields/portrait_option/portrait_option.tscn b/common/ui/fields/portrait_option/portrait_option.tscn deleted file mode 100644 index 7483cd82..00000000 --- a/common/ui/fields/portrait_option/portrait_option.tscn +++ /dev/null @@ -1,80 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://c3jo73wxyv6ux"] - -[ext_resource type="Script" uid="uid://8i3yw0dywx4c" path="res://common/ui/fields/portrait_option/portrait_option.gd" id="1_0c6eg"] -[ext_resource type="Texture2D" uid="uid://bogfuvhttgn1v" path="res://ui/assets/icons/star.svg" id="3_p62s6"] - -[node name="PortraitOption" type="Panel"] -custom_minimum_size = Vector2(0, 40) -anchors_preset = -1 -anchor_right = 0.096 -anchor_bottom = 0.05 -offset_right = -0.399994 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -script = ExtResource("1_0c6eg") - -[node name="MarginContainer" type="MarginContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"] -layout_mode = 2 - -[node name="Control" type="Control" parent="MarginContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/Control"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="LineEdit" type="LineEdit" parent="MarginContainer/HBoxContainer/Control/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -theme_type_variation = &"LineEditPortraitOption" -text = "default" -flat = true -caret_blink = true -text_direction = 1 - -[node name="Button" type="Button" parent="MarginContainer/HBoxContainer/Control"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = -1 -anchor_left = -0.047 -anchor_top = -0.167 -anchor_right = 1.081 -anchor_bottom = 1.167 -offset_left = 0.0420003 -offset_top = 0.00800037 -offset_right = 0.033989 -offset_bottom = -0.00800133 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 -flat = true - -[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer"] -layout_mode = 2 - -[node name="btnStar" type="TextureButton" parent="MarginContainer/HBoxContainer/HBoxContainer"] -layout_mode = 2 -texture_normal = ExtResource("3_p62s6") -stretch_mode = 5 - -[connection signal="focus_exited" from="MarginContainer/HBoxContainer/Control/HBoxContainer/LineEdit" to="." method="_on_line_edit_focus_exited"] -[connection signal="text_submitted" from="MarginContainer/HBoxContainer/Control/HBoxContainer/LineEdit" to="." method="_on_line_edit_text_submitted"] -[connection signal="gui_input" from="MarginContainer/HBoxContainer/Control/Button" to="." method="_on_button_gui_input"] -[connection signal="pressed" from="MarginContainer/HBoxContainer/Control/Button" to="." method="_on_button_pressed"] -[connection signal="pressed" from="MarginContainer/HBoxContainer/HBoxContainer/btnStar" to="." method="_on_btn_star_pressed"] diff --git a/common/ui/fields/property.gd b/common/ui/fields/property.gd deleted file mode 100644 index 24c5b7ba..00000000 --- a/common/ui/fields/property.gd +++ /dev/null @@ -1,144 +0,0 @@ -## Represents a graph node property and its UI controls in Monologue. -class_name Property extends RefCounted - -## Emitted when property change is to be commited to undo/redo history. -signal change(old_value: Variant, new_value: Variant) -## Emitted if the graph node of this property should be displayed in panel. -signal display -## Emitted when the field's value is being changed and is requesting a preview. -signal preview(value: Variant) -## Emitted on show() and only if the field is visible to the user. -signal shown - -## Dictionary of field method names to argument list values. -var callers: Dictionary = {} -## Reference to UI instance. -var field: Control -## Reference to the field container. -var field_container: Control -## Scene used to instantiate the field's UI control. -var scene: PackedScene -## Dictionary of field property names to set values. -var setters: Dictionary -## Dictionary of callables to connect to field signals. -var connecters: Dictionary -## Temporary boolean to uncollapse the field when first shown if set to true. -var uncollapse: bool -## Initial value of the property. -var default_value: Variant -## Actual value of the property. -var value: Variant: - set = set_value, - get = get_value -## Toggles visibility of the field instance. -var visible: bool: - set = set_visible -## Overwrites the displayed property label -var custom_label: Variant - - -func _init( - ui_scene: PackedScene, - ui_setters: Dictionary = {}, - default: Variant = "", - ui_custom_label: Variant = null -) -> void: - scene = ui_scene - setters = ui_setters - value = default - default_value = default - custom_label = ui_custom_label - visible = true - - -func get_value() -> Variant: - return value - - -## Invokes a given method with the given arguments on the field if present. -func invoke(method_name: String, argument_list: Array) -> Variant: - if is_instance_valid(field): - return field.callv(method_name, argument_list) - return null - - -## Change the property's UI scene and replace the active field instance. -func morph(new_scene: PackedScene) -> void: - scene = new_scene - if is_instance_valid(field): - var panel = field.get_parent() - var child_index = field.get_index() - field_container.queue_free() - show(panel, child_index) - - -func propagate(new_value: Variant, can_display: bool = true) -> void: - preview.emit(new_value) - if is_instance_valid(field): - field.propagate(new_value) - elif can_display or not visible: - uncollapse = true - display.emit() - - -## Trigger a property value change only when valeus are different. -func save_value(new_value: Variant) -> void: - if new_value is Dictionary: - new_value = value.merged(new_value, true) - - if not Util.is_equal(value, new_value): - change.emit(value, new_value) - - -## Setter for value which does not trigger change signals. -func set_value(new_value: Variant) -> void: - value = new_value - - -func set_visible(can_see: bool) -> void: - visible = can_see - _check_visibility() - - -func show(panel: Control, child_index: int = -1, auto_margin: bool = true) -> MonologueField: - field = scene.instantiate() - - field_container = MarginContainer.new() - field_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL - field_container.size_flags_vertical = field.size_flags_vertical - field_container.add_theme_constant_override("margin_right", 0) - field_container.add_theme_constant_override("margin_bottom", 0) - if field is CollapsibleField or field is MonologueList or not auto_margin: - field_container.add_theme_constant_override("margin_left", 0) - field_container.add_theme_constant_override("margin_top", 0) - - for property in setters.keys(): - field.set(property, setters.get(property)) - - field_container.add_child(field) - panel.add_child(field_container) - _check_visibility() - - if child_index >= 0: - panel.move_child(field_container, child_index) - - for method in callers.keys(): - field.callv(method, callers.get(method, [])) - - for callable in connecters.keys(): - var signal_name: String = connecters.get(callable, "") - if field.has_signal(signal_name): - field.connect(connecters.get(callable), callable) - - field.propagate(value) - field.connect("field_changed", preview.emit) - field.connect("field_updated", save_value) - _check_visibility() - if visible: - shown.emit() - return field - - -func _check_visibility(): - if is_instance_valid(field): - field_container.visible = visible diff --git a/common/ui/fields/property.gd.uid b/common/ui/fields/property.gd.uid deleted file mode 100644 index 44756639..00000000 --- a/common/ui/fields/property.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://chah3f6jf1tls diff --git a/common/ui/fields/slider/monologue_slider.gd b/common/ui/fields/slider/monologue_slider.gd deleted file mode 100644 index 72547fc3..00000000 --- a/common/ui/fields/slider/monologue_slider.gd +++ /dev/null @@ -1,59 +0,0 @@ -class_name MonologueSlider extends MonologueField - -@export var default: float -@export var minimum: float -@export var maximum: float -@export var step: float -@export var suffix: String - -@onready var control_label = $FieldLabel -@onready var spin_box = $HBoxContainer/SpinBox -@onready var reset_button = $HBoxContainer/ResetButton -@onready var slider = $HBoxContainer/HSlider - -var skip_spin_box_update: bool = false - - -func _ready(): - slider.min_value = minimum - slider.max_value = maximum - slider.step = step - - spin_box.min_value = minimum - spin_box.max_value = maximum - spin_box.step = step - spin_box.suffix = suffix - - -func set_label_text(text: String) -> void: - control_label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - slider.value = value if (value is float or value is int) else default - - -func _on_drag_ended(value_changed: bool) -> void: - if value_changed: - field_updated.emit(slider.value) - - -func _on_reset() -> void: - if slider.value != default: - slider.value = default - field_updated.emit(default) - - -func _on_value_changed(value: float) -> void: - skip_spin_box_update = true - spin_box.value = value - - -func _on_spin_box_value_changed(value: float) -> void: - if skip_spin_box_update: - skip_spin_box_update = false - return - - slider.value = value - field_updated.emit(slider.value) diff --git a/common/ui/fields/slider/monologue_slider.gd.uid b/common/ui/fields/slider/monologue_slider.gd.uid deleted file mode 100644 index 773fc609..00000000 --- a/common/ui/fields/slider/monologue_slider.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bfviu4cmsm4jo diff --git a/common/ui/fields/slider/monologue_slider.tscn b/common/ui/fields/slider/monologue_slider.tscn deleted file mode 100644 index 5ab0d24e..00000000 --- a/common/ui/fields/slider/monologue_slider.tscn +++ /dev/null @@ -1,72 +0,0 @@ -[gd_scene load_steps=12 format=3 uid="uid://cndkr1vq6ab1o"] - -[ext_resource type="Script" uid="uid://bfviu4cmsm4jo" path="res://common/ui/fields/slider/monologue_slider.gd" id="1_waj5i"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_ntngi"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_ipp53"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_8e8dw"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_cqv1s"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_w48go"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_peeyv"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_delru"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_8xcfa"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_catv5"] - -[sub_resource type="Texture2DRD" id="Texture2DRD_d3004"] - -[node name="MonologueSlider" type="HBoxContainer"] -offset_right = 291.0 -offset_bottom = 29.0 -script = ExtResource("1_waj5i") - -[node name="FieldLabel" parent="." instance=ExtResource("2_ntngi")] -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="HSlider" type="HSlider" parent="HBoxContainer"] -custom_minimum_size = Vector2(100, 0) -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -min_value = -100.0 - -[node name="SpinBox" type="SpinBox" parent="HBoxContainer"] -custom_minimum_size = Vector2(60, 0) -layout_mode = 2 -theme_override_constants/set_min_buttons_width_from_icons = 0 -theme_override_constants/buttons_width = 0 -theme_override_constants/field_and_buttons_separation = 0 -theme_override_constants/buttons_vertical_separation = 0 -theme_override_icons/up_disabled = SubResource("Texture2DRD_ipp53") -theme_override_icons/down = SubResource("Texture2DRD_8e8dw") -theme_override_icons/down_hover = SubResource("Texture2DRD_cqv1s") -theme_override_icons/down_pressed = SubResource("Texture2DRD_w48go") -theme_override_icons/down_disabled = SubResource("Texture2DRD_peeyv") -theme_override_icons/up_pressed = SubResource("Texture2DRD_delru") -theme_override_icons/up_hover = SubResource("Texture2DRD_8xcfa") -theme_override_icons/up = SubResource("Texture2DRD_catv5") -theme_override_icons/updown = SubResource("Texture2DRD_d3004") -alignment = 1 -update_on_text_changed = true -select_all_on_focus = true - -[node name="ResetButton" type="Button" parent="HBoxContainer"] -visible = false -layout_mode = 2 -size_flags_vertical = 4 -theme_type_variation = &"Button_Outline" -text = "reset" - -[connection signal="drag_ended" from="HBoxContainer/HSlider" to="." method="_on_drag_ended"] -[connection signal="value_changed" from="HBoxContainer/HSlider" to="." method="_on_value_changed"] -[connection signal="value_changed" from="HBoxContainer/SpinBox" to="." method="_on_spin_box_value_changed"] -[connection signal="pressed" from="HBoxContainer/ResetButton" to="." method="_on_reset"] diff --git a/common/ui/fields/spin_box/monologue_spin_box.gd b/common/ui/fields/spin_box/monologue_spin_box.gd deleted file mode 100644 index e58b97aa..00000000 --- a/common/ui/fields/spin_box/monologue_spin_box.gd +++ /dev/null @@ -1,31 +0,0 @@ -class_name MonologueSpinBox extends MonologueField - -@export var as_integer: bool = true -@export var minimum: float = -9999999999 -@export var maximum: float = 9999999999 -@export var step: float = 1 -@export var suffix: String - -@onready var label = $Label -@onready var spin_box = $CustomSpinBox - - -func _ready(): - spin_box.min_value = minimum - spin_box.max_value = maximum - spin_box.step = step - spin_box.suffix = suffix - spin_box._update_settings() - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - spin_box.value = value if (value is float or value is int) else 0 - - -func _on_custom_spin_box_value_changed(value: Variant) -> void: - field_updated.emit(value) diff --git a/common/ui/fields/spin_box/monologue_spin_box.gd.uid b/common/ui/fields/spin_box/monologue_spin_box.gd.uid deleted file mode 100644 index 5502bee1..00000000 --- a/common/ui/fields/spin_box/monologue_spin_box.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://catei6iaaw77f diff --git a/common/ui/fields/spin_box/monologue_spin_box.tscn b/common/ui/fields/spin_box/monologue_spin_box.tscn deleted file mode 100644 index b0fb7313..00000000 --- a/common/ui/fields/spin_box/monologue_spin_box.tscn +++ /dev/null @@ -1,16 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://c0d8fac8so0p0"] - -[ext_resource type="Script" uid="uid://catei6iaaw77f" path="res://common/ui/fields/spin_box/monologue_spin_box.gd" id="1_wmtop"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_1ivi1"] -[ext_resource type="PackedScene" uid="uid://wiapsnoaoc44" path="res://common/ui/custom_spinbox/custom_spinbox.tscn" id="3_a42se"] - -[node name="MonologueSpinBox" type="HBoxContainer"] -script = ExtResource("1_wmtop") - -[node name="Label" parent="." instance=ExtResource("2_1ivi1")] -layout_mode = 2 - -[node name="CustomSpinBox" parent="." instance=ExtResource("3_a42se")] -layout_mode = 2 - -[connection signal="value_changed" from="CustomSpinBox" to="." method="_on_custom_spin_box_value_changed"] diff --git a/common/ui/fields/text/monologue_text.gd b/common/ui/fields/text/monologue_text.gd deleted file mode 100644 index d060898b..00000000 --- a/common/ui/fields/text/monologue_text.gd +++ /dev/null @@ -1,33 +0,0 @@ -class_name MonologueText extends MonologueField - -@export var minimum_size := Vector2(200, 200) - -@onready var label = $Label -@onready var text_edit = $HBoxContainer/TextEdit -@onready var expand_container = $HBoxContainer/TextEdit/Button -@onready var expand_button = $HBoxContainer/TextEdit/Button - - -func _ready(): - text_edit.custom_minimum_size = minimum_size - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - text_edit.text = str(value) - - -func _on_focus_exited() -> void: - field_updated.emit(text_edit.text) - - -func _on_text_changed() -> void: - field_changed.emit(text_edit.text) - - -func _on_button_pressed() -> void: - GlobalSignal.emit("expand_text_edit", [text_edit]) diff --git a/common/ui/fields/text/monologue_text.gd.uid b/common/ui/fields/text/monologue_text.gd.uid deleted file mode 100644 index fd975a2c..00000000 --- a/common/ui/fields/text/monologue_text.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://laakj3xm565t diff --git a/common/ui/fields/text/monologue_text.tscn b/common/ui/fields/text/monologue_text.tscn deleted file mode 100644 index 1cd7bc50..00000000 --- a/common/ui/fields/text/monologue_text.tscn +++ /dev/null @@ -1,63 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://durq2yowmkr60"] - -[ext_resource type="Script" uid="uid://laakj3xm565t" path="res://common/ui/fields/text/monologue_text.gd" id="1_m7tlj"] -[ext_resource type="Texture2D" uid="uid://bu603ytypk2jb" path="res://ui/assets/icons/expand.svg" id="2_mkdom"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_nsrvi"] - -[node name="MonologueText" type="HBoxContainer"] -size_flags_horizontal = 3 -script = ExtResource("1_m7tlj") - -[node name="Label" parent="." instance=ExtResource("2_nsrvi")] -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 - -[node name="TextEdit" type="TextEdit" parent="HBoxContainer"] -custom_minimum_size = Vector2(200, 200) -layout_mode = 2 -size_flags_horizontal = 3 -wrap_mode = 1 -caret_blink = true - -[node name="Button" type="Button" parent="HBoxContainer/TextEdit"] -custom_minimum_size = Vector2(22, 22) -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -22.0 -offset_top = 2.0 -offset_right = -2.0 -offset_bottom = 22.0 -grow_horizontal = 0 - -[node name="CenterContainer" type="CenterContainer" parent="HBoxContainer/TextEdit/Button"] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -7.5 -offset_top = -7.5 -offset_right = 7.5 -offset_bottom = 7.5 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 - -[node name="TextureRect" type="TextureRect" parent="HBoxContainer/TextEdit/Button/CenterContainer"] -custom_minimum_size = Vector2(14, 14) -layout_mode = 2 -mouse_filter = 2 -texture = ExtResource("2_mkdom") -expand_mode = 4 -stretch_mode = 5 - -[connection signal="focus_exited" from="HBoxContainer/TextEdit" to="." method="_on_focus_exited"] -[connection signal="text_changed" from="HBoxContainer/TextEdit" to="." method="_on_text_changed"] -[connection signal="pressed" from="HBoxContainer/TextEdit/Button" to="." method="_on_button_pressed"] diff --git a/common/ui/fields/timeline/monologue_timeline.gd b/common/ui/fields/timeline/monologue_timeline.gd deleted file mode 100644 index a2b8b44e..00000000 --- a/common/ui/fields/timeline/monologue_timeline.gd +++ /dev/null @@ -1,327 +0,0 @@ -class_name MonologueAnimationTimeline extends MonologueField - -const IMAGE = ["*.bmp,*.jpg,*.jpeg,*.png,*.svg,*.webp;Image Files"] -const DEFAULT_LAYER_NAME: String = "Layer %s" - -var filters: Array = ["*.bmp", "*.jpg", "*.jpeg", "*.png", "*.svg", "*.webp"] - -@onready var layer_vbox := %LayerVBox -@onready var layer_timeline_vbox := %LayerTimelineVBox -@onready var cell_number_hbox := %CellNumberHBox -@onready var fps_spinbox := %FpsSpinBox -@onready var layer_container := %LayerContainer -@onready var import_frame_button := %ImportFrameButton - -@onready var layer := preload("res://common/ui/fields/timeline/timeline_layer.tscn") -@onready var layer_timeline := preload("res://common/ui/fields/timeline/timeline_cell_layer.tscn") -@onready var cell_number := preload("res://common/ui/fields/timeline/timeline_cell_number.tscn") -@onready var placement_indicator := preload("res://common/ui/horizontal_placement_indicator.tscn") - -var cell_count: int = 1 -var base_path: String -var selected_cell_idx: int = -1 -var selected_cell_layer_idx: int = -1 -var current_indicator: Control -var selected_layer: Layer -var preview_section - -var fps: float = 12.0 - - -func _process(_delta: float) -> void: - if current_indicator == null: - return - var indicator_dist: float = current_indicator.global_position.y - get_global_mouse_position().y - var layer_height: float = get_layer_height() - var layer_dist: float = ( - get_global_mouse_position().y - (selected_layer.global_position.y + layer_height / 2.0) - ) - var indicator_index: int = current_indicator.get_index() - - current_indicator.show() - if indicator_dist >= layer_height / 2.0: - layer_vbox.move_child(current_indicator, indicator_index - 1) - elif indicator_dist <= -layer_height / 2.0: - layer_vbox.move_child(current_indicator, indicator_index + 1) - elif abs(layer_dist) < layer_height: - current_indicator.hide() - - -func _clear() -> void: - cell_count = 1 - for child in layer_vbox.get_children(): - child.queue_free() - for child in layer_timeline_vbox.get_children(): - child.queue_free() - - -func propagate(value: Variant) -> void: - super.propagate(value) - _from_dict(value) - - -func _from_dict(dict: Dictionary) -> void: - _clear() - cell_count = dict.get("FrameCount", 1) - fps_spinbox.value = dict.get("Fps", 12) - selected_cell_idx = -1 - selected_cell_layer_idx = -1 - - var default_layer_data := [ - {"LayerName": DEFAULT_LAYER_NAME % 1, "Frames": {0: {"ImagePath": "", "Exposure": 1}}} - ] - - for layer_data in dict.get("Layers", default_layer_data): - var new_layer: Layer = add_timeline() - new_layer.timeline_label.text = layer_data.get("LayerName", "Layer") - layer_timeline_vbox.get_children().back()._from_dict(layer_data) - - _update_cell_number() - _update_preview.call_deferred() - - -func _to_dict() -> Dictionary: - var dict: Dictionary = {"Fps": fps, "FrameCount": cell_count, "Layers": []} - var layers: Array = get_all_layers() - - for l: Layer in layers: - var layer_idx: int = layers.find(l) - var l_timeline: LayerTimeline = layer_timeline_vbox.get_child(layer_idx) - dict["Layers"].append({"LayerName": l.timeline_label.text, "Frames": l_timeline._to_dict()}) - return dict - - -func get_all_layers() -> Array: - var layers: Array = [] - for child in layer_vbox.get_children(): - if child is not Layer or child.is_queued_for_deletion(): - continue - layers.append(child) - - return layers - - -func get_cell_width() -> int: - return layer_container.cell_width - - -func get_layer_height() -> int: - return get_all_layers()[0].size.y - - -func add_cell() -> void: - cell_count += 1 - _update_cell_number() - - -func add_timeline() -> Layer: - var new_layer: Layer = layer.instantiate() - var new_layer_timeline: LayerTimeline = layer_timeline.instantiate() - new_layer_timeline.timeline = self - - layer_vbox.add_child(new_layer) - layer_timeline_vbox.add_child(new_layer_timeline) - - new_layer.timeline_label.text = DEFAULT_LAYER_NAME % layer_vbox.get_child_count() - new_layer.hover_button.connect("button_down", _on_layer_button_down.bind(new_layer)) - new_layer.hover_button.connect("button_up", _on_layer_button_up.bind(new_layer)) - new_layer.delete_button_pressed.connect(_on_layer_delete_button_pressed.bind(new_layer)) - new_layer_timeline.connect("timeline_updated", _on_timeline_updated.bind(new_layer_timeline)) - - _update_preview() - - return new_layer - - -func _update_cell_number() -> void: - for cell in cell_number_hbox.get_children(): - cell.queue_free() - for i in range(cell_count): - var new_cell := cell_number.instantiate() - new_cell.cell_number = i + 1 - new_cell.custom_minimum_size.x = get_cell_width() - cell_number_hbox.add_child(new_cell) - - -func _update_preview() -> void: - if layer_timeline_vbox == null: - return - var sprites: Array = [] - var layers: Array[Node] = layer_timeline_vbox.get_children() - layers.reverse() - for child_timeline: LayerTimeline in layers: - sprites.append(child_timeline._to_sprite_frames()) - preview_section.update_animation(sprites) - - -func _on_timeline_updated(_layer_timeline: LayerTimeline) -> void: - _update_field.call_deferred() - - -func _update_field() -> void: - _update_preview() - field_updated.emit(_to_dict()) - - -func _on_btn_add_cell_pressed() -> void: - add_cell() - _update_field.call_deferred() - - -func _on_btn_add_layer_pressed() -> void: - add_timeline() - _update_field.call_deferred() - - -func _on_import_frame_button_pressed() -> void: - if selected_cell_idx <= -1 and selected_cell_layer_idx <= -1: - return - GlobalSignal.emit("open_files_request", [_on_files_selected, IMAGE, base_path.get_base_dir()]) - - -func get_selected_cell() -> Variant: - if selected_cell_idx <= -1 and selected_cell_layer_idx <= -1: - return null - - var s_layer_timeline: LayerTimeline = ( - layer_timeline_vbox.get_children()[selected_cell_layer_idx] - ) - return s_layer_timeline.get_all_cells()[selected_cell_idx] - - -func _on_files_selected(paths: Array) -> void: - if selected_cell_idx <= -1 and selected_cell_layer_idx <= -1: - return - - var first_path: String = paths.pop_front() - get_selected_cell().image_path = Path.absolute_to_relative(first_path, base_path) - get_selected_cell()._update() - - var selected_cell_layer: LayerTimeline = layer_timeline_vbox.get_child(selected_cell_layer_idx) - var first_frame_duration: int = int(selected_cell_layer.get_frame_duration(selected_cell_idx)) - var idx: int = 0 - for path in paths: - idx += 1 - var cell: TimelineCell = selected_cell_layer.add_cell( - Path.absolute_to_relative(path, base_path) - ) - selected_cell_layer.hbox.move_child(cell, selected_cell_idx + idx * first_frame_duration) - - for i in range(first_frame_duration - 1): - var exp_cell: TimelineCell = selected_cell_layer.add_cell() - exp_cell.is_exposure = true - selected_cell_layer.hbox.move_child( - exp_cell, selected_cell_idx + idx * first_frame_duration + i + 1 - ) - exp_cell._update() - - _update_field.call_deferred() - - -func cell_selected(s_cell: TimelineCell, s_timeline: LayerTimeline) -> void: - var cell_idx: int = s_timeline.get_all_cells().find(s_cell) - var timeline_idx: int = layer_timeline_vbox.get_children().find(s_timeline) - selected_cell_idx = cell_idx - selected_cell_layer_idx = timeline_idx - sub_select(cell_idx, timeline_idx) - if not s_cell.is_exposure: - import_frame_button.disabled = false - - -func cell_deselected() -> void: - var disable_func: Callable = func() -> void: - if import_frame_button.has_focus(): - return - import_frame_button.disabled = true - selected_cell_idx = -1 - selected_cell_layer_idx = -1 - sub_select(-1, -1) - disable_func.call_deferred() - - -func sub_select(col_idx: int, row_idx: int) -> void: - var deselect: bool = col_idx <= -1 and row_idx <= -1 - for cell in cell_number_hbox.get_children(): - cell.reset_style() - var timeline_idx: int = 0 - for t: LayerTimeline in layer_timeline_vbox.get_children(): - var cell_idx: int = 0 - for cell in t.get_all_cells(): - if cell_idx == col_idx and not deselect: - cell.sub_select() - if row_idx != timeline_idx: - cell.lose_focus() - else: - cell.reset_style() - cell.lose_focus() - cell_idx += 1 - timeline_idx += 1 - if not deselect: - cell_number_hbox.get_child(col_idx).sub_select() - - -func _on_layer_scroll_container_gui_input(_event: InputEvent) -> void: - %LayerTimelineScrollContainer.scroll_vertical = %LayerScrollContainer.scroll_vertical - - -func _on_layer_timeline_scroll_container_gui_input(_event: InputEvent) -> void: - %LayerScrollContainer.scroll_vertical = %LayerTimelineScrollContainer.scroll_vertical - - -func _on_layer_button_down(target_layer: Layer) -> void: - var layer_idx: int = get_all_layers().find(target_layer) - current_indicator = placement_indicator.instantiate() - layer_vbox.add_child(current_indicator) - layer_vbox.move_child(current_indicator, layer_idx + 1) - selected_layer = target_layer - - -func _on_layer_button_up(target_layer: Layer) -> void: - selected_layer = null - if not current_indicator.visible: - current_indicator.free() - current_indicator = null - return - - var new_placement_idx: int = current_indicator.get_index() - var layer_idx: int = get_all_layers().find(target_layer) - var t_layer_timeline: LayerTimeline = layer_timeline_vbox.get_child(layer_idx) - layer_vbox.move_child(target_layer, new_placement_idx) - current_indicator.free() - current_indicator = null - layer_timeline_vbox.move_child(t_layer_timeline, get_all_layers().find(target_layer)) - _update_field.call_deferred() - - -func _on_layer_delete_button_pressed(target_layer: Layer) -> void: - var layer_idx: int = get_all_layers().find(target_layer) - var t_layer_timeline: LayerTimeline = layer_timeline_vbox.get_child(layer_idx - 1) - t_layer_timeline.queue_free() - target_layer.queue_free() - _update_field.call_deferred() - - -func _on_fps_spin_box_value_changed(value: float) -> void: - if value != fps: - fps = value - _update_field.call_deferred() - - -func _on_play_backwards_button_pressed() -> void: - preview_section.play_backwards() - - -func _on_skip_backward_button_pressed() -> void: - pass # Replace with function body. - - -func _on_stop_button_pressed() -> void: - preview_section.stop() - - -func _on_skip_forward_button_pressed() -> void: - pass # Replace with function body. - - -func _on_play_button_pressed() -> void: - preview_section.play() diff --git a/common/ui/fields/timeline/monologue_timeline.gd.uid b/common/ui/fields/timeline/monologue_timeline.gd.uid deleted file mode 100644 index 96e5a4b6..00000000 --- a/common/ui/fields/timeline/monologue_timeline.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bqqjre8f0yujm diff --git a/common/ui/fields/timeline/monologue_timeline.tscn b/common/ui/fields/timeline/monologue_timeline.tscn deleted file mode 100644 index 75b01fb9..00000000 --- a/common/ui/fields/timeline/monologue_timeline.tscn +++ /dev/null @@ -1,366 +0,0 @@ -[gd_scene load_steps=33 format=3 uid="uid://bqbr75kwcpu3i"] - -[ext_resource type="Script" uid="uid://bqqjre8f0yujm" path="res://common/ui/fields/timeline/monologue_timeline.gd" id="1_scldq"] -[ext_resource type="Texture2D" uid="uid://xiahr3jughjg" path="res://ui/assets/icons/plus_min.svg" id="2_qevyo"] -[ext_resource type="Texture2D" uid="uid://fcpb4fd7uma3" path="res://ui/assets/icons/trash_min.svg" id="3_i3rsl"] -[ext_resource type="PackedScene" uid="uid://wiapsnoaoc44" path="res://common/ui/custom_spinbox/custom_spinbox.tscn" id="4_i3rsl"] -[ext_resource type="Texture2D" uid="uid://08g8utdd2kai" path="res://ui/assets/icons/media_play_backward.svg" id="4_wgfvs"] -[ext_resource type="Texture2D" uid="uid://bowdhs0emx5i2" path="res://ui/assets/icons/media_skip_backward.svg" id="5_aaeml"] -[ext_resource type="Texture2D" uid="uid://ctm64x2sdw2y2" path="res://ui/assets/icons/media_stop.svg" id="6_kgr41"] -[ext_resource type="Texture2D" uid="uid://8cxo7ofybuix" path="res://ui/assets/icons/media_skip_forward.svg" id="7_gcvrj"] -[ext_resource type="Texture2D" uid="uid://bficqn5yhfmah" path="res://ui/assets/icons/media_play.svg" id="8_ga8ls"] -[ext_resource type="Texture2D" uid="uid://c07ekrem76mk3" path="res://ui/assets/icons/improt_cell.svg" id="9_vagt2"] -[ext_resource type="Script" uid="uid://csoamuetoupw0" path="res://common/ui/fields/timeline/timeline_layer_cell_container.gd" id="10_6qrpt"] -[ext_resource type="Texture2D" uid="uid://hlck6y4i3l5q" path="res://ui/assets/icons/plus.svg" id="11_u65pl"] -[ext_resource type="PackedScene" uid="uid://c3kq4oc8yxco7" path="res://common/ui/fields/timeline/timeline_cell_number.tscn" id="12_4mkmo"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wlqpu"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.835294, 0.317647, 0.376471, 1) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v62g1"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.84, 0.3192, 0.37996, 0.498039) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8ap2m"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.835294, 0.317647, 0.376471, 1) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4f5yu"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xe0nk"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_arqpt"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_7j2ls"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_argys"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_aaeml"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kgr41"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_gcvrj"] - -[sub_resource type="LabelSettings" id="LabelSettings_86o0e"] -line_spacing = 0.0 -font_size = 14 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vknlb"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xe0nk"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_arqpt"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7j2ls"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qevyo"] -draw_center = false -corner_radius_top_left = 4 -corner_radius_top_right = 4 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_omduo"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_t1qxj"] - -[node name="Timeline" type="PanelContainer"] -offset_right = 528.0 -offset_bottom = 83.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_type_variation = &"CollapsibleFieldPanel" -script = ExtResource("1_scldq") - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 5 - -[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer"] -custom_minimum_size = Vector2(0, 26) -layout_mode = 2 - -[node name="btnAddLayer" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -visible = false -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/focus = SubResource("StyleBoxFlat_wlqpu") -theme_override_styles/hover = SubResource("StyleBoxFlat_v62g1") -theme_override_styles/pressed = SubResource("StyleBoxFlat_8ap2m") -theme_override_styles/normal = SubResource("StyleBoxEmpty_4f5yu") -icon = ExtResource("2_qevyo") -expand_icon = true - -[node name="btnDelete" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -visible = false -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/focus = SubResource("StyleBoxFlat_wlqpu") -theme_override_styles/hover = SubResource("StyleBoxFlat_v62g1") -theme_override_styles/pressed = SubResource("StyleBoxFlat_8ap2m") -theme_override_styles/normal = SubResource("StyleBoxEmpty_4f5yu") -icon = ExtResource("3_i3rsl") -expand_icon = true - -[node name="VSeparator" type="VSeparator" parent="VBoxContainer/PanelContainer/HBoxContainer"] -visible = false -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer/HBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="FpsSpinBox" parent="VBoxContainer/PanelContainer/HBoxContainer/HBoxContainer" instance=ExtResource("4_i3rsl")] -unique_name_in_owner = true -layout_mode = 2 -suffix = "fps" - -[node name="PlayBackwardsButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -icon = ExtResource("4_wgfvs") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="SkipBackwardButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -visible = false -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -icon = ExtResource("5_aaeml") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="StopButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -icon = ExtResource("6_kgr41") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="SkipForwardButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -visible = false -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -icon = ExtResource("7_gcvrj") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="PlayButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -icon = ExtResource("8_ga8ls") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="VSeparator2" type="VSeparator" parent="VBoxContainer/PanelContainer/HBoxContainer"] -layout_mode = 2 - -[node name="ImportFrameButton" type="Button" parent="VBoxContainer/PanelContainer/HBoxContainer"] -unique_name_in_owner = true -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -theme_override_styles/disabled = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_xe0nk") -theme_override_styles/hover = SubResource("StyleBoxEmpty_arqpt") -theme_override_styles/pressed = SubResource("StyleBoxEmpty_7j2ls") -theme_override_styles/normal = SubResource("StyleBoxEmpty_argys") -disabled = true -icon = ExtResource("9_vagt2") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="LayerContainer" type="HSplitContainer" parent="VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/separation = 5 -script = ExtResource("10_6qrpt") - -[node name="LayerContainer" type="PanelContainer" parent="VBoxContainer/LayerContainer"] -custom_minimum_size = Vector2(150, 0) -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_aaeml") - -[node name="MainLayerVBox" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox"] -custom_minimum_size = Vector2(0, 35) -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_kgr41") - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/PanelContainer"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/separation = 0 - -[node name="PanelContainer4" type="PanelContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/PanelContainer/HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_styles/panel = SubResource("StyleBoxEmpty_gcvrj") - -[node name="Label" type="Label" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/PanelContainer/HBoxContainer/PanelContainer4"] -layout_mode = 2 -label_settings = SubResource("LabelSettings_86o0e") - -[node name="LayerScrollContainer" type="ScrollContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_vertical = 3 -vertical_scroll_mode = 3 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/LayerScrollContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 - -[node name="LayerVBox" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/LayerScrollContainer/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="btnAddLayer" type="Button" parent="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/LayerScrollContainer/VBoxContainer"] -custom_minimum_size = Vector2(24, 24) -layout_mode = 2 -theme_override_constants/icon_max_width = 10 -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_vknlb") -theme_override_styles/hover = SubResource("StyleBoxFlat_xe0nk") -theme_override_styles/pressed = SubResource("StyleBoxFlat_arqpt") -theme_override_styles/normal = SubResource("StyleBoxFlat_7j2ls") -icon = ExtResource("11_u65pl") -icon_alignment = 1 - -[node name="LayerTimelineContainer" type="ScrollContainer" parent="VBoxContainer/LayerContainer"] -layout_mode = 2 -vertical_scroll_mode = 0 - -[node name="LayerTimelineVBox" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - -[node name="FrameCountTimeline" type="PanelContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox"] -clip_contents = true -custom_minimum_size = Vector2(0, 25) -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_qevyo") - -[node name="HBox" type="HBoxContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/FrameCountTimeline"] -layout_mode = 2 -size_flags_vertical = 3 -theme_override_constants/separation = 0 - -[node name="CellNumberHBox" type="HBoxContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/FrameCountTimeline/HBox"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="CellNumber" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/FrameCountTimeline/HBox/CellNumberHBox" instance=ExtResource("12_4mkmo")] -custom_minimum_size = Vector2(27, 35) -layout_mode = 2 - -[node name="PanelContainer6" type="PanelContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/FrameCountTimeline/HBox"] -custom_minimum_size = Vector2(27, 26) -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_omduo") - -[node name="LayerTimelineScrollContainer" type="ScrollContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -follow_focus = true -horizontal_scroll_mode = 0 - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/LayerTimelineScrollContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_override_constants/separation = 0 - -[node name="LayerTimelineVBox" type="VBoxContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/LayerTimelineScrollContainer/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 - -[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/LayerTimelineScrollContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 24) -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_t1qxj") - -[connection signal="value_changed" from="VBoxContainer/PanelContainer/HBoxContainer/HBoxContainer/FpsSpinBox" to="." method="_on_fps_spin_box_value_changed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/PlayBackwardsButton" to="." method="_on_play_backwards_button_pressed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/SkipBackwardButton" to="." method="_on_skip_backward_button_pressed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/StopButton" to="." method="_on_stop_button_pressed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/SkipForwardButton" to="." method="_on_skip_forward_button_pressed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/PlayButton" to="." method="_on_play_button_pressed"] -[connection signal="pressed" from="VBoxContainer/PanelContainer/HBoxContainer/ImportFrameButton" to="." method="_on_import_frame_button_pressed"] -[connection signal="mouse_entered" from="VBoxContainer/LayerContainer" to="VBoxContainer/LayerContainer" method="_on_mouse_entered"] -[connection signal="mouse_exited" from="VBoxContainer/LayerContainer" to="VBoxContainer/LayerContainer" method="_on_mouse_exited"] -[connection signal="gui_input" from="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/LayerScrollContainer" to="." method="_on_layer_scroll_container_gui_input"] -[connection signal="pressed" from="VBoxContainer/LayerContainer/LayerContainer/MainLayerVBox/LayerScrollContainer/VBoxContainer/btnAddLayer" to="." method="_on_btn_add_layer_pressed"] -[connection signal="gui_input" from="VBoxContainer/LayerContainer/LayerTimelineContainer/LayerTimelineVBox/LayerTimelineScrollContainer" to="." method="_on_layer_timeline_scroll_container_gui_input"] diff --git a/common/ui/fields/timeline/timeline_cell.gd b/common/ui/fields/timeline/timeline_cell.gd deleted file mode 100644 index ef0a95ea..00000000 --- a/common/ui/fields/timeline/timeline_cell.gd +++ /dev/null @@ -1,105 +0,0 @@ -class_name TimelineCell extends PanelContainer - -signal button_down -signal button_up -signal button_focus_exited - -@onready var single_cell_texture := preload("res://ui/assets/icons/cell_single.svg") -@onready var empty_cell_texture := preload("res://ui/assets/icons/cell_empty.svg") -@onready var left_cell_texture := preload("res://ui/assets/icons/cell_left.svg") -@onready var right_cell_texture := preload("res://ui/assets/icons/cell_right.svg") -@onready var middle_cell_texture := preload("res://ui/assets/icons/cell_middle.svg") - -@onready var button := %Button -@onready var line_indicator := %LineIndicator -@onready var texture_rect := $TextureContainer/TextureRect -@onready var texture_container := $TextureContainer -@onready var hflow := %HFlow - -var image_path: String: - set = _set_image_path -var is_exposure: bool = false # If is the same frame as the previous one -var timeline: LayerTimeline - - -func _set_image_path(value: String) -> void: - image_path = value - _update() - - -func _ready() -> void: - GlobalSignal.add_listener("timeline_zoom_in", _on_timeline_zoom) - GlobalSignal.add_listener("timeline_zoom_out", _on_timeline_zoom) - _update() - - -func _update() -> void: - if not is_exposure and not image_path.is_empty(): - var root_dir = timeline.timeline.base_path.get_base_dir() + Path.get_separator() - texture_rect.texture = ImageLoader.load_thumbnail( - Path.relative_to_absolute(image_path, root_dir) - ) - - texture_container.visible = !is_exposure - line_indicator.visible = is_exposure - - -func lose_focus() -> void: - button.button_pressed = false - - -func get_base_sb() -> StyleBoxFlat: - # TODO: Use theme variation instead - var sb: StyleBox = StyleBoxFlat.new() - sb.bg_color = Color("d651613f") - sb.border_color = Color("1e1e21") - sb.border_width_right = 1 - return sb - - -func sub_select() -> void: - add_theme_stylebox_override("panel", get_base_sb()) - - -func reset_style() -> void: - var sb: StyleBox = get_base_sb() - sb.draw_center = false - add_theme_stylebox_override("panel", sb) - - -func _on_timeline_zoom(cell_width: int) -> void: - custom_minimum_size.x = cell_width - - -func _on_button_button_down() -> void: - button_down.emit() - - -func _on_button_button_up() -> void: - button_up.emit() - - -func _on_button_toggled(toggled_on: bool) -> void: - if toggled_on == false: - button_focus_exited.emit() - - -func _on_inc_exposure_button_pressed() -> void: - timeline.add_exposure(self) - - -func _on_mouse_entered() -> void: - if timeline.current_indicator == null: - hflow.show() - - -func _on_mouse_exited() -> void: - hflow.hide() - - -func _on_dec_exposure_button_pressed() -> void: - timeline.remove_cell(self) - - -func _on_button_focus_exited() -> void: - button_focus_exited.emit() diff --git a/common/ui/fields/timeline/timeline_cell.gd.uid b/common/ui/fields/timeline/timeline_cell.gd.uid deleted file mode 100644 index cb1930dd..00000000 --- a/common/ui/fields/timeline/timeline_cell.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bstbijin8qrmw diff --git a/common/ui/fields/timeline/timeline_cell.tscn b/common/ui/fields/timeline/timeline_cell.tscn deleted file mode 100644 index 1ec2c598..00000000 --- a/common/ui/fields/timeline/timeline_cell.tscn +++ /dev/null @@ -1,152 +0,0 @@ -[gd_scene load_steps=11 format=3 uid="uid://cdpox07h3wlf3"] - -[ext_resource type="Script" uid="uid://bstbijin8qrmw" path="res://common/ui/fields/timeline/timeline_cell.gd" id="1_w2yvg"] -[ext_resource type="Script" uid="uid://dqhwi718vnyxk" path="res://common/layouts/character_edit/alpha_bg.gd" id="2_04ohn"] -[ext_resource type="Texture2D" uid="uid://2avo8aox8ls2" path="res://ui/assets/icons/minus_min.svg" id="3_tep1x"] -[ext_resource type="Texture2D" uid="uid://xiahr3jughjg" path="res://ui/assets/icons/plus_min.svg" id="4_ob7f1"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y3h2k"] -draw_center = false -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xo2vn"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1614f"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.835294, 0.317647, 0.376471, 1) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y37jy"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.84, 0.3192, 0.37996, 0.498039) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_5et5j"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7wlkg"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[node name="Cell" type="PanelContainer"] -custom_minimum_size = Vector2(27, 52) -offset_right = 27.0 -offset_bottom = 52.0 -theme_override_styles/panel = SubResource("StyleBoxFlat_y3h2k") -script = ExtResource("1_w2yvg") - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -alignment = 1 - -[node name="LineIndicator" type="ColorRect" parent="VBox"] -unique_name_in_owner = true -visible = false -custom_minimum_size = Vector2(0, 2) -layout_mode = 2 - -[node name="TextureContainer" type="PanelContainer" parent="."] -clip_contents = true -layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_xo2vn") - -[node name="AlphaBG" type="ColorRect" parent="TextureContainer"] -layout_mode = 2 -script = ExtResource("2_04ohn") - -[node name="TextureRect" type="TextureRect" parent="TextureContainer"] -custom_minimum_size = Vector2(26, 26) -layout_mode = 2 -expand_mode = 1 -stretch_mode = 5 - -[node name="IncExposureContainer" type="Control" parent="."] -layout_mode = 2 -mouse_filter = 2 - -[node name="Button" type="Button" parent="IncExposureContainer"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 1 -theme_override_styles/focus = SubResource("StyleBoxFlat_1614f") -theme_override_styles/hover = SubResource("StyleBoxFlat_y37jy") -theme_override_styles/pressed = SubResource("StyleBoxFlat_1614f") -theme_override_styles/normal = SubResource("StyleBoxEmpty_5et5j") -keep_pressed_outside = true - -[node name="HFlow" type="HFlowContainer" parent="IncExposureContainer"] -unique_name_in_owner = true -visible = false -layout_mode = 1 -anchors_preset = 14 -anchor_top = 0.5 -anchor_right = 1.0 -anchor_bottom = 0.5 -offset_top = -12.0 -offset_bottom = 12.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -alignment = 1 -last_wrap_alignment = 2 - -[node name="DecHBox" type="HBoxContainer" parent="IncExposureContainer/HFlow"] -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 - -[node name="DecExposureButton" type="Button" parent="IncExposureContainer/HFlow/DecHBox"] -custom_minimum_size = Vector2(24, 24) -layout_mode = 2 -mouse_filter = 1 -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/hover = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/pressed = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/normal = SubResource("StyleBoxFlat_7wlkg") -icon = ExtResource("3_tep1x") -expand_icon = true - -[node name="IncHBox" type="HBoxContainer" parent="IncExposureContainer/HFlow"] -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 -alignment = 2 - -[node name="IncExposureButton" type="Button" parent="IncExposureContainer/HFlow/IncHBox"] -custom_minimum_size = Vector2(24, 24) -layout_mode = 2 -mouse_filter = 1 -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/hover = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/pressed = SubResource("StyleBoxFlat_7wlkg") -theme_override_styles/normal = SubResource("StyleBoxFlat_7wlkg") -icon = ExtResource("4_ob7f1") -expand_icon = true - -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] -[connection signal="button_down" from="IncExposureContainer/Button" to="." method="_on_button_button_down"] -[connection signal="button_up" from="IncExposureContainer/Button" to="." method="_on_button_button_up"] -[connection signal="focus_exited" from="IncExposureContainer/Button" to="." method="_on_button_focus_exited"] -[connection signal="toggled" from="IncExposureContainer/Button" to="." method="_on_button_toggled"] -[connection signal="pressed" from="IncExposureContainer/HFlow/DecHBox/DecExposureButton" to="." method="_on_dec_exposure_button_pressed"] -[connection signal="pressed" from="IncExposureContainer/HFlow/IncHBox/IncExposureButton" to="." method="_on_inc_exposure_button_pressed"] diff --git a/common/ui/fields/timeline/timeline_cell_layer.gd b/common/ui/fields/timeline/timeline_cell_layer.gd deleted file mode 100644 index eb117611..00000000 --- a/common/ui/fields/timeline/timeline_cell_layer.gd +++ /dev/null @@ -1,230 +0,0 @@ -class_name LayerTimeline extends PanelContainer - -signal timeline_updated - -@onready var hbox := %HBox - -var timeline: MonologueAnimationTimeline -var timeline_cell := preload("res://common/ui/fields/timeline/timeline_cell.tscn") -var placement_indicator := preload("res://common/ui/vertical_placement_indicator.tscn") - -var current_indicator: Control -var selected_cell: TimelineCell - - -func _ready() -> void: - add_cell() - - -func add_cell(image_path = "") -> TimelineCell: - var cells := get_all_cells() - var is_exposure: bool = false if image_path != null else cells.size() > 0 - - var new_cell := timeline_cell.instantiate() - new_cell.timeline = self - hbox.add_child(new_cell) - new_cell.is_exposure = is_exposure - new_cell.custom_minimum_size.x = timeline.get_cell_width() - new_cell.image_path = image_path - new_cell._update() - new_cell.connect("button_down", _on_cell_button_down.bind(new_cell)) - new_cell.connect("button_up", _on_cell_button_up.bind(new_cell)) - new_cell.connect("button_focus_exited", _on_cell_focus_exited) - - if get_all_cells().size() > timeline.cell_count: - timeline.add_cell() - - return new_cell - - -func _on_cell_button_down(cell: TimelineCell) -> void: - current_indicator = placement_indicator.instantiate() - hbox.add_child(current_indicator) - hbox.move_child(current_indicator, cell.get_index() + 1) - current_indicator.show() - selected_cell = cell - - -func _on_cell_button_up(cell: TimelineCell) -> void: - selected_cell = null - if not current_indicator.visible: - timeline.cell_selected(cell, self) - current_indicator.queue_free() - current_indicator = null - return - - var indicator_idx = current_indicator.get_index() - hbox.move_child(cell, indicator_idx) - - current_indicator.queue_free() - current_indicator = null - timeline.selected_cell_idx = get_all_cells().find(cell) - timeline.selected_cell_layer_idx = timeline.layer_timeline_vbox.get_children().find(self) - - var first_cell: TimelineCell = get_all_cells()[0] - if first_cell.is_exposure: - first_cell.is_exposure = false - first_cell._update() - - timeline_updated.emit() - - for child in get_all_cells(): - if child == cell: - continue - - child.lose_focus() - - -func _on_cell_focus_exited() -> void: - get_all_cells()[timeline.selected_cell_idx].reset_style() - timeline.cell_deselected() - - -func _process(_delta: float) -> void: - if current_indicator == null: - return - - var indicator_dist: float = current_indicator.global_position.x - get_global_mouse_position().x - var cell_width: float = timeline.get_cell_width() - var cell_dist: float = ( - get_global_mouse_position().x - (selected_cell.global_position.x + cell_width / 2.0) - ) - var indicator_index: int = current_indicator.get_index() - current_indicator.show() - if indicator_dist >= cell_width / 2.0: - hbox.move_child(current_indicator, indicator_index - 1) - elif indicator_dist <= -cell_width / 2.0: - hbox.move_child(current_indicator, indicator_index + 1) - elif abs(cell_dist) < cell_width: - current_indicator.hide() - - -func remove_cell(cell: TimelineCell) -> void: - var cells: Array = get_all_cells() - var index: int = cells.find(cell) - - if not cell.is_exposure and have_exposure_after(cell): - var c_after: TimelineCell = cells[index + 1] - c_after.is_exposure = false - c_after.image_path = cell.image_path - c_after._update() - - hbox.remove_child(cell) - cell.queue_free() - - timeline_updated.emit() - - -func fill() -> void: - _clear() - for _i in range(timeline.cell_count): - add_cell() - - -func _clear() -> void: - for cell in get_all_cells(): - cell.queue_free() - - -func _from_dict(dict: Dictionary) -> void: - _clear() - var frames: Dictionary = dict.get("Frames") - for frame_idx in frames.keys(): - for i in range(frames[frame_idx].get("Exposure", 1)): - var frame_data: Dictionary = frames[frame_idx] - var cell := add_cell() - cell.is_exposure = i > 0 - if i <= 0: - cell.image_path = frame_data.get("ImagePath", "") - cell._update() - - -func _to_dict() -> Dictionary: - var dict: Dictionary = {} - var cells: Array = get_all_cells() - for cell: TimelineCell in cells: - if cell.is_exposure: - continue - - var cell_idx: int = cells.find(cell) - dict[cell_idx] = {"ImagePath": cell.image_path, "Exposure": get_frame_duration(cell_idx)} - - return dict - - -func get_all_cells() -> Array: - var cells: Array = [] - for child in hbox.get_children(): - if child is not TimelineCell or child.is_queued_for_deletion(): - continue - cells.append(child) - - return cells - - -func _to_sprite_frames() -> SpriteFrames: - var sprite_frames := SpriteFrames.new() - sprite_frames.set_animation_speed("default", timeline.fps) - - var cells: Array = get_all_cells() - for i in range(timeline.cell_count): - var texture: Texture2D - var frame_duration: float = 1.0 - - if cells.size() > i: - var cell: TimelineCell = cells[i - 1] - if cell.is_exposure: - continue - - var idx = cells.find(cell) - frame_duration = get_frame_duration(idx) - - var root_dir = timeline.base_path.get_base_dir() + Path.get_separator() - var frame_path: String = Path.relative_to_absolute(cell.image_path, root_dir) - if FileAccess.file_exists(frame_path): - texture = ImageLoader.load_image(frame_path) - else: - texture = Texture2D.new() - - sprite_frames.add_frame("default", texture, frame_duration, i) - - return sprite_frames - - -func get_frame_duration(frame_idx: int) -> float: - var duration: float = 1.0 - - var cells: Array = get_all_cells().slice(frame_idx + 1) - for cell: TimelineCell in cells: - if cell.is_exposure: - duration += 1.0 - continue - break - - return duration - - -func add_exposure(of_cell: TimelineCell): - var index: int = get_all_cells().find(of_cell) - - var cell: TimelineCell = add_cell() - cell.is_exposure = true - hbox.move_child(cell, index + 1) - cell._on_mouse_exited() - timeline_updated.emit() - - -func _on_button_pressed() -> void: - var cell: TimelineCell = add_cell() - cell.is_exposure = false - cell._update() - timeline_updated.emit() - - -func have_exposure_after(cell: TimelineCell) -> bool: - var cells := get_all_cells() - var index: int = cells.find(cell) - if index == cells.size() - 1: - return false - - return cells[index + 1].is_exposure diff --git a/common/ui/fields/timeline/timeline_cell_layer.gd.uid b/common/ui/fields/timeline/timeline_cell_layer.gd.uid deleted file mode 100644 index 52d99c5c..00000000 --- a/common/ui/fields/timeline/timeline_cell_layer.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dt0lru5kmkesc diff --git a/common/ui/fields/timeline/timeline_cell_layer.tscn b/common/ui/fields/timeline/timeline_cell_layer.tscn deleted file mode 100644 index 022b82ef..00000000 --- a/common/ui/fields/timeline/timeline_cell_layer.tscn +++ /dev/null @@ -1,44 +0,0 @@ -[gd_scene load_steps=7 format=3 uid="uid://hc8mgc7ndi5d"] - -[ext_resource type="Script" uid="uid://dt0lru5kmkesc" path="res://common/ui/fields/timeline/timeline_cell_layer.gd" id="1_uew4g"] -[ext_resource type="Texture2D" uid="uid://hlck6y4i3l5q" path="res://ui/assets/icons/plus.svg" id="2_68mbi"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0ynx3"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jdrfe"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wroo4"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_abmyl"] -bg_color = Color(0.117647, 0.117647, 0.129412, 1) - -[node name="LayerTimeline" type="PanelContainer"] -custom_minimum_size = Vector2(0, 52) -size_flags_horizontal = 3 -theme_type_variation = &"ItemContainerFlat" -script = ExtResource("1_uew4g") - -[node name="BaseContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="HBox" type="HBoxContainer" parent="BaseContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="Button" type="Button" parent="BaseContainer"] -custom_minimum_size = Vector2(24, 0) -layout_mode = 2 -theme_override_constants/icon_max_width = 10 -theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_0ynx3") -theme_override_styles/hover = SubResource("StyleBoxFlat_jdrfe") -theme_override_styles/pressed = SubResource("StyleBoxFlat_wroo4") -theme_override_styles/normal = SubResource("StyleBoxFlat_abmyl") -icon = ExtResource("2_68mbi") -icon_alignment = 1 - -[connection signal="pressed" from="BaseContainer/Button" to="." method="_on_button_pressed"] diff --git a/common/ui/fields/timeline/timeline_cell_number.tscn b/common/ui/fields/timeline/timeline_cell_number.tscn deleted file mode 100644 index 40e27468..00000000 --- a/common/ui/fields/timeline/timeline_cell_number.tscn +++ /dev/null @@ -1,100 +0,0 @@ -[gd_scene load_steps=6 format=3 uid="uid://c3kq4oc8yxco7"] - -[sub_resource type="GDScript" id="GDScript_aiax2"] -script/source = "extends PanelContainer - -signal selected(cell_number: int) - -@onready var label: Label = $Label - -@export var cell_number: int = 1 - - -func _ready() -> void: - label.text = str(cell_number) - GlobalSignal.add_listener(\"timeline_zoom_in\", _on_timeline_zoom) - GlobalSignal.add_listener(\"timeline_zoom_out\", _on_timeline_zoom) - - -func get_base_sb() -> StyleBoxFlat: - # TODO: Use theme variation instead - var sb: StyleBox = StyleBoxFlat.new() - sb.bg_color = Color(\"d651613f\") - sb.border_color = Color(\"1e1e21\") - sb.border_width_right = 1 - return sb - - -func sub_select() -> void: - add_theme_stylebox_override(\"panel\", get_base_sb()) - -func reset_style() -> void: - var sb: StyleBox = get_base_sb() - sb.draw_center = false - add_theme_stylebox_override(\"panel\", sb) - - -func _on_button_pressed() -> void: - selected.emit(cell_number) - - -func _on_timeline_zoom(cell_width: int) -> void: - custom_minimum_size.x = cell_width - - -func _on_minimum_size_changed() -> void: - custom_minimum_size.x = max(custom_minimum_size.x, 27.0) -" - -[sub_resource type="LabelSettings" id="LabelSettings_jbxed"] -line_spacing = 0.0 -font_size = 14 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fi15l"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.835294, 0.317647, 0.376471, 1) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vog8c"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.84, 0.3192, 0.37996, 0.498039) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_x14x4"] - -[node name="CellNumber" type="PanelContainer"] -custom_minimum_size = Vector2(27, 26) -theme_type_variation = &"TimelineCellNumber" -script = SubResource("GDScript_aiax2") - -[node name="Label" type="Label" parent="."] -layout_mode = 2 -text = "1" -label_settings = SubResource("LabelSettings_jbxed") -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Button" type="Button" parent="."] -visible = false -layout_mode = 2 -theme_override_styles/focus = SubResource("StyleBoxFlat_fi15l") -theme_override_styles/hover = SubResource("StyleBoxFlat_vog8c") -theme_override_styles/pressed = SubResource("StyleBoxFlat_fi15l") -theme_override_styles/normal = SubResource("StyleBoxEmpty_x14x4") - -[connection signal="minimum_size_changed" from="." to="." method="_on_minimum_size_changed"] -[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] diff --git a/common/ui/fields/timeline/timeline_layer.gd b/common/ui/fields/timeline/timeline_layer.gd deleted file mode 100644 index 72a293ce..00000000 --- a/common/ui/fields/timeline/timeline_layer.gd +++ /dev/null @@ -1,10 +0,0 @@ -class_name Layer extends PanelContainer - -signal delete_button_pressed - -@onready var timeline_label := %Label -@onready var hover_button := %HoverButton - - -func _on_delete_button_pressed() -> void: - delete_button_pressed.emit() diff --git a/common/ui/fields/timeline/timeline_layer.gd.uid b/common/ui/fields/timeline/timeline_layer.gd.uid deleted file mode 100644 index 8d065b20..00000000 --- a/common/ui/fields/timeline/timeline_layer.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bodradtjajfd7 diff --git a/common/ui/fields/timeline/timeline_layer.tscn b/common/ui/fields/timeline/timeline_layer.tscn deleted file mode 100644 index 6db97d30..00000000 --- a/common/ui/fields/timeline/timeline_layer.tscn +++ /dev/null @@ -1,88 +0,0 @@ -[gd_scene load_steps=7 format=3 uid="uid://d2ekvpgbp07fe"] - -[ext_resource type="Script" uid="uid://bodradtjajfd7" path="res://common/ui/fields/timeline/timeline_layer.gd" id="1_teuw3"] -[ext_resource type="PackedScene" uid="uid://dfwf55ovgwir3" path="res://common/ui/buttons/delete_button.tscn" id="2_ta0kq"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_cpp16"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6c6x0"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.84, 0.3192, 0.37996, 0.498039) -corner_radius_top_left = 4 -corner_radius_top_right = 4 -corner_radius_bottom_right = 4 -corner_radius_bottom_left = 4 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j41u5"] -draw_center = false -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.835294, 0.317647, 0.376471, 1) -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[sub_resource type="LabelSettings" id="LabelSettings_86o0e"] -line_spacing = 0.0 -font_size = 14 - -[node name="Layer" type="PanelContainer"] -custom_minimum_size = Vector2(0, 52) -theme_type_variation = &"TimelineLayerPanel" -script = ExtResource("1_teuw3") - -[node name="Control" type="Control" parent="."] -layout_mode = 2 -mouse_filter = 2 - -[node name="HoverButton" type="Button" parent="Control"] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -8.0 -offset_top = -8.0 -offset_right = 8.0 -offset_bottom = 8.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_styles/focus = SubResource("StyleBoxEmpty_cpp16") -theme_override_styles/hover = SubResource("StyleBoxFlat_6c6x0") -theme_override_styles/pressed = SubResource("StyleBoxFlat_j41u5") -theme_override_styles/normal = SubResource("StyleBoxEmpty_cpp16") -keep_pressed_outside = true - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 - -[node name="Label" type="Label" parent="HBoxContainer/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 3 -text = "Layer 1" -label_settings = SubResource("LabelSettings_86o0e") - -[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] -layout_mode = 2 -mouse_filter = 2 -alignment = 1 - -[node name="DeleteButton" parent="HBoxContainer/VBoxContainer" instance=ExtResource("2_ta0kq")] -layout_mode = 2 - -[connection signal="pressed" from="HBoxContainer/VBoxContainer/DeleteButton" to="." method="_on_delete_button_pressed"] diff --git a/common/ui/fields/timeline/timeline_layer_cell_container.gd b/common/ui/fields/timeline/timeline_layer_cell_container.gd deleted file mode 100644 index 217915b6..00000000 --- a/common/ui/fields/timeline/timeline_layer_cell_container.gd +++ /dev/null @@ -1,38 +0,0 @@ -extends HSplitContainer - -const MAX_CELL_WIDTH: int = 150 -const MIN_CELL_WIDTH: int = 26 - -@onready var layer_timeline_scroll_container := %LayerTimelineScrollContainer - -var mouse_hover: bool = false -var cell_width: int = 75 - - -func _input(_event: InputEvent) -> void: - # Disable scroll container if Ctrl is pressed - if Input.is_action_just_pressed("Ctrl"): - layer_timeline_scroll_container.mouse_filter = MOUSE_FILTER_IGNORE - elif Input.is_action_just_released("Ctrl"): - layer_timeline_scroll_container.mouse_filter = MOUSE_FILTER_PASS - - -func _gui_input(event: InputEvent) -> void: - if event is InputEventMouseButton and event.is_pressed() and Input.is_action_pressed("Ctrl"): - var t: float = remap(cell_width, MIN_CELL_WIDTH, MAX_CELL_WIDTH, 0.0, 1.0) - # zoom in - if event.button_index == MOUSE_BUTTON_WHEEL_UP: - cell_width = min(cell_width + lerp(2.0, 12.0, pow(t, 2)), MAX_CELL_WIDTH) - GlobalSignal.emit("timeline_zoom_in", [cell_width]) - # zoom out - if event.button_index == MOUSE_BUTTON_WHEEL_DOWN: - cell_width = max(cell_width - lerp(2.0, 12.0, pow(t, 2)), MIN_CELL_WIDTH) - GlobalSignal.emit("timeline_zoom_out", [cell_width]) - - -func _on_mouse_entered() -> void: - mouse_hover = true - - -func _on_mouse_exited() -> void: - mouse_hover = false diff --git a/common/ui/fields/timeline/timeline_layer_cell_container.gd.uid b/common/ui/fields/timeline/timeline_layer_cell_container.gd.uid deleted file mode 100644 index bb8612df..00000000 --- a/common/ui/fields/timeline/timeline_layer_cell_container.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://csoamuetoupw0 diff --git a/common/ui/fields/toggle/monologue_toggle.gd b/common/ui/fields/toggle/monologue_toggle.gd deleted file mode 100644 index 0146dca8..00000000 --- a/common/ui/fields/toggle/monologue_toggle.gd +++ /dev/null @@ -1,17 +0,0 @@ -class_name MonologueToggle extends MonologueField - -@onready var label = $Label -@onready var check_button = %CheckButton - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - check_button.set_pressed_no_signal(value if value is bool else false) - - -func _on_check_button_toggled(toggled_on: bool) -> void: - field_updated.emit(toggled_on) diff --git a/common/ui/fields/toggle/monologue_toggle.gd.uid b/common/ui/fields/toggle/monologue_toggle.gd.uid deleted file mode 100644 index 1bd63073..00000000 --- a/common/ui/fields/toggle/monologue_toggle.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ctumcy1k8si7o diff --git a/common/ui/fields/toggle/monologue_toggle.tscn b/common/ui/fields/toggle/monologue_toggle.tscn deleted file mode 100644 index 76a379ce..00000000 --- a/common/ui/fields/toggle/monologue_toggle.tscn +++ /dev/null @@ -1,22 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://dh7yuosc0hhpp"] - -[ext_resource type="Script" uid="uid://ctumcy1k8si7o" path="res://common/ui/fields/toggle/monologue_toggle.gd" id="1_0u5l0"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_tj6ps"] - -[node name="MonologueCheckButton" type="HBoxContainer"] -offset_right = 43.0 -offset_bottom = 29.0 -script = ExtResource("1_0u5l0") - -[node name="Label" parent="." instance=ExtResource("2_tj6ps")] -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 2 -alignment = 1 - -[node name="CheckButton" type="CheckButton" parent="VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 - -[connection signal="toggled" from="VBoxContainer/CheckButton" to="." method="_on_check_button_toggled"] diff --git a/common/ui/fields/vector/monologue_vector.gd b/common/ui/fields/vector/monologue_vector.gd deleted file mode 100644 index 98096304..00000000 --- a/common/ui/fields/vector/monologue_vector.gd +++ /dev/null @@ -1,38 +0,0 @@ -class_name MonologueVector extends MonologueField - -@export var minimum: float = -9999999999 -@export var maximum: float = 9999999999 -@export var step: float = 1 - -@onready var label = $FieldLabel -@onready var x_spin_box := %XSpinBox -@onready var y_spin_box := %YSpinBox - -var ribbon_scene = preload("res://common/ui/ribbon/ribbon.tscn") - - -func _ready() -> void: - x_spin_box.min_value = minimum - y_spin_box.min_value = minimum - x_spin_box.max_value = maximum - y_spin_box.max_value = maximum - x_spin_box.step = step - y_spin_box.step = step - - -func set_label_text(text: String) -> void: - label.text = text - - -func propagate(value: Variant) -> void: - super.propagate(value) - x_spin_box.value = value[0] if (value[0] is float or value[0] is int) else 0 - y_spin_box.value = value[1] if (value[1] is float or value[1] is int) else 0 - - -func _on_focus_exited() -> void: - _on_spin_box_value_changed() - - -func _on_spin_box_value_changed(_value: float = 0.0) -> void: - field_updated.emit([x_spin_box.value, y_spin_box.value]) diff --git a/common/ui/fields/vector/monologue_vector.gd.uid b/common/ui/fields/vector/monologue_vector.gd.uid deleted file mode 100644 index 8d95749d..00000000 --- a/common/ui/fields/vector/monologue_vector.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://37empx7gweoa diff --git a/common/ui/fields/vector/monologue_vector.tscn b/common/ui/fields/vector/monologue_vector.tscn deleted file mode 100644 index b7f17d93..00000000 --- a/common/ui/fields/vector/monologue_vector.tscn +++ /dev/null @@ -1,47 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://5ciquxsl5tw5"] - -[ext_resource type="Script" uid="uid://37empx7gweoa" path="res://common/ui/fields/vector/monologue_vector.gd" id="1_qpl0j"] -[ext_resource type="PackedScene" uid="uid://x0daq5tsejey" path="res://common/ui/fields/field_label.tscn" id="2_8olwg"] -[ext_resource type="PackedScene" uid="uid://wiapsnoaoc44" path="res://common/ui/custom_spinbox/custom_spinbox.tscn" id="2_034ks"] - -[node name="MonologueVector" type="HBoxContainer"] -offset_right = 282.0 -offset_bottom = 29.0 -script = ExtResource("1_qpl0j") - -[node name="FieldLabel" parent="." instance=ExtResource("2_8olwg")] -layout_mode = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="HBoxContainer/HBoxContainer"] -layout_mode = 2 -text = "x" - -[node name="XSpinBox" parent="HBoxContainer/HBoxContainer" instance=ExtResource("2_034ks")] -unique_name_in_owner = true -layout_mode = 2 -suffix = "px" - -[node name="VSeparator" type="VSeparator" parent="HBoxContainer"] -layout_mode = 2 - -[node name="HBoxContainer2" type="HBoxContainer" parent="HBoxContainer"] -layout_mode = 2 - -[node name="Label" type="Label" parent="HBoxContainer/HBoxContainer2"] -layout_mode = 2 -text = "y" - -[node name="YSpinBox" parent="HBoxContainer/HBoxContainer2" instance=ExtResource("2_034ks")] -unique_name_in_owner = true -layout_mode = 2 -suffix = "px" - -[connection signal="value_changed" from="HBoxContainer/HBoxContainer/XSpinBox" to="." method="_on_spin_box_value_changed"] -[connection signal="value_changed" from="HBoxContainer/HBoxContainer2/YSpinBox" to="." method="_on_spin_box_value_changed"] diff --git a/common/ui/graph/custom_graph_edit.gd b/common/ui/graph/custom_graph_edit.gd new file mode 100644 index 00000000..d6267037 --- /dev/null +++ b/common/ui/graph/custom_graph_edit.gd @@ -0,0 +1,67 @@ +## Represents the graph area which creates and connects MonologueGraphNodes. +class_name CustomGraphEdit extends GraphEdit + +const SCROLLBAR_OVERRIDE_KEYS: Array[String] = ["grabber", "scroll"] + +var connecting_mode: bool +var mouse_hovering: bool = false + + +func _ready() -> void: + _hide_default_scrollbars() + +func scroll_to_node(graph_node: GraphNode) -> void: + for child: Node in get_children(): + if child is GraphNode: + (child as GraphNode).selected = false + graph_node.selected = true + + var node_center: Vector2 = graph_node.position_offset + graph_node.size / 2.0 + scroll_offset = node_center * zoom - get_rect().size / 2.0 + + +func _hide_default_scrollbars() -> void: + for child: Node in get_children(true): + if child is GraphNode: + continue + for subchild: Node in child.get_children(true): + if not subchild is ScrollBar: + continue + for key: String in SCROLLBAR_OVERRIDE_KEYS: + subchild.add_theme_stylebox_override(key, StyleBoxEmpty.new()) + + +func _on_connection_to_empty(node: String, port: int, release: Vector2) -> void: + var center: Vector2 = (get_local_mouse_position() + scroll_offset) / zoom + var graph_release: Vector2 = (release + scroll_offset) / zoom + EventBus.enable_picker_mode.emit(node, port, release, graph_release, center) + + +func _on_gui_input(event: InputEvent) -> void: + if mouse_hovering: + var cursor_drag: bool = Input.is_action_pressed("Spacebar") + var cursor_hand_closed: bool = cursor_drag and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) + if Input.is_mouse_button_pressed(MOUSE_BUTTON_MIDDLE): + cursor_hand_closed = true + if cursor_hand_closed: + DisplayServer.cursor_set_custom_image(Cursor.CLOSED_HAND) + elif cursor_drag: + DisplayServer.cursor_set_custom_image(Cursor.HAND) + else: + DisplayServer.cursor_set_custom_image(Cursor.ARROW) + + if ( + event is InputEventMouseButton + and event.is_pressed() + and event.button_index == MOUSE_BUTTON_LEFT + ): + EventBus.show_languages.emit(false) + + +func _on_mouse_entered() -> void: + mouse_hovering = true + + +func _on_mouse_exited() -> void: + DisplayServer.cursor_set_custom_image(null) + mouse_hovering = false diff --git a/common/ui/graph/custom_graph_edit.gd.uid b/common/ui/graph/custom_graph_edit.gd.uid new file mode 100644 index 00000000..4401c67f --- /dev/null +++ b/common/ui/graph/custom_graph_edit.gd.uid @@ -0,0 +1 @@ +uid://dfy1ptnvfis7m diff --git a/common/ui/graph/graph_container.gd b/common/ui/graph/graph_container.gd new file mode 100644 index 00000000..d5cd2030 --- /dev/null +++ b/common/ui/graph/graph_container.gd @@ -0,0 +1,148 @@ +## Consists of a TabBar which allows the user to switch between GraphEdits. +## Saving and loading of GraphEdit data is handled by MonologueEditor. +class_name GraphContainer extends PanelContainer + +var prompt_scene: PackedScene = preload("uid://bkreq3xdr7gxw") + +@export var inspector_panel: InspectorPanel +@onready var graph: MonologueGraphEdit = %GraphEdit +@onready var snap_button: Button = %SnapButton +@onready var grid_button: Button = %GridButton + +var _selected_nodes: Dictionary = {} # storyline_id -> InspectableNode +var _is_applying_selection: bool = false +var _current_lang_prop: Property = null # tracked to disconnect on storyline switch + + +func _ready() -> void: + graph.node_view_selected.connect(_on_graph_edit_node_view_selected) + snap_button.pressed.connect(_on_snap_button_pressed) + grid_button.pressed.connect(_on_grid_button_pressed) + snap_button.button_pressed = ConfigManager.get_config("snap") + grid_button.button_pressed = ConfigManager.get_config("show_grid") + _on_snap_button_pressed() + _on_grid_button_pressed() + + EventBus.request_node_selection.connect(_on_request_node_selection) + EventBus.request_storyline_inspection.connect(_on_request_storyline_inspection) + EventBus.graph_snap.connect(_on_event_graph_snap) + EventBus.graph_show_grid.connect(_on_event_show_grid) + + ProjectManager.project_loaded.connect(_on_project_loaded) + + +func _on_project_loaded() -> void: + await get_tree().process_frame + + var storyline: StorylineDocument = ProjectManager.current_project.storylines[0] + load_storyline(storyline) + + +func load_storyline(storyline: StorylineDocument) -> void: + graph.storyline_id = storyline.id + graph.connection_manager = ConnectionManager.new(storyline) + graph.refresh() + + # Disconnect from the previous storyline's languages property + if is_instance_valid(_current_lang_prop) and _current_lang_prop.value_changed.is_connected(_on_languages_changed): + _current_lang_prop.value_changed.disconnect(_on_languages_changed) + + _current_lang_prop = storyline.get_property("languages") + if _current_lang_prop: + _current_lang_prop.value_changed.connect(_on_languages_changed) + EventBus.load_languages.emit(storyline.get_property_value("languages"), graph) + + # Restore the previously selected node (and inspector) for this storyline + var last_node: InspectableNode = _selected_nodes.get(storyline.id) + if last_node and is_instance_valid(last_node) and is_instance_valid(last_node.graph_view): + graph.set_selected(last_node.graph_view) + EventBus.request_object_inspection.emit(last_node) + + +func _on_languages_changed(_old: Variant, new_value: Variant) -> void: + EventBus.load_languages.emit(new_value, graph) + + +func _on_graph_edit_node_view_selected(node: InspectableNode) -> void: + request_node_selection(node) + + +func _on_request_node_selection( + object: InspectableObject, storyline_id: String, skip_history: bool = false +) -> void: + if object is not InspectableNode or storyline_id != graph.storyline_id: + return + request_node_selection(object, skip_history) + +func _on_request_storyline_inspection(storyline: StorylineDocument) -> void: + if graph.storyline_id == storyline.id: + return + + load_storyline(storyline) + + +func request_node_selection(node: InspectableNode, skip_history: bool = false) -> void: + if _is_applying_selection: + return + + var current_node: InspectableNode = _selected_nodes.get(graph.storyline_id) + var needs_selection_update := current_node != node + if not needs_selection_update: + if graph and node and is_instance_valid(node.graph_view): + needs_selection_update = not node.graph_view.selected + + if not needs_selection_update and inspector_panel.current_object == node: + return + + if skip_history: + _apply_selection(node, graph.storyline_id) + return + + var history: CommandManager = ProjectManager.current_project.command_manager + if not history: + _apply_selection(node, graph.storyline_id) + return + + var command := NodeSelectionCommand.new( + graph.storyline_id, current_node, node, Callable(self, "_apply_selection") + ) + + history.execute(command) + + +func _apply_selection(node: InspectableNode, storyline_id: String) -> void: + _is_applying_selection = true + + if node: + _selected_nodes[storyline_id] = node + else: + _selected_nodes.erase(storyline_id) + + if node and is_instance_valid(node.graph_view): + graph.set_selected(node.graph_view) + + EventBus.request_object_inspection.emit(node) + _is_applying_selection = false + + +func _on_add_node_btn_pressed() -> void: + EventBus.enable_picker_mode.emit("", -1, null, null, null, true) + + +func _on_snap_button_pressed() -> void: + graph.snapping_enabled = snap_button.button_pressed + ConfigManager.set_config("snap", snap_button.button_pressed) + + +func _on_grid_button_pressed() -> void: + graph.show_grid = grid_button.button_pressed + ConfigManager.set_config("show_grid", grid_button.button_pressed) + +func _on_event_graph_snap(enabled: bool) -> void: + graph.snapping_enabled = enabled + snap_button.button_pressed = enabled + + +func _on_event_show_grid(grid_visible: bool) -> void: + graph.show_grid = grid_visible + grid_button.button_pressed = grid_visible diff --git a/common/ui/graph/graph_container.gd.uid b/common/ui/graph/graph_container.gd.uid new file mode 100644 index 00000000..4317b079 --- /dev/null +++ b/common/ui/graph/graph_container.gd.uid @@ -0,0 +1 @@ +uid://ffk7n7akpe7t diff --git a/common/ui/graph/graph_container.tscn b/common/ui/graph/graph_container.tscn new file mode 100644 index 00000000..d45517ca --- /dev/null +++ b/common/ui/graph/graph_container.tscn @@ -0,0 +1,98 @@ +[gd_scene format=3 uid="uid://c8525nhvwt1y0"] + +[ext_resource type="Script" uid="uid://ffk7n7akpe7t" path="res://common/ui/graph/graph_container.gd" id="1_plmop"] +[ext_resource type="Script" uid="uid://c38kvfr67qro8" path="res://common/ui/menu_buttons/graph_view_button.gd" id="2_3el0f"] +[ext_resource type="Texture2D" uid="uid://bl4t6n5wyuelg" path="res://ui/assets/icons/magnet.svg" id="2_32s7k"] +[ext_resource type="Script" uid="uid://mqidcjji32ft" path="res://common/ui/menu_buttons/graph_edit_button.gd" id="2_juqgi"] +[ext_resource type="Texture2D" uid="uid://cvel6ifgyclof" path="res://ui/assets/icons/grid.svg" id="3_3el0f"] +[ext_resource type="Script" uid="uid://crx7ke21ixwnx" path="res://common/ui/menu_buttons/graph_add_button.gd" id="4_c51jt"] +[ext_resource type="Script" uid="uid://gwjipgxmwslb" path="res://common/ui/graph/search_bar.gd" id="5_plmop"] +[ext_resource type="PackedScene" uid="uid://qdgl8co6qy6" path="res://common/ui/graph/graph_edit.tscn" id="8_qfao8"] + +[node name="GraphContainer" type="PanelContainer" unique_id=379673752] +clip_contents = true +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_type_variation = &"EditorGraphPanel" +script = ExtResource("1_plmop") + +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=493267978] +layout_mode = 2 +theme_type_variation = &"VBoxContainerCompact" + +[node name="Header" type="PanelContainer" parent="VBoxContainer" unique_id=93875237] +layout_mode = 2 + +[node name="HeaderLeft" type="HBoxContainer" parent="VBoxContainer/Header" unique_id=1180880832] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 4 + +[node name="EditButton" type="Button" parent="VBoxContainer/Header/HeaderLeft" unique_id=312964398] +layout_mode = 2 +text = "Edit" +script = ExtResource("2_juqgi") + +[node name="ViewButton" type="Button" parent="VBoxContainer/Header/HeaderLeft" unique_id=548572994] +layout_mode = 2 +text = "View" +script = ExtResource("2_3el0f") + +[node name="SelectButton" type="Button" parent="VBoxContainer/Header/HeaderLeft" unique_id=1093210356] +layout_mode = 2 +disabled = true +text = "Select" + +[node name="AddButton" type="Button" parent="VBoxContainer/Header/HeaderLeft" unique_id=1597228810] +layout_mode = 2 +text = "Add" +script = ExtResource("4_c51jt") + +[node name="HeaderCenter" type="HBoxContainer" parent="VBoxContainer/Header" unique_id=1840524053] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 4 + +[node name="SearchBar" type="HBoxContainer" parent="VBoxContainer/Header/HeaderCenter" unique_id=770372650 node_paths=PackedStringArray("graph_edit")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("5_plmop") +graph_edit = NodePath("../../../GraphEdit") + +[node name="SearchBarInput" type="LineEdit" parent="VBoxContainer/Header/HeaderCenter/SearchBar" unique_id=134137898] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Search" + +[node name="HeaderRight" type="HBoxContainer" parent="VBoxContainer/Header" unique_id=1190022585] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 + +[node name="SnapButton" type="Button" parent="VBoxContainer/Header/HeaderRight" unique_id=235394610] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Toggle snapping" +theme_type_variation = &"ToggleIconButton" +toggle_mode = true +icon = ExtResource("2_32s7k") + +[node name="GridButton" type="Button" parent="VBoxContainer/Header/HeaderRight" unique_id=1985773366] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Toggle grid" +theme_type_variation = &"ToggleIconButton" +toggle_mode = true +button_pressed = true +icon = ExtResource("3_3el0f") + +[node name="GraphEdit" parent="VBoxContainer" unique_id=2027188591 instance=ExtResource("8_qfao8")] +unique_name_in_owner = true +layout_mode = 2 +scroll_offset = Vector2(0, -35) diff --git a/common/ui/graph/graph_edit.gd b/common/ui/graph/graph_edit.gd new file mode 100644 index 00000000..13120a18 --- /dev/null +++ b/common/ui/graph/graph_edit.gd @@ -0,0 +1,415 @@ +class_name MonologueGraphEdit extends CustomGraphEdit +signal node_view_selected(node: InspectableNode) + +var storyline_id: String +var connection_manager: ConnectionManager +var current_language_index: int = 0 +var _node_map: Dictionary = {} # Maps GraphNode -> InspectableNode +var _selected_nodes: Dictionary = {} +var _copied_nodes: Array = [] +var _pending_positions: Dictionary = {} # GraphNode -> Vector2 captured during drag +var _is_applying_position: bool = false + + +func _ready() -> void: + super._ready() + connection_request.connect(_on_connection_request) + connection_to_empty.connect(_on_connection_to_empty) + copy_nodes_request.connect(_on_copy_nodes_request) + cut_nodes_request.connect(_on_cut_nodes_request) + delete_nodes_request.connect(_on_delete_nodes_request) + disconnection_request.connect(_on_disconnection_request) + end_node_move.connect(_on_end_node_move) + node_deselected.connect(_on_node_deselected) + node_selected.connect(_on_node_selected) + paste_nodes_request.connect(_on_paste_nodes_request) + + EventBus.refresh_graph.connect(refresh) + + +func get_storyline() -> StorylineDocument: + return ProjectManager.current_project.get_storyline(storyline_id) + + +func refresh() -> void: + var storyline: StorylineDocument = get_storyline() + if not storyline: + return + + # Initialize connection manager if not already done + if not connection_manager: + connection_manager = ConnectionManager.new(storyline) + + # Clear node map and connections + _node_map.clear() + _pending_positions.clear() + clear_connections() + + for child: GraphElement in get_all_graph_nodes(): + child.queue_free() + + for node: InspectableNode in storyline.nodes: + add_graph_node_view(node) + + # Reconnect all tracked connections after rebuilding nodes + _reconnect_all_slots() + + +func refresh_node(node: InspectableNode) -> void: + if not node or not is_instance_valid(node.graph_view): + return + + clear_connections() + GraphNodeViewFactory.modulate_stylebox(node.graph_view, node) + GraphNodeViewFactory.apply_metadata(node.graph_view, node) + GraphNodeViewFactory.populate(node.graph_view, node) + _sync_position_from_property(node) + _reconnect_all_slots() + + +func add_graph_node_view(node: InspectableNode) -> GraphNode: + var graph_node: GraphNode = GraphNodeViewFactory.build(node) + + # Store bidirectional mapping + _node_map[graph_node] = node + node.graph_view = graph_node + + # Connect signals + if not graph_node.position_offset_changed.is_connected( + _on_graph_node_position_changed.bind(graph_node) + ): + graph_node.position_offset_changed.connect(_on_graph_node_position_changed.bind(graph_node)) + + # Add to scene and set initial position + add_child(graph_node) + if not node.property_changed.is_connected(_on_inspectable_node_property_changed): + node.property_changed.connect(_on_inspectable_node_property_changed.bind(node)) + + # Apply initial position from property + _sync_position_from_property(node) + return graph_node + + +## Called when InspectableNode property changes (undo/redo, programmatic) +func _on_inspectable_node_property_changed(property_name: String, node: InspectableNode) -> void: + if property_name == "position": + if not _is_applying_position: + _sync_position_from_property(node) + else: + refresh_node(node) + + +## Sync GraphNode position from InspectableNode property +func _sync_position_from_property(node: InspectableNode) -> void: + if not node or not is_instance_valid(node.graph_view): + return + + # Don't overwrite a position that hasn't been committed yet (active drag) + if _pending_positions.has(node.graph_view): + return + + var position_property: Property = node.get_property("position") + var raw: Variant = position_property.get_value() if position_property else null + var desired_position: Vector2 + if raw is Vector2: + desired_position = raw + elif raw is Array: + var raw_arr: Array = raw + if raw_arr.size() >= 2: + var x_val: float = raw_arr[0] if raw_arr[0] is float else 0.0 + var y_val: float = raw_arr[1] if raw_arr[1] is float else 0.0 + desired_position = Vector2(x_val, y_val) + else: + desired_position = Vector2.ZERO + + if node.graph_view.position_offset == desired_position: + return + + _is_applying_position = true + node.graph_view.position_offset = desired_position + _is_applying_position = false + + +## Called when GraphNode position changes (user drag) +func _on_graph_node_position_changed(graph_node: GraphNode) -> void: + if _is_applying_position: + return + + _pending_positions[graph_node] = graph_node.position_offset + + +func _on_node_selected(graph_node: Node) -> void: + _selected_nodes[graph_node] = true + var node: InspectableNode = _node_map.get(graph_node) + if node: + node_view_selected.emit(node) + + +func _on_node_deselected(graph_node: Node) -> void: + _selected_nodes[graph_node] = false + + +func _on_connection_request( + from_view_name: StringName, from_port: int, to_view_name: StringName, to_port: int +) -> void: + var from_node: InspectableNode = get_node_from_view_name(from_view_name) + var to_node: InspectableNode = get_node_from_view_name(to_view_name) + var from_graph_node: GraphNode = get_node(from_view_name as String) + var to_graph_node: GraphNode = get_node(to_view_name as String) + var from_port_type: int = from_graph_node.get_output_port_type(from_port) + var to_port_type: int = to_graph_node.get_input_port_type(to_port) + + if not FieldBucket.is_compatible(from_port_type, to_port_type): + return + + # Get property names at the port indices + var from_property_name: String = get_property_name_at_port(String(from_view_name), from_port, true) + var to_property_name: String = get_property_name_at_port(String(to_view_name), to_port, false) + + if from_property_name.is_empty() or to_property_name.is_empty(): + push_warning("Cannot create connection: property not found at port") + return + + var storyline: StorylineDocument = get_storyline() + var command: NodeConnectionCommand = NodeConnectionCommand.new( + self, + str(from_node.get_property_value("id")), + str(to_node.get_property_value("id")), + from_property_name, + to_property_name + ) + storyline.history.execute(command) + + +func _has_connection_at_slot(node_name: StringName, port_index: int, is_output: bool) -> bool: + var node_key: String = "from_node" if is_output else "to_node" + var port_key: String = "from_port" if is_output else "to_port" + var target_name: String = String(node_name) + + for connection: Dictionary in get_connection_list(): + if str(connection.get(node_key, "")) != target_name: + continue + if str(connection.get(port_key, -1)).to_int() == port_index: + return true + + return false + + +func get_all_graph_nodes() -> Array: + return get_children().filter(func(child: Node) -> bool: return child is GraphNode) + + +## Reconnect all slots based on tracked connections in connection_manager +func _reconnect_all_slots() -> void: + if not connection_manager: + return + + var storyline: StorylineDocument = get_storyline() + var all_connections: Array[Dictionary] = connection_manager.get_all_connections() + + for conn: Dictionary in all_connections: + var from_node_id: String = conn["from_node_id"] + var from_property: String = conn["from_property"] + var to_node_id: String = conn["to_node_id"] + var to_property: String = conn["to_property"] + + var from_node: InspectableNode = storyline.get_node(from_node_id) + var to_node: InspectableNode = storyline.get_node(to_node_id) + + if not from_node or not to_node: + continue + + var from_view_name: String = from_node.graph_view.name + var to_view_name: String = to_node.graph_view.name + + # Get port indices for the properties + var from_port: int = get_port_index_for_property(from_view_name, from_property, true) + var to_port: int = get_port_index_for_property(to_view_name, to_property, false) + + # Only connect if both ports are valid + if from_port >= 0 and to_port >= 0: + connect_node(from_view_name, from_port, to_view_name, to_port) + + +## Get visible properties in the same order as displayed in graph +func _get_visible_properties(node: InspectableNode) -> Array[Property]: + return node.get_visible_properties() + + +## Get the port index for a specific property by name. +## is_output=true counts only export ports (output), false counts only exposed ports (input). +## Supports composite names like "choices:item_id" for sub-ports. +func get_port_index_for_property(node_name: String, property_name: String, is_output: bool = false) -> int: + var node: InspectableNode = get_node_from_view_name(node_name) + if not node: + return -1 + + var rows: Array[GraphNodeRow] = GraphNodeViewFactory._build_rows(node) + var count: int = 0 + for row: GraphNodeRow in rows: + var has_port: bool = row._enable_right_port if is_output else row._enable_left_port + if not has_port: + continue + if row.get_connection_name() == property_name: + return count + count += 1 + + return -1 + + +func get_node_from_view_name(graph_view_name: String) -> InspectableNode: + var storyline: StorylineDocument = get_storyline() + + for node: InspectableNode in storyline.nodes: + if node.graph_view.name == graph_view_name: + return node + return null + + +## Get property name at a specific port index. +## Returns composite name (e.g. "choices:item_id") for sub-ports. +func get_property_name_at_port(node_name: String, port_index: int, is_output: bool) -> String: + var node: InspectableNode = get_node_from_view_name(node_name) + if not node: + return "" + + var rows: Array[GraphNodeRow] = GraphNodeViewFactory._build_rows(node) + var count: int = 0 + for row: GraphNodeRow in rows: + var has_port: bool = row._enable_right_port if is_output else row._enable_left_port + if not has_port: + continue + if count == port_index: + return row.get_connection_name() + count += 1 + return "" + + +func _on_end_node_move() -> void: + if _pending_positions.is_empty(): + return + + _is_applying_position = true + + var storyline: StorylineDocument = get_storyline() + var history: CommandManager = storyline.history if storyline else null + if not history: + _is_applying_position = false + _pending_positions.clear() + return + + for graph_node: Variant in _pending_positions.keys(): + if not is_instance_valid(graph_node): + continue + + var node: InspectableNode = _node_map.get(graph_node) + if not node: + continue + + var target_position: Array = [_pending_positions[graph_node].x, _pending_positions[graph_node].y] + var position_property: Property = node.get_property("position") + if not position_property: + continue + if not position_property.get_value() is Array: + position_property.set_value([0.0, 0.0]) + var positon_value: Array = position_property.get_value() + if positon_value == target_position: + continue + + var command: PropertyChangeCommand = PropertyChangeCommand.new( + node, "position", positon_value, target_position + ) + history.execute(command) + + _is_applying_position = false + _pending_positions.clear() + + +func _on_disconnection_request( + from_view_name: StringName, from_port: int, to_view_name: StringName, to_port: int +) -> void: + if not connection_manager: + return + + connection_manager.unregister_connection(from_view_name, from_port, to_view_name, to_port) + var from_node: InspectableNode = get_node_from_view_name(from_view_name) + var to_node: InspectableNode = get_node_from_view_name(to_view_name) + var from_graph_node: GraphNode = get_node(from_view_name as String) + var to_graph_node: GraphNode = get_node(to_view_name as String) + var from_port_type: int = from_graph_node.get_output_port_type(from_port) + var to_port_type: int = to_graph_node.get_input_port_type(to_port) + + if from_port_type != to_port_type: + return + + # Get property names at the port indices + var from_property_name: String = get_property_name_at_port(String(from_view_name), from_port, true) + var to_property_name: String = get_property_name_at_port(String(to_view_name), to_port, false) + + if from_property_name.is_empty() or to_property_name.is_empty(): + push_warning("Cannot create connection: property not found at port") + return + + var storyline: StorylineDocument = get_storyline() + var command: NodeConnectionCommand = NodeConnectionCommand.new( + self, + str(from_node.get_property_value("id")), + str(to_node.get_property_value("id")), + from_property_name, + to_property_name + ) + storyline.history.execute(command) + + +func _on_copy_nodes_request() -> void: + if _selected_nodes.size() <= 0: + return + _copied_nodes.clear() + + for node: Node in get_children(): + if node is GraphNode and _selected_nodes.get(node, false): + var source_node: InspectableNode = _node_map.get(node) + if source_node: + var duplicated: InspectableNode = source_node.duplicate(true) + _copied_nodes.append(duplicated) + + +func _on_paste_nodes_request() -> void: + # TODO: Move nodes based on the cursor position + var storyline: StorylineDocument = get_storyline() + var command: AddNodesCommand = AddNodesCommand.new(storyline_id, _copied_nodes) + storyline.history.execute(command) + + +func _on_cut_nodes_request() -> void: + if _selected_nodes.size() <= 0: + return + _copied_nodes.clear() + + var nodes_to_delete: Array[InspectableNode] = [] + + for node: Node in get_children(): + if node is GraphNode and _selected_nodes.get(node, false): + var source_node: InspectableNode = _node_map.get(node) + if source_node: + var duplicated: InspectableNode = source_node.duplicate(true) + _copied_nodes.append(duplicated) + nodes_to_delete.append(source_node) + + var storyline: StorylineDocument = get_storyline() + var command: DeleteNodesCommand = DeleteNodesCommand.new(storyline_id, nodes_to_delete) + storyline.history.execute(command) + + +func _on_delete_nodes_request(graph_nodes: Array[StringName]) -> void: + var storyline: StorylineDocument = get_storyline() + var nodes: Array[InspectableNode] = [] + for node_name: StringName in graph_nodes: + var graph_node: GraphNode = get_node("%s" % node_name) + var node: InspectableNode = _node_map.get(graph_node) + nodes.append(node) + + var command: DeleteNodesCommand = DeleteNodesCommand.new(storyline_id, nodes) + storyline.history.execute(command) + + refresh() diff --git a/common/layouts/graph_edit/monologue_graph_edit.gd.uid b/common/ui/graph/graph_edit.gd.uid similarity index 100% rename from common/layouts/graph_edit/monologue_graph_edit.gd.uid rename to common/ui/graph/graph_edit.gd.uid diff --git a/common/ui/graph/graph_edit.tscn b/common/ui/graph/graph_edit.tscn new file mode 100644 index 00000000..46d89a29 --- /dev/null +++ b/common/ui/graph/graph_edit.tscn @@ -0,0 +1,29 @@ +[gd_scene format=3 uid="uid://qdgl8co6qy6"] + +[ext_resource type="Script" uid="uid://bvrbvq6oihg7d" path="res://common/ui/graph/graph_edit.gd" id="1_odres"] + +[node name="GraphEdit" type="GraphEdit" unique_id=1378799081] +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 +grid_pattern = 1 +snapping_enabled = false +snapping_distance = 30 +right_disconnects = true +zoom_min = 0.1 +zoom_max = 2.0 +minimap_enabled = false +show_menu = false +show_zoom_buttons = false +show_grid_buttons = false +show_minimap_button = false +show_arrange_button = false +script = ExtResource("1_odres") + +[connection signal="gui_input" from="." to="." method="_on_gui_input"] +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/common/ui/graph/graph_node_view_factory.gd b/common/ui/graph/graph_node_view_factory.gd new file mode 100644 index 00000000..806be435 --- /dev/null +++ b/common/ui/graph/graph_node_view_factory.gd @@ -0,0 +1,219 @@ +class_name GraphNodeViewFactory extends RefCounted + +const SLOT_IN_TEXTURE: Texture2D = preload("res://ui/assets/icons/slot_in.svg") +const SLOT_OUT_TEXTURE: Texture2D = preload("res://ui/assets/icons/slot_out.svg") + + +static func build(node: InspectableNode) -> GraphNode: + var graph_node: GraphNode = GraphNode.new() + graph_node.custom_minimum_size.x = 192 + graph_node.draggable = true + graph_node.selectable = true + graph_node.resizable = false + modulate_stylebox(graph_node, node) + apply_metadata(graph_node, node) + populate(graph_node, node) + return graph_node + + +static func modulate_stylebox(graph_node: GraphNode, node: InspectableNode) -> void: + var color_prop: Property = node.get_property("color") + if not color_prop: + return + + var node_color: Color = Color(str(color_prop.get_value())) + var sb_names: Array = ["panel", "panel_selected"] + + for sb_name: String in sb_names: + graph_node.remove_theme_stylebox_override(sb_name) + + if node_color == Color.BLACK: + return + + for sb_name: String in sb_names: + var base_sb: StyleBox = graph_node.get_theme_stylebox(sb_name) + + if base_sb is StyleBoxFlat: + var flat_sb: StyleBoxFlat = base_sb as StyleBoxFlat + var new_sb: StyleBoxFlat = flat_sb.duplicate() + var new_bg_color: Color = Color(node_color, 0.35) + var new_border_color: Color = Color(node_color, 0.35) + new_bg_color = flat_sb.bg_color.blend(new_bg_color) + new_border_color = flat_sb.border_color.blend(new_border_color) + new_sb.bg_color = new_bg_color + new_sb.border_color = new_border_color + graph_node.add_theme_stylebox_override(sb_name, new_sb) + + +static func populate(graph_node: GraphNode, node: InspectableNode) -> void: + if not is_instance_valid(graph_node): + return + + apply_metadata(graph_node, node) + + for child: Control in graph_node.get_children(): + graph_node.remove_child(child) + child.queue_free() + + if graph_node.has_method("clear_all_slots"): + graph_node.clear_all_slots() + + var title_bar: HBoxContainer = graph_node.get_titlebar_hbox() + if title_bar: + title_bar.hide() + + var rows: Array[GraphNodeRow] = _build_rows(node) + for idx: int in rows.size(): + var row: GraphNodeRow = rows[idx] + var container: HBoxContainer = HBoxContainer.new() + container.mouse_filter = Control.MOUSE_FILTER_PASS + container.theme_type_variation = "GraphNodeViewRownHBox" + + var key_label: Label = Label.new() + key_label.mouse_filter = Control.MOUSE_FILTER_PASS + key_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + key_label.text = row.get_key() + + var value_label: Label = Label.new() + value_label.mouse_filter = Control.MOUSE_FILTER_PASS + if row.get_type(): + value_label.text = "[%s]" % row.get_type() + + var field_metadata: Dictionary = FieldBucket.get_metadata(row.get_type()) + var slot_color: Color = field_metadata.get("color", Color.WHITE) + + value_label.label_settings = LabelSettings.new() + value_label.label_settings.font_color = slot_color + + if idx == 0: + key_label.theme_type_variation = "GraphNodeViewTitleLabel" + value_label.theme_type_variation = "GraphNodeViewValueLabel" + + container.add_child(key_label) + container.add_child(value_label) + graph_node.add_child(container) + + if row.port_size == "large": + container.custom_minimum_size.y = 32 + + var type_id: int = FieldBucket.get_type_id(row.get_type()) + graph_node.set_slot( + idx, + row._enable_left_port, + type_id, + slot_color, + row._enable_right_port, + type_id, + slot_color, + SLOT_IN_TEXTURE, + SLOT_OUT_TEXTURE, + true + ) + + graph_node.set_slot_custom_icon_left(idx, SLOT_IN_TEXTURE) + graph_node.set_slot_custom_icon_right(idx, SLOT_OUT_TEXTURE) + + graph_node.set_size(Vector2.ZERO) + + +static func apply_metadata(graph_node: GraphNode, node: InspectableNode) -> void: + if not is_instance_valid(graph_node): + return + graph_node.title = Util.to_readable_name(node.get_type()) + var id_prop: Property = node.get_property("id") + graph_node.name = str(id_prop.get_value()) if id_prop else _derive_node_name(node) + + +static func _build_rows(node: InspectableNode) -> Array[GraphNodeRow]: + var rows: Array[GraphNodeRow] = [] + for prop: Property in node.get_properties(): + if not prop.is_visible_in_graph(): + continue + + var row: GraphNodeRow = _build_property_row(prop) + if prop.get_settings_value("is_main_property"): + rows.push_front(row) + continue + rows.append(row) + + if prop.type == "collection": + rows.append_array(_build_list_sub_rows(node, prop)) + + return rows + + +static func _build_property_row(prop: Property) -> GraphNodeRow: + var enable_left: bool = prop.get_settings_value("exposed", false) == true + var enable_right: bool = prop.get_settings_value("export", false) == true + var label: String = ( + prop.get_display_name() if prop.get_settings_value("is_main_property") else prop.name + ) + # For list properties, resolve port type from the collection's field type + var row_type: String = prop.type + if prop.type == "collection": + var collection_name: String = prop.get_settings_value(PropertySettings.KEY_COLLECTION, "") + if not collection_name.is_empty(): + var field_descriptor: FieldDescriptor = FieldBucket.get_field_descriptor(collection_name) + if field_descriptor: + row_type = collection_name + var row: GraphNodeRow = GraphNodeRow.new(label, row_type, enable_left, enable_right) + row._property_name = prop.name + row.port_size = prop.get_settings_value(PropertySettings.KEY_PORT_SIZE, "normal") + return row + + +static func _build_list_sub_rows(node: InspectableNode, prop: Property) -> Array[GraphNodeRow]: + var sub_rows: Array[GraphNodeRow] = [] + var coll_name: String = prop.get_settings_value(PropertySettings.KEY_COLLECTION, "") + if coll_name.is_empty(): + return sub_rows + + var probe: CollectionItem = CollectionBucket.create_item(coll_name, CommandManager.new()) + if not probe or not probe.has_main_property(): + return sub_rows + + # Internal items from the property value + var list_value: Variant = prop.get_value() + if list_value is Array: + for item_data: Dictionary in list_value: + if not item_data is Dictionary: + continue + var item_id: String = _extract_dict_string(item_data, "id") + if item_id.is_empty(): + continue + var item_name: String = _extract_dict_string(item_data, "name") + var sub_label: String = " %s" % [item_name if not item_name.is_empty() else item_id] + var sub_row: GraphNodeRow = GraphNodeRow.new(sub_label, "context", false, true) + sub_row.sub_property_id = "%s:%s" % [prop.name, item_id] + sub_rows.append(sub_row) + + # External items (e.g. connected OptionNodes) + var externals: Array[Dictionary] = node.get_external_list_items(prop.name) + for ext_data: Dictionary in externals: + var ext_name: String = ext_data.get("name", "") + var ext_src_id: String = ext_data.get("source_node_id", "") + if ext_src_id.is_empty(): + continue + var sub_label: String = " %s" % [ext_name if not ext_name.is_empty() else ext_src_id] + var sub_row: GraphNodeRow = GraphNodeRow.new(sub_label, "context", false, true) + sub_row.sub_property_id = "%s:ext_%s" % [prop.name, ext_src_id] + sub_rows.append(sub_row) + + return sub_rows + + +## Extracts a string from a dict value. +static func _extract_dict_string(data: Dictionary, key: String) -> String: + var raw_dict: Dictionary = data.get(key, {}) + return str(raw_dict.get("value", "")) + + +static func _derive_node_name(node: InspectableNode) -> String: + # Fallback to id (no type prefix) if needed + var id_value: String = "" + var id_property: Property = node.get_property("id") + if id_property: + id_value = str(id_property.get_value()) + if id_value.is_empty(): + id_value = IDGen.generate(5) + return id_value diff --git a/common/ui/graph/graph_node_view_factory.gd.uid b/common/ui/graph/graph_node_view_factory.gd.uid new file mode 100644 index 00000000..e9449977 --- /dev/null +++ b/common/ui/graph/graph_node_view_factory.gd.uid @@ -0,0 +1 @@ +uid://d3r62bie5le05 diff --git a/common/ui/graph/search_bar.gd b/common/ui/graph/search_bar.gd new file mode 100644 index 00000000..e249d78b --- /dev/null +++ b/common/ui/graph/search_bar.gd @@ -0,0 +1,90 @@ +extends HBoxContainer + +@onready var search_input: LineEdit = %SearchBarInput + +@export var graph_edit: MonologueGraphEdit + +var _results: Array[InspectableNode] = [] +var _current_index: int = -1 + + +func _ready() -> void: + assert(graph_edit != null) + + search_input.text_changed.connect(_on_text_changed) + search_input.gui_input.connect(_on_search_gui_input) + _refresh_ui() + + +func _on_search_gui_input(event: InputEvent) -> void: + if not event is InputEventKey or not event.is_pressed(): + return + match event.keycode: + KEY_ESCAPE: + accept_event() + KEY_ENTER, KEY_KP_ENTER: + _navigate(-1 if event.shift_pressed else 1) + accept_event() + KEY_UP: + _navigate(-1) + accept_event() + KEY_DOWN: + _navigate(1) + accept_event() + + +func _on_text_changed(text: String) -> void: + _run_search(text) + + +func _run_search(query: String) -> void: + _results.clear() + _current_index = -1 + + if not query.is_empty() and graph_edit: + var q: String = query.to_lower() + for graph_node: GraphNode in graph_edit.get_all_graph_nodes(): + var node: InspectableNode = graph_edit._node_map.get(graph_node) + if node and _node_matches(node, q): + _results.append(node) + + if not _results.is_empty(): + _current_index = 0 + _focus_current() + + _refresh_ui() + + +func _node_matches(node: InspectableNode, query_lower: String) -> bool: + # TODO: Improve query + + for property: Property in node.get_properties(): + var value: Variant = property.get_value() + if value is String and value.to_lower().contains(query_lower): + return true + return false + + +func _navigate(direction: int) -> void: + if _results.is_empty(): + return + _current_index = posmod(_current_index + direction, _results.size()) + _focus_current() + _refresh_ui() + + +func _focus_current() -> void: + if _current_index < 0 or _current_index >= _results.size(): + return + var node: InspectableNode = _results[_current_index] + if not is_instance_valid(node) or not is_instance_valid(node.graph_view): + return + graph_edit.scroll_to_node(node.graph_view) + + +func _refresh_ui() -> void: + var has_results: bool = not _results.is_empty() + + if not has_results: + pass + # TODO: Maybe do something diff --git a/common/ui/graph/search_bar.gd.uid b/common/ui/graph/search_bar.gd.uid new file mode 100644 index 00000000..611fca34 --- /dev/null +++ b/common/ui/graph/search_bar.gd.uid @@ -0,0 +1 @@ +uid://gwjipgxmwslb diff --git a/common/ui/h_auto_split_container.gd b/common/ui/h_auto_split_container.gd new file mode 100644 index 00000000..02097476 --- /dev/null +++ b/common/ui/h_auto_split_container.gd @@ -0,0 +1,44 @@ +class_name HAutoSplitContainer extends HSplitContainer + +#var split_percents: Array[float] = []: + #set(value): + #split_percents = value + #if not _dragging: + #_percents_to_offsets() +# +#var _dragging := false +# +#func _ready() -> void: + #resized.connect(_on_resized) + #dragged.connect(_on_dragged) + #_offsets_to_percents.call_deferred() +# +#func _percents_to_offsets() -> void: + #var total := _total_width() + #if total <= 0.0: + #return + #var offsets := split_offsets + #for i in mini(split_percents.size(), offsets.size()): + #offsets[i] = int(total * split_percents[i] / 100.0) + #split_offsets = offsets +# +#func _offsets_to_percents() -> void: + #var total := _total_width() + #if total <= 0.0: + #return + #var new_percents: Array[float] = [] + #for offset in split_offsets: + #new_percents.append(offset / total * 100.0) + #split_percents = new_percents +# +#func _on_resized() -> void: + #if not _dragging: + #_percents_to_offsets() +# +#func _on_dragged(_offset: int) -> void: + #_dragging = true + #_offsets_to_percents() + #_dragging = false +# +#func _total_width() -> float: + #return size.x diff --git a/common/ui/h_auto_split_container.gd.uid b/common/ui/h_auto_split_container.gd.uid new file mode 100644 index 00000000..1b17e4ec --- /dev/null +++ b/common/ui/h_auto_split_container.gd.uid @@ -0,0 +1 @@ +uid://boag5gmomlm3 diff --git a/common/ui/horizontal_placement_indicator.tscn b/common/ui/horizontal_placement_indicator.tscn deleted file mode 100644 index 4522efd0..00000000 --- a/common/ui/horizontal_placement_indicator.tscn +++ /dev/null @@ -1,69 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://t876wx6hfyy5"] - -[ext_resource type="Texture2D" uid="uid://mjb34j4suabp" path="res://common/layouts/character_edit/assets/triangle_pointer.svg" id="1_lxcb6"] - -[node name="HPlacementIndicator" type="Control"] -z_index = 2 -layout_mode = 3 -anchor_right = 0.019 -offset_right = 0.400002 -size_flags_vertical = 3 - -[node name="ColorRect2" type="ColorRect" parent="."] -custom_minimum_size = Vector2(1, 0) -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 1.0 -offset_top = -2.0 -offset_right = -0.999996 -offset_bottom = 2.0 -grow_horizontal = 2 -grow_vertical = 0 -color = Color(0, 0, 0, 1) - -[node name="TextureRect" type="TextureRect" parent="."] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_top = -8.0 -offset_right = 15.0 -offset_bottom = -1.0 -grow_horizontal = 0 -rotation = 1.5708 -texture = ExtResource("1_lxcb6") -expand_mode = 2 -stretch_mode = 5 - -[node name="TextureRect2" type="TextureRect" parent="."] -layout_mode = 1 -anchors_preset = -1 -anchor_top = 1.0 -anchor_bottom = 1.019 -offset_left = 7.0 -offset_top = -8.0 -offset_right = 22.0 -offset_bottom = -1.0 -rotation = 1.5708 -texture = ExtResource("1_lxcb6") -expand_mode = 2 -stretch_mode = 5 -flip_v = true - -[node name="ColorRect" type="ColorRect" parent="."] -custom_minimum_size = Vector2(1, 0) -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 1.0 -offset_top = -1.0 -offset_right = -0.999996 -offset_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 0 -color = Color(0.835294, 0.317647, 0.376471, 1) diff --git a/common/ui/inspector/expose_button.tscn b/common/ui/inspector/expose_button.tscn new file mode 100644 index 00000000..e3eba3f0 --- /dev/null +++ b/common/ui/inspector/expose_button.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=4 format=3 uid="uid://2ehh7rdn6yg6"] + +[ext_resource type="Texture2D" uid="uid://x3csrllomotc" path="res://ui/assets/icons/unexposed.svg" id="1_g3anq"] +[ext_resource type="Texture2D" uid="uid://d4fp2shj4o5sk" path="res://ui/assets/icons/exposed.svg" id="2_uxtll"] +[ext_resource type="Texture2D" uid="uid://qs0nswphmi80" path="res://ui/assets/icons/unexposable.svg" id="3_6c77f"] + +[node name="ExposeButton" type="TextureButton"] +custom_minimum_size = Vector2(16, 16) +offset_right = 16.0 +offset_bottom = 16.0 +size_flags_vertical = 3 +toggle_mode = true +texture_normal = ExtResource("1_g3anq") +texture_pressed = ExtResource("2_uxtll") +texture_disabled = ExtResource("3_6c77f") +ignore_texture_size = true +stretch_mode = 5 diff --git a/common/ui/inspector/inspector_category_container.gd b/common/ui/inspector/inspector_category_container.gd new file mode 100644 index 00000000..6360514d --- /dev/null +++ b/common/ui/inspector/inspector_category_container.gd @@ -0,0 +1,26 @@ +class_name InspectorCategoryContainer extends FoldableContainer + +@onready var _vbox: VBoxContainer = %VBox + + +func _ready() -> void: + title_alignment = HORIZONTAL_ALIGNMENT_LEFT + + +func add_control( + node: Node, + force_readable_name: bool = false, + internal: InternalMode = InternalMode.INTERNAL_MODE_DISABLED +) -> void: + if not is_node_ready(): + return + + if not is_instance_valid(node): + push_warning("Attempted to add a null control to inspector category container.") + return + + _vbox.add_child(node, force_readable_name, internal) + + +func is_empty() -> bool: + return _vbox.get_child_count() <= 0 diff --git a/common/ui/inspector/inspector_category_container.gd.uid b/common/ui/inspector/inspector_category_container.gd.uid new file mode 100644 index 00000000..df6a747c --- /dev/null +++ b/common/ui/inspector/inspector_category_container.gd.uid @@ -0,0 +1 @@ +uid://djpi62hkqu8r6 diff --git a/common/ui/inspector/inspector_category_container.tscn b/common/ui/inspector/inspector_category_container.tscn new file mode 100644 index 00000000..046cd7bc --- /dev/null +++ b/common/ui/inspector/inspector_category_container.tscn @@ -0,0 +1,13 @@ +[gd_scene format=3 uid="uid://bvf68w7xrfrom"] + +[ext_resource type="Script" uid="uid://djpi62hkqu8r6" path="res://common/ui/inspector/inspector_category_container.gd" id="1_d6x5l"] + +[node name="FoldableContainer" type="FoldableContainer" unique_id=2010418272] +offset_right = 24.0 +offset_bottom = 32.0 +title_alignment = 1 +script = ExtResource("1_d6x5l") + +[node name="VBox" type="VBoxContainer" parent="." unique_id=1203723153] +unique_name_in_owner = true +layout_mode = 2 diff --git a/common/ui/inspector/inspector_panel.gd b/common/ui/inspector/inspector_panel.gd new file mode 100644 index 00000000..5c514a75 --- /dev/null +++ b/common/ui/inspector/inspector_panel.gd @@ -0,0 +1,398 @@ +class_name InspectorPanel extends PanelContainer + +var _fields: Array[Field] = [] +var _pending_expand_category: String = "" + +@onready var back_button: Button = %BackButton +@onready var header_container: HBoxContainer = %FieldHeader +@onready var field_container: VBoxContainer = %Fields +@onready var run_button: Button = %RunButton +@onready var inspector_category_container: PackedScene = preload("uid://bvf68w7xrfrom") +@onready var expose_button: PackedScene = preload("uid://2ehh7rdn6yg6") + +var current_object: InspectableObject +var history: Array[InspectableObject] = [] + + +func _ready() -> void: + EventBus.request_object_inspection.connect(inspect) + EventBus.inspector_property_changed.connect(_on_external_property_changed) + EventBus.show_inspector.connect(_on_event_show_inspector) + + ProjectManager.project_loaded.connect(_on_project_loaded) + back_button.pressed.connect(_on_back_button_pressed) + visible = ConfigManager.get_config("show_inspector") + + +func _on_project_loaded() -> void: + var command_manager: CommandManager = ProjectManager.current_project.command_manager + command_manager.undone.connect(_on_history_undo_redo) + command_manager.redone.connect(_on_history_undo_redo) + + +func _on_event_show_inspector(_visible: bool) -> void: + inspect(current_object) + + +## Called after every undo or redo. Re-resolves the inspection stack from the +## root so stale CollectionItem references are replaced with fresh objects, then rebuilds. +func _on_history_undo_redo() -> void: + rebuild() + + +func inspect(object: InspectableObject, from_history: bool = false) -> void: + var old_root: InspectableObject = current_object + if object and old_root and old_root != object: + if not from_history: + history.append(old_root) + + var old_node: InspectableNode = old_root as InspectableNode + if old_node and is_instance_valid(old_node.graph_view): + old_node.graph_view.selected = false + + back_button.disabled = history.is_empty() + current_object = object + if not object or object is not InspectableObject: + hide() + Log.warn("Inspector hidden due to invalid object") + return + + if not ConfigManager.get_config("show_inspector"): + hide() + return + + show() + Log.info("Inspect object", object.get_property_value("id") if object else "") + + rebuild() + + +func rebuild() -> void: + var inspected: InspectableObject = current_object + run_button.visible = current_object is InspectableNode + _fields.clear() + await get_tree().process_frame # TODO: Bad practice + + for field: Control in field_container.get_children(): + field.queue_free() + + for child: Control in header_container.get_children(): + child.queue_free() + + var separator: HSeparator = HSeparator.new() + separator.theme_type_variation = "UltraWideHSeparator" + field_container.add_child(separator) + + if not inspected: + var label: Label = Label.new() + label.text = "Nothing to show here." + label.size_flags_vertical = Control.SIZE_EXPAND_FILL + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + field_container.add_child(label) + else: + var properties: Array[Property] = inspected.get_properties() + var categories: Dictionary = _group_by_category(properties) + + for category_name: String in categories.keys(): + var props: Array = categories[category_name] + + if category_name.begins_with("Special"): + var special_category: String = category_name.trim_prefix("Special:") + _handle_special_category_section(special_category, props) + continue + + _create_category_section(category_name, props) + + _pending_expand_category = "" + + +func _find_labels(node: Node, labels: Array) -> void: + if node is Label: + labels.append(node) + for child: Node in node.get_children(): + _find_labels(child, labels) + + +func _restore_focus_to_property(property_name: String) -> void: + # Walk into each category container and find the p_container tagged with property_name + for category: Node in field_container.get_children(): + for p_container: Node in category.get_children(): + if not p_container.has_meta("property_name"): + continue + if p_container.get_meta("property_name") != property_name: + continue + var fields: Array = [] + _find_focusable_fields(p_container, fields) + if fields.size() > 0: + var first_field: Control = fields[0] + first_field.grab_focus() + return + + +func _find_focusable_fields(node: Node, fields: Array) -> void: + if node is LineEdit or node is TextEdit or node is OptionButton \ + or node is CheckBox or node is SpinBox or node is ColorPickerButton \ + or (node is Button and not node is TextureButton): + fields.append(node) + for child: Node in node.get_children(): + _find_focusable_fields(child, fields) + + +func _group_by_category(properties: Array[Property]) -> Dictionary: + var groups: Dictionary[String, Array] = {} + for prop: Property in properties: + # Skip properties not visible in inspector + if not prop.get_settings_value("visible_in_inspector", true): + continue + + var category: String = "General" + if prop.has_settings("category"): + category = prop.get_settings_value("category") + + if not groups.has(category): + groups[category] = [] + + groups[category].append(prop) + return groups + + +func _create_category_section(category_name: String, properties: Array) -> InspectorCategoryContainer: + # If the section only contains list properties. + var is_ghost_section: bool = properties.filter( + func(p: Property) -> bool: + return _is_list(p) + ).size() == properties.size() and properties.size() != 0 + + var separator: HSeparator = HSeparator.new() + separator.theme_type_variation = "UltraWideHSeparator" + var container: InspectorCategoryContainer = inspector_category_container.instantiate() + container.title = category_name + + if not is_ghost_section: + field_container.add_child(container) + field_container.add_child(separator) + + for property: Property in properties: + var property_editor: Control = _create_property_editor(property) + if property_editor: + container.add_control(property_editor) + + return container + + +func _handle_special_category_section(category_name: String, properties: Array) -> void: + var container: Control + + match category_name: + "Header": + container = header_container + + for property: Property in properties: + var property_editor: Control = _create_property_editor(property, true, true) + if not property_editor: + continue + + container.add_child(property_editor) + container.move_child(property_editor, 0) + + +func _create_property_editor(property: Property, flat: bool = false, hide_left: bool = false) -> Control: + var is_list: bool = _is_list(property) + var is_editable: bool = property.get_settings_value(PropertySettings.KEY_EDITABLE, true) + var is_read_only: bool = property.get_settings_value(PropertySettings.KEY_READ_ONLY, false) + var has_port: bool = ( + property.get_settings_value(PropertySettings.KEY_EXPOSED, false) + or property.get_settings_value(PropertySettings.KEY_EXPORT, false) + ) + if not is_editable and not is_read_only and not has_port: + return null + + var p_field: Control + if property.is_input_connected() and not is_list: + var inspect_button: Button = Button.new() + inspect_button.text = "Go to connected node" + inspect_button.tooltip_text = "Inspect connected node" + inspect_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + inspect_button.pressed.connect(_on_inspect_connected_node.bind(property)) + p_field = inspect_button + else: + p_field = FieldBucket.safe_create_field(property.type) + + var is_vertical: bool = false + if p_field is Field: + is_vertical = (p_field as Field).prefers_vertical_layout(property.get_settings()) + + var p_container: PanelContainer = PanelContainer.new() + p_container.set_meta("property_name", property.name) + p_container.theme_type_variation = "FieldContainer" + if property.get_settings_value("expand", true): + p_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + + if flat: + p_container.add_theme_stylebox_override("panel", StyleBoxEmpty.new()) + + var p_hbox: Control + if not hide_left: + if is_vertical: + var p_vbox: VBoxContainer = VBoxContainer.new() + p_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + p_container.add_child(p_vbox) + + var p_label_row: HBoxContainer = HBoxContainer.new() + p_vbox.add_child(p_label_row) + + var p_expose_button: TextureButton = expose_button.instantiate() + p_expose_button.disabled = not current_object is InspectableNode \ + or not property.get_settings_value(PropertySettings.KEY_EXPOSABLE, false) + p_expose_button.button_pressed = property.get_settings_value(PropertySettings.KEY_EXPOSED, false) + p_expose_button.toggled.connect( + _on_property_expose_state_changed.bind(current_object, property.name) + ) + p_label_row.add_child(p_expose_button) + + var p_label: Label = Label.new() + p_label.clip_text = true + p_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + p_label.text = property.name + p_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS_FORCE + p_label_row.add_child(p_label) + + p_hbox = p_vbox + else: + var p_ahbox: AdvancedHBoxContainer = AdvancedHBoxContainer.new() + p_ahbox.ratio = [2, 3] + p_ahbox.force_ratio = true + p_ahbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL + p_container.add_child(p_ahbox) + + var p_left_main_container: HBoxContainer = HBoxContainer.new() + p_left_main_container.clip_contents = true + p_ahbox.add_child(p_left_main_container) + + var p_left_container: HBoxContainer = HBoxContainer.new() + p_left_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + p_left_container.size_flags_vertical = Control.SIZE_SHRINK_BEGIN + p_left_main_container.add_child(p_left_container) + + var p_expose_button: TextureButton = expose_button.instantiate() + p_expose_button.disabled = not current_object is InspectableNode \ + or not property.get_settings_value(PropertySettings.KEY_EXPOSABLE, false) + p_expose_button.button_pressed = property.get_settings_value(PropertySettings.KEY_EXPOSED, false) + p_expose_button.toggled.connect( + _on_property_expose_state_changed.bind(current_object, property.name) + ) + p_left_container.add_child(p_expose_button) + + var p_label: Label = Label.new() + p_label.clip_text = true + p_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + p_label.text = property.name + p_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS_FORCE + p_left_container.add_child(p_label) + + if is_list: + p_left_main_container.hide() + + p_hbox = p_ahbox + else: + p_hbox = VBoxContainer.new() + p_container.add_child(p_hbox) + + if p_field is not Field: + p_hbox.add_child(p_field) + return p_container + _fields.append(p_field) + + if is_list: + var list_section: InspectorCategoryContainer = _create_category_section(property.get_display_name(), []) + list_section.add_control.call_deferred(p_container) + + property.bind_field.call_deferred(p_field, current_object) + p_hbox.add_child(p_field) + return p_container if not is_list else null + + +func _cache_category_states(category_states: Dictionary) -> void: + for child: Control in field_container.get_children(): + var fc: FoldableContainer = child as FoldableContainer + if fc: + category_states[fc.title] = fc.folded + + +func _apply_category_state(container: FoldableContainer, category_name: String, category_states: Dictionary) -> void: + var stored_state: Variant = category_states.get(category_name) + if stored_state is bool: + container.folded = stored_state + else: + container.folded = false + + if _pending_expand_category == category_name: + container.folded = false + + category_states[category_name] = container.folded + + +func _on_property_expose_state_changed( + toggled_on: bool, node: InspectableNode, property_name: String +) -> void: + node.set_property_settings_value(property_name, "exposed", toggled_on) + + +func _on_inspect_connected_node(property: Property) -> void: + if not current_object or not current_object is InspectableNode: + return + + var node: InspectableNode = current_object as InspectableNode + + # Get the graph edit from the node's graph view + if not node.graph_view or not node.graph_view.get_parent(): + return + + var graph_edit: MonologueGraphEdit = node.graph_view.get_parent() as MonologueGraphEdit + if not graph_edit: + return + + if not graph_edit.connection_manager: + return + + # Get the connected node from the connection manager + var connected_node: InspectableNode = graph_edit.connection_manager.get_connected_node(node, property.name) + + if connected_node and connected_node.graph_view: + EventBus.request_node_selection.emit(connected_node, connected_node.storyline_id) + + +func _is_list(property: Property) -> bool: + return property.type in ["list", "collection"] + + +func _on_external_property_changed( + obj: InspectableObject, property_name: String, _is_undo: bool +) -> void: + if not obj: + return + + var property: Property = obj.get_property(property_name) + if not property: + return + + if not property.get_settings_value("visible_in_inspector", true): + return + + if current_object in obj.get_property_children(property_name): + return + + _pending_expand_category = property.get_category() + + if obj == current_object: + rebuild() + return + + inspect(obj) + + +func _on_back_button_pressed() -> void: + if history.is_empty(): + return + + inspect(history.pop_back(), true) diff --git a/common/layouts/side_panel/side_panel.gd.uid b/common/ui/inspector/inspector_panel.gd.uid similarity index 100% rename from common/layouts/side_panel/side_panel.gd.uid rename to common/ui/inspector/inspector_panel.gd.uid diff --git a/common/ui/inspector/inspector_panel.tscn b/common/ui/inspector/inspector_panel.tscn new file mode 100644 index 00000000..fac92486 --- /dev/null +++ b/common/ui/inspector/inspector_panel.tscn @@ -0,0 +1,60 @@ +[gd_scene format=3 uid="uid://dgvhvxdrd58qp"] + +[ext_resource type="Script" uid="uid://dtf4ge38njewp" path="res://common/ui/inspector/inspector_panel.gd" id="1_haagr"] +[ext_resource type="Texture2D" uid="uid://b272tbdmvxj20" path="res://ui/assets/icons/play.svg" id="2_34x8o"] +[ext_resource type="Script" uid="uid://bw5hfxuolu4p3" path="res://common/ui/search_bar.gd" id="2_n4c53"] +[ext_resource type="Texture2D" uid="uid://c0cmik715jqff" path="res://ui/assets/icons/arrow_left.svg" id="2_ok6on"] + +[node name="InspectorPanel" type="PanelContainer" unique_id=1918224978] +offset_right = 158.0 +offset_bottom = 121.0 +theme_type_variation = &"InspectorPanel" +script = ExtResource("1_haagr") + +[node name="VBox" type="VBoxContainer" parent="." unique_id=591075862] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Header" type="VBoxContainer" parent="VBox" unique_id=713426282] +layout_mode = 2 + +[node name="SearchBar" type="HBoxContainer" parent="VBox/Header" unique_id=711294994] +layout_mode = 2 + +[node name="BackButton" type="Button" parent="VBox/Header/SearchBar" unique_id=1944554305] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Inspect the last object" +theme_type_variation = &"IconButton" +icon = ExtResource("2_ok6on") + +[node name="SearchBar" type="LineEdit" parent="VBox/Header/SearchBar" unique_id=1045515954] +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("2_n4c53") + +[node name="RunButton" type="Button" parent="VBox/Header/SearchBar" unique_id=62143223] +unique_name_in_owner = true +layout_mode = 2 +icon = ExtResource("2_34x8o") +icon_alignment = 1 + +[node name="FieldHeader" type="HBoxContainer" parent="VBox/Header" unique_id=2122655254] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Scroller" type="ScrollContainer" parent="VBox" unique_id=150618628] +clip_contents = false +layout_mode = 2 +size_flags_vertical = 3 +follow_focus = true + +[node name="Fields" type="VBoxContainer" parent="VBox/Scroller" unique_id=801729299] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_type_variation = &"HBoxContainerCompact" + +[connection signal="pressed" from="VBox/Header/SearchBar/RunButton" to="." method="_on_rfh_button_pressed"] diff --git a/common/ui/label_memory_usage.gd b/common/ui/label_memory_usage.gd new file mode 100644 index 00000000..d305fb6d --- /dev/null +++ b/common/ui/label_memory_usage.gd @@ -0,0 +1,15 @@ +class_name MemoryUsageLabel extends Label + + +func _process(_delta: float) -> void: + var memory_usage: float = Performance.get_monitor(Performance.MEMORY_STATIC) / 1_048_576.0 + text = "Memory: %.1f MB" % memory_usage + + var fps: float = Performance.get_monitor(Performance.TIME_FPS) + var time_process: float = Performance.get_monitor(Performance.TIME_PROCESS) + var draw_calls: float = Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME) + tooltip_text = "FPS: %.1f (Process: %.2f ms)\nDraw calls: %d" % [fps, time_process, draw_calls] + + process_mode = ProcessMode.PROCESS_MODE_DISABLED + await get_tree().create_timer(1.0, true, false, true).timeout + process_mode = ProcessMode.PROCESS_MODE_ALWAYS diff --git a/common/ui/label_memory_usage.gd.uid b/common/ui/label_memory_usage.gd.uid new file mode 100644 index 00000000..6fec730d --- /dev/null +++ b/common/ui/label_memory_usage.gd.uid @@ -0,0 +1 @@ +uid://bi4poj4ni2xba diff --git a/common/layouts/language_switcher/language_option.gd b/common/ui/language_switcher/language_option.gd similarity index 93% rename from common/layouts/language_switcher/language_option.gd rename to common/ui/language_switcher/language_option.gd index 9840cc3b..d8483e4c 100644 --- a/common/layouts/language_switcher/language_option.gd +++ b/common/ui/language_switcher/language_option.gd @@ -3,6 +3,8 @@ class_name LanguageOption extends Button signal language_name_changed(old_name: String, new_name: String, option: LanguageOption) signal language_removed(option: LanguageOption) +## Short language code used as the dictionary key in translatable values (e.g. "en", "fr"). +var language_code: String = "en" var language_name: String: set = set_language_name diff --git a/common/layouts/language_switcher/language_option.gd.uid b/common/ui/language_switcher/language_option.gd.uid similarity index 100% rename from common/layouts/language_switcher/language_option.gd.uid rename to common/ui/language_switcher/language_option.gd.uid diff --git a/common/layouts/language_switcher/language_option.tscn b/common/ui/language_switcher/language_option.tscn similarity index 94% rename from common/layouts/language_switcher/language_option.tscn rename to common/ui/language_switcher/language_option.tscn index 4cb8ce93..e80c9649 100644 --- a/common/layouts/language_switcher/language_option.tscn +++ b/common/ui/language_switcher/language_option.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=4 format=3 uid="uid://x84tcb0t06mo"] +[gd_scene format=3 uid="uid://x84tcb0t06mo"] -[ext_resource type="Script" uid="uid://cwe4fsgrbbc8u" path="res://common/layouts/language_switcher/language_option.gd" id="1_ektfo"] +[ext_resource type="Script" uid="uid://cwe4fsgrbbc8u" path="res://common/ui/language_switcher/language_option.gd" id="1_ektfo"] [ext_resource type="Texture2D" uid="uid://dixramdai4iq4" path="res://ui/assets/icons/pen.svg" id="1_p47ob"] [ext_resource type="Texture2D" uid="uid://hmjhxdsk3pwj" path="res://ui/assets/icons/trash.svg" id="3_1qtny"] diff --git a/common/ui/language_switcher/language_switcher.gd b/common/ui/language_switcher/language_switcher.gd new file mode 100644 index 00000000..c53584e7 --- /dev/null +++ b/common/ui/language_switcher/language_switcher.gd @@ -0,0 +1,92 @@ +class_name LanguageSwitcher extends OptionButton + +## Current graph edit which has the loaded languages. +var graph_edit: MonologueGraphEdit +var _is_applying: bool = false + + +func _ready() -> void: + item_selected.connect(_on_item_selected) + ProjectManager.project_loaded.connect(_on_project_loaded) + EventBus.refresh.connect(_on_global_refresh) + EventBus.enable_language_switcher.connect(set_enabled) + EventBus.disable_language_switcher.connect(set_enabled.bind(false)) + load_languages() + + +func _on_project_loaded() -> void: + var project: MonologueProject = ProjectManager.current_project + project.get_collection("languages").content_changed.connect(_on_languages_content_changed) + _on_languages_content_changed() + + +func _on_languages_content_changed() -> void: + var project: MonologueProject = ProjectManager.current_project + var languages: Array = project.get_collection_value("languages") + load_languages(languages) + + +func load_languages(languages: Array = []) -> void: + clear() + + var seen_codes: PackedStringArray = [] + for i: int in languages.size(): + var lang: Dictionary = languages[i] + var lang_code: String = lang.get("code", {}).get("value", "en") + var lang_name: String = lang.get("name", {}).get("value", "Language %d" % (i + 1)) + if seen_codes.has(lang_code): + continue + seen_codes.append(lang_code) + add_item(lang_name) + set_item_metadata(item_count - 1, lang_code) + + var restore_idx: int = 0 + if item_count > 0: + select(restore_idx) + _apply_selection(restore_idx) + + +func select_by_locale(locale_code: String) -> void: + for i: int in item_count: + if get_item_metadata(i) == locale_code: + select(i) + _apply_selection(i) + return + + +func set_enabled(active: bool = true) -> void: + disabled = not active + + +func _on_item_selected(idx: int) -> void: + _apply_selection(idx) + + +func _on_global_refresh() -> void: + # Another source (e.g. TranslatableField LocalizationOption) changed the language; + # update our selection to match without re-emitting refresh. + if _is_applying: + return + var project: MonologueProject = ProjectManager.current_project + var active: String = project.active_language_code if project else "en" + for i: int in item_count: + if get_item_metadata(i) == active: + if selected != i: + select(i) + if graph_edit: + graph_edit.current_language_index = i + return + + +func _apply_selection(idx: int) -> void: + if idx < 0 or idx >= item_count: + return + _is_applying = true + var code: String = get_item_metadata(idx) + var project: MonologueProject = ProjectManager.current_project + if graph_edit: + graph_edit.current_language_index = idx + if project: + project.active_language_code = code + EventBus.refresh.emit() + _is_applying = false diff --git a/common/layouts/language_switcher/language_switcher.gd.uid b/common/ui/language_switcher/language_switcher.gd.uid similarity index 100% rename from common/layouts/language_switcher/language_switcher.gd.uid rename to common/ui/language_switcher/language_switcher.gd.uid diff --git a/common/ui/language_switcher/language_switcher.tscn b/common/ui/language_switcher/language_switcher.tscn new file mode 100644 index 00000000..55b40a31 --- /dev/null +++ b/common/ui/language_switcher/language_switcher.tscn @@ -0,0 +1,7 @@ +[gd_scene format=3 uid="uid://cb3se7h7akt47"] + +[ext_resource type="Script" uid="uid://bd5c0hwweht6a" path="res://common/ui/language_switcher/language_switcher.gd" id="2_mi5fl"] + +[node name="LanguageSwitcher" type="OptionButton" unique_id=348986612] +custom_minimum_size = Vector2(160, 0) +script = ExtResource("2_mi5fl") diff --git a/common/ui/menu_buttons/editor_menu_button.gd b/common/ui/menu_buttons/editor_menu_button.gd new file mode 100644 index 00000000..e8a15d12 --- /dev/null +++ b/common/ui/menu_buttons/editor_menu_button.gd @@ -0,0 +1,126 @@ +@abstract +class_name EditorMenuButton extends Button + +var _menu: PopupMenu +var _callbacks: Dictionary = {} + +func _ready() -> void: + _menu = PopupMenu.new() + add_child(_menu) + _menu.id_pressed.connect(_on_id_pressed) + toggled.connect(_on_pressed) + _menu.visibility_changed.connect(_on_menu_visibility_changed) + toggle_mode = true + + +func _input(event: InputEvent) -> void: + if event.is_pressed(): + _rebuild_menu() + _menu.activate_item_by_event(event) + + + +@abstract +func _build_menu() -> void + + +func _rebuild_menu() -> void: + for child in _menu.get_children(): + child.queue_free() + _menu.clear() + _callbacks.clear() + _build_menu() + + +func _on_pressed(toggled_on: bool) -> void: + if not toggled_on: + _menu.hide() + return + _show_menu() + +func _on_menu_visibility_changed() -> void: + if _menu.visible: + return + set_pressed_no_signal(false) + +func _show_menu() -> void: + _rebuild_menu() + var rect: Rect2 = get_global_rect() + rect.position.y += size.y + _menu.popup_on_parent(rect) + +func add_row(label: String, callback: Callable = Callable(), enabled: bool = true, actions: Array[String] = []) -> void: + var id := _menu.item_count + _menu.add_item(label, id) + + var item_shortcut: Shortcut = Shortcut.new() + for action_name: String in actions: + if not InputMap.has_action(action_name): + Log.warn("Invalid action name '%s'." % action_name) + continue + + var input_action: InputEventAction = InputEventAction.new() + input_action.action = action_name + item_shortcut.events.append(input_action) + _menu.set_item_shortcut(id, item_shortcut) + + if callback.is_valid(): + _callbacks[id] = callback + + _menu.set_item_disabled(id, not enabled) + +# Callback receives the new bool value. +func add_check_row(label: String, checked := false, callback: Callable = Callable(), enabled: bool = true, actions: Array[String] = []) -> void: + var id := _menu.item_count + _menu.add_check_item(label, id) + _menu.set_item_checked(_menu.get_item_index(id), checked) + + var item_shortcut: Shortcut = Shortcut.new() + for action_name: String in actions: + if not InputMap.has_action(action_name): + Log.warn("Invalid action name '%s'." % action_name) + continue + + var input_action: InputEventAction = InputEventAction.new() + input_action.action = action_name + item_shortcut.events.append(input_action) + _menu.set_item_shortcut(id, item_shortcut) + + if callback.is_valid(): + _callbacks[id] = callback + + _menu.set_item_disabled(id, not enabled) + +func add_separator(label: String = "") -> void: + _menu.add_separator(label) + +# Returns the submenu PopupMenu to populate. +func add_submenu_row(label: String, callback: Callable = Callable(), enabled: bool = true) -> PopupMenu: + var id := _menu.item_count + var submenu := PopupMenu.new() + submenu.name = label.replace(" ", "_") + "_" + str(_menu.item_count) + _menu.add_child(submenu) + _menu.add_submenu_node_item(label, submenu, id) + submenu.id_pressed.connect(_on_submenu_id_pressed.bind(id)) + if callback.is_valid(): + _callbacks[id] = callback + + _menu.set_item_disabled(id, not enabled) + + return submenu + +func _on_id_pressed(id: int) -> void: + set_pressed_no_signal(false) + var idx := _menu.get_item_index(id) + if _menu.is_item_checkable(idx): + var checked := not _menu.is_item_checked(idx) + _menu.set_item_checked(idx, checked) + if id in _callbacks: + _callbacks[id].call(checked) + elif id in _callbacks: + _callbacks[id].call() + + +func _on_submenu_id_pressed(id: int, submenu_id: int) -> void: + set_pressed_no_signal(false) + _callbacks[submenu_id].call(id) diff --git a/common/ui/menu_buttons/editor_menu_button.gd.uid b/common/ui/menu_buttons/editor_menu_button.gd.uid new file mode 100644 index 00000000..844dbd21 --- /dev/null +++ b/common/ui/menu_buttons/editor_menu_button.gd.uid @@ -0,0 +1 @@ +uid://c54mv1bq3d6u8 diff --git a/common/ui/menu_buttons/graph_add_button.gd b/common/ui/menu_buttons/graph_add_button.gd new file mode 100644 index 00000000..6fc695c9 --- /dev/null +++ b/common/ui/menu_buttons/graph_add_button.gd @@ -0,0 +1,20 @@ +extends EditorMenuButton + +var graph_dict: Dictionary = {} + +func _build_menu() -> void: + var id: int = 0 + for category in NodeBucket.get_categories(false): + var submenu: PopupMenu = add_submenu_row(category, _on_add_node) + + for node: GraphNodeDescriptor in NodeBucket.get_descriptors_by_category(category): + var node_name: String = node.name + var node_display_name: String = node.display_name + submenu.add_item(node_display_name, id) + graph_dict[id] = node_name + id += 1 + + +func _on_add_node(item_id: int) -> void: + var node_name: String = graph_dict[item_id] + EventBus.add_graph_node.emit(node_name) # FIXME signal doesn't work diff --git a/common/ui/menu_buttons/graph_add_button.gd.uid b/common/ui/menu_buttons/graph_add_button.gd.uid new file mode 100644 index 00000000..56b670d3 --- /dev/null +++ b/common/ui/menu_buttons/graph_add_button.gd.uid @@ -0,0 +1 @@ +uid://crx7ke21ixwnx diff --git a/common/ui/menu_buttons/graph_edit_button.gd b/common/ui/menu_buttons/graph_edit_button.gd new file mode 100644 index 00000000..b4720d00 --- /dev/null +++ b/common/ui/menu_buttons/graph_edit_button.gd @@ -0,0 +1,26 @@ +extends EditorMenuButton + +@onready var graph: MonologueGraphEdit = %GraphEdit + +func _build_menu() -> void: + var is_last_storyline: bool = ProjectManager.current_project.storylines.size() <= 1 + add_row("Delete Storyline", _on_delete_storyline, not is_last_storyline) + + +func _get_storyline() -> StorylineDocument: + var storyline_id: String = graph.storyline_id + return ProjectManager.current_project.get_storyline(storyline_id) + + +func _on_delete_storyline() -> void: + var storyline: StorylineDocument = _get_storyline() + EventBus.ask_dialog.emit(_on_delete_storyline_dialog_callback, "Are you sure?", "You are about to delete the storyline '%s'." % storyline.name) + + +func _on_delete_storyline_dialog_callback(response: int) -> void: + if not response == Prompt.CONFIRMED: + return + + var storyline: StorylineDocument = _get_storyline() + ProjectManager.current_project.delete_storyline(storyline) + EventBus.request_storyline_inspection.emit(ProjectManager.current_project.storylines[0]) diff --git a/common/ui/menu_buttons/graph_edit_button.gd.uid b/common/ui/menu_buttons/graph_edit_button.gd.uid new file mode 100644 index 00000000..d7bab374 --- /dev/null +++ b/common/ui/menu_buttons/graph_edit_button.gd.uid @@ -0,0 +1 @@ +uid://mqidcjji32ft diff --git a/common/ui/menu_buttons/graph_view_button.gd b/common/ui/menu_buttons/graph_view_button.gd new file mode 100644 index 00000000..e8fa461f --- /dev/null +++ b/common/ui/menu_buttons/graph_view_button.gd @@ -0,0 +1,15 @@ +extends EditorMenuButton + +func _build_menu() -> void: + add_check_row("Snap", ConfigManager.get_config("snap"), _on_snap, true, ["mnl_graph_snap"]) + add_check_row("Show Grid", ConfigManager.get_config("show_grid"), _on_show_grid, true, ["mnl_graph_show_grid"]) + + +func _on_snap(enabled: bool) -> void: + ConfigManager.set_config("snap", enabled) + EventBus.graph_snap.emit(enabled) + + +func _on_show_grid(enabled: bool) -> void: + ConfigManager.set_config("show_grid", enabled) + EventBus.graph_show_grid.emit(enabled) diff --git a/common/ui/menu_buttons/graph_view_button.gd.uid b/common/ui/menu_buttons/graph_view_button.gd.uid new file mode 100644 index 00000000..9d1ea82d --- /dev/null +++ b/common/ui/menu_buttons/graph_view_button.gd.uid @@ -0,0 +1 @@ +uid://c38kvfr67qro8 diff --git a/common/ui/menu_buttons/main_edit_button.gd b/common/ui/menu_buttons/main_edit_button.gd new file mode 100644 index 00000000..ac015500 --- /dev/null +++ b/common/ui/menu_buttons/main_edit_button.gd @@ -0,0 +1,26 @@ +@warning_ignore_start("int_as_enum_without_match", "int_as_enum_without_cast") +extends EditorMenuButton + + +func _build_menu() -> void: + var command_manager: CommandManager = ProjectManager.current_project.command_manager + add_row("Undo", _on_undo, command_manager.can_undo(), ["mnl_undo"]) + add_row("Redo", _on_redo, command_manager.can_redo(), ["mnl_redo"]) + add_submenu_row("Undo History", Callable(), false) + add_separator() + add_row("Open Localization Utility", Callable(), false) + + +func _on_undo() -> void: + var focus_owner: Control = get_viewport().gui_get_focus_owner() + if focus_owner: + focus_owner.release_focus() + + var command_manager: CommandManager = ProjectManager.current_project.command_manager + command_manager.undo() + + +func _on_redo() -> void: + var command_manager: CommandManager = ProjectManager.current_project.command_manager + command_manager.redo() + diff --git a/common/ui/menu_buttons/main_edit_button.gd.uid b/common/ui/menu_buttons/main_edit_button.gd.uid new file mode 100644 index 00000000..658957aa --- /dev/null +++ b/common/ui/menu_buttons/main_edit_button.gd.uid @@ -0,0 +1 @@ +uid://cate5mgxkgmlh diff --git a/common/ui/menu_buttons/main_file_button.gd b/common/ui/menu_buttons/main_file_button.gd new file mode 100644 index 00000000..0c03c252 --- /dev/null +++ b/common/ui/menu_buttons/main_file_button.gd @@ -0,0 +1,63 @@ +@warning_ignore_start("int_as_enum_without_match", "int_as_enum_without_cast") +extends EditorMenuButton + +var recent_ids: Dictionary = {} + +func _build_menu() -> void: + recent_ids = {} + + add_row("New", _on_new, true, ["mnl_new"]) + add_row("Open...", _on_open, true, ["mnl_open"]) + + var recent_files: PackedStringArray = ProjectManager.get_history() + var recent_menu: PopupMenu = add_submenu_row("Open Recent", _on_open_recent, recent_files.size() > 0) + for file: String in recent_files: + if file == ProjectManager.current_project.project_path: + continue + var id: int = recent_menu.item_count + recent_ids[id] = file + recent_menu.add_item(file.get_file(), id) + add_separator() + + add_row("Save", _on_save, true, ["mnl_save"]) + add_row("Save As...", _on_save_as, false, ["mnl_save_as"]) + add_row("Save Copy...", Callable(), false) + add_row("Save Version", Callable(), false) + add_separator() + add_submenu_row("Export", _on_export, false) + add_separator() + add_row("Quit", _on_quit, true, ["mnl_exit"]) + + +func _on_new() -> void: + var new_project: MonologueProject = MonologueProject.new() + ProjectManager.load_project(new_project) + + +func _on_open() -> void: + EventBus.open_file_request.emit(_on_open_dialog_callback, MonologueProject.FORMAT_FILTER) + + +func _on_open_dialog_callback(path: String) -> void: + ProjectManager.load_project_from_path(path) + + +func _on_open_recent(item_id: int) -> void: + ProjectManager.load_project_from_path(recent_ids[item_id]) + + +func _on_save() -> void: + ProjectManager.save_project(ProjectManager.current_project) + + +func _on_save_as() -> void: + pass + + +func _on_export() -> void: + pass + + +func _on_quit() -> void: + if await ProjectManager.close_current_project(): + get_tree().quit() diff --git a/common/ui/menu_buttons/main_file_button.gd.uid b/common/ui/menu_buttons/main_file_button.gd.uid new file mode 100644 index 00000000..6ca53d01 --- /dev/null +++ b/common/ui/menu_buttons/main_file_button.gd.uid @@ -0,0 +1 @@ +uid://0vqgemjkhdc2 diff --git a/common/ui/menu_buttons/main_help_button.gd b/common/ui/menu_buttons/main_help_button.gd new file mode 100644 index 00000000..17ef1453 --- /dev/null +++ b/common/ui/menu_buttons/main_help_button.gd @@ -0,0 +1,33 @@ +extends EditorMenuButton + + +func _build_menu() -> void: + add_row("Documentation", Callable(), false) + add_row("Support Us", Callable(), false) + add_row("Contribute", Callable(), false) + add_separator() + add_row("Report a Bug", _on_report_a_bug) + add_row("Copy System Info", _on_save_system_info) + + +func _on_report_a_bug() -> void: + OS.shell_open("https://github.com/monologue-tool/monologue/issues/new?template=BUG-REPORT.yml") + + +func _on_save_system_info() -> void: + var lines: Array[String] = [] + + var version: String = ProjectSettings.get_setting("application/config/version") + + lines.append("Monologue %s - System Info" % version) + lines.append("") + lines.append("version: %s " % version) + lines.append("is debug: %s" % OS.is_debug_build()) + lines.append("os: %s (%s)" % [OS.get_name(), OS.get_version()]) + lines.append("cpu: %s (%s core)" % [OS.get_processor_name(), OS.get_processor_count()]) + lines.append("memory:") + for key: String in OS.get_memory_info().keys(): + var value: int = OS.get_memory_info()[key] + lines.append(" - %s: %.2f Gb (%s bytes)" % [key, value/1e+9, value]) + + DisplayServer.clipboard_set("\n".join(lines)) diff --git a/common/ui/menu_buttons/main_help_button.gd.uid b/common/ui/menu_buttons/main_help_button.gd.uid new file mode 100644 index 00000000..344b51b5 --- /dev/null +++ b/common/ui/menu_buttons/main_help_button.gd.uid @@ -0,0 +1 @@ +uid://b8tpwk6h2qmhc diff --git a/common/ui/menu_buttons/main_view_button.gd b/common/ui/menu_buttons/main_view_button.gd new file mode 100644 index 00000000..3944559c --- /dev/null +++ b/common/ui/menu_buttons/main_view_button.gd @@ -0,0 +1,28 @@ +extends EditorMenuButton + +func _build_menu() -> void: + add_check_row("Show Inspector", ConfigManager.get_config("show_inspector"), _on_show_inspector) + add_check_row("Show Project Explorer", ConfigManager.get_config("show_project_explorer"), _on_show_project_explorer) + add_check_row("Show Console", ConfigManager.get_config("show_console"), _on_show_console) + add_check_row("Show Status Bar", ConfigManager.get_config("show_status_bar"), _on_show_status_bar) + add_separator() + add_row("Regenerate theme", _on_regenerate_theme, true, ["mnl_regenerate_theme"]) + +func _on_show_inspector(enabled: bool) -> void: + ConfigManager.set_config("show_inspector", enabled) + EventBus.show_inspector.emit(enabled) + +func _on_show_project_explorer(enabled: bool) -> void: + ConfigManager.set_config("show_project_explorer", enabled) + EventBus.show_project_explorer.emit(enabled) + +func _on_show_console(enabled: bool) -> void: + ConfigManager.set_config("show_console", enabled) + EventBus.show_console.emit(enabled) + +func _on_show_status_bar(enabled: bool) -> void: + ConfigManager.set_config("show_status_bar", enabled) + EventBus.show_status_bar.emit(enabled) + +func _on_regenerate_theme() -> void: + ThemeLayout.generate_and_apply_theme() diff --git a/common/ui/menu_buttons/main_view_button.gd.uid b/common/ui/menu_buttons/main_view_button.gd.uid new file mode 100644 index 00000000..db81fb57 --- /dev/null +++ b/common/ui/menu_buttons/main_view_button.gd.uid @@ -0,0 +1 @@ +uid://rb4fwghqhreo diff --git a/common/ui/project_panel/project_panel.gd b/common/ui/project_panel/project_panel.gd new file mode 100644 index 00000000..79047ac5 --- /dev/null +++ b/common/ui/project_panel/project_panel.gd @@ -0,0 +1,120 @@ +extends PanelContainer + +@onready var project_explorer: VBoxContainer = %ProjectExplorer + +@onready var delete_icon: DPITexture = preload("res://ui/assets/icons/trash.svg") +@onready var add_icon: DPITexture = preload("res://ui/assets/icons/plus.svg") + +var collections_fc: FoldableContainer +var collections_container: VBoxContainer +var storylines_fc: FoldableContainer +var storylines_container: VBoxContainer + + +func _ready() -> void: + ProjectManager.project_loaded.connect(_rebuild_explorer) + EventBus.request_object_inspection.connect(_on_request_object_inspection) + EventBus.request_storyline_inspection.connect(_on_request_storyline_inspection) + EventBus.show_project_explorer.connect(_on_event_show_project_panel) + EventBus.storyline_deleted.connect(_rebuild_explorer) + + visible = ConfigManager.get_config("show_project_explorer") + + _rebuild_explorer() + + +func _on_event_show_project_panel(_visible: bool) -> void: + visible = ConfigManager.get_config("show_project_explorer") + + +func _rebuild_explorer() -> void: + for child: Control in project_explorer.get_children(): + child.queue_free() + + collections_fc = null + collections_container = null + storylines_fc = null + storylines_container = null + + var project: MonologueProject = ProjectManager.current_project + if not project: + return + + + var display_name: String = project.name + if project.project_path.is_empty(): + display_name = "<%s>" % display_name + if project.is_dirty: + display_name = "%s*" % display_name + + collections_fc = _create_foldable_container("Collections") + collections_container = VBoxContainer.new() + collections_fc.add_child(collections_container) + + storylines_fc = _create_foldable_container("Storylines") + storylines_container = VBoxContainer.new() + storylines_fc.add_child(storylines_container) + + var add_button: Button = Button.new() + add_button.theme_type_variation = "IconButton" + add_button.icon = add_icon + add_button.pressed.connect(_on_add_storyline_button_pressed) + storylines_fc.add_title_bar_control(add_button) + + var collection_button_group: ButtonGroup = ButtonGroup.new() + for collection: CollectionDocument in project.collections: + var collection_btn: Button = Button.new() + collection_btn.text = collection.name + collection_btn.toggle_mode = true + collection_btn.theme_type_variation = "ToggleButton" + collection_btn.alignment = HORIZONTAL_ALIGNMENT_LEFT + collection_btn.button_group = collection_button_group + collection_btn.pressed.connect(_on_collection_button_pressed.bind(collection)) + collection_btn.set_meta("document", collection) + collections_container.add_child(collection_btn) + + var storyline_button_group: ButtonGroup = ButtonGroup.new() + for storyline: StorylineDocument in project.storylines: + var storyline_btn: Button = Button.new() + storyline_btn.text = storyline.name + storyline_btn.toggle_mode = true + storyline_btn.theme_type_variation = "ToggleButton" + storyline_btn.alignment = HORIZONTAL_ALIGNMENT_LEFT + storyline_btn.button_group = storyline_button_group + storyline_btn.pressed.connect(_on_storyline_button_pressed.bind(storyline)) + storyline_btn.set_meta("document", storyline) + storylines_container.add_child(storyline_btn) + + +func _create_foldable_container(title: String) -> FoldableContainer: + var container: FoldableContainer = FoldableContainer.new() + container.title = title + container.title_alignment = HORIZONTAL_ALIGNMENT_LEFT + project_explorer.add_child(container) + return container + +func _on_collection_button_pressed(collection: CollectionDocument) -> void: + EventBus.request_object_inspection.emit(collection) + +func _on_storyline_button_pressed(storyline: StorylineDocument) -> void: + EventBus.request_storyline_inspection.emit(storyline) + +func _on_add_storyline_button_pressed() -> void: + ProjectManager.current_project.add_new_storyline() + _rebuild_explorer() + +func _on_request_object_inspection(object: InspectableObject) -> void: + if not collections_container: + return + + for button: Button in collections_container.get_children(): + var collection: CollectionDocument = button.get_meta("document") + button.set_pressed_no_signal(collection == object) + +func _on_request_storyline_inspection(storyline: StorylineDocument) -> void: + if not storylines_container: + return + + for button: Button in storylines_container.get_children(): + var btn_storyline: StorylineDocument = button.get_meta("document") + button.set_pressed_no_signal(btn_storyline == storyline) diff --git a/common/ui/project_panel/project_panel.gd.uid b/common/ui/project_panel/project_panel.gd.uid new file mode 100644 index 00000000..5585eaf0 --- /dev/null +++ b/common/ui/project_panel/project_panel.gd.uid @@ -0,0 +1 @@ +uid://creja611rnsop diff --git a/common/ui/ribbon/ribbon.gd b/common/ui/ribbon/ribbon.gd deleted file mode 100644 index 48b65cf4..00000000 --- a/common/ui/ribbon/ribbon.gd +++ /dev/null @@ -1,11 +0,0 @@ -extends Control - -@onready var animation_player = $AnimationPlayer - - -func _on_animation_finished(_animation_name): - queue_free() - - -func _on_timeout(): - animation_player.play("disappear") diff --git a/common/ui/ribbon/ribbon.gd.uid b/common/ui/ribbon/ribbon.gd.uid deleted file mode 100644 index 19280cc9..00000000 --- a/common/ui/ribbon/ribbon.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ckcvcwuuvb05f diff --git a/common/ui/ribbon/ribbon.tscn b/common/ui/ribbon/ribbon.tscn deleted file mode 100644 index 24dfcd1e..00000000 --- a/common/ui/ribbon/ribbon.tscn +++ /dev/null @@ -1,91 +0,0 @@ -[gd_scene load_steps=7 format=3 uid="uid://jmhc22ufpdcg"] - -[ext_resource type="Script" uid="uid://ckcvcwuuvb05f" path="res://common/ui/ribbon/ribbon.gd" id="1_01cej"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_i71ea"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0862745, 0.101961, 0.12549, 1) -corner_radius_top_left = 4 -corner_radius_top_right = 4 -corner_radius_bottom_right = 4 -corner_radius_bottom_left = 4 - -[sub_resource type="LabelSettings" id="LabelSettings_egetl"] -font_size = 12 - -[sub_resource type="Animation" id="Animation_hkoa3"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 0, -"values": [Color(1, 1, 1, 1)] -} - -[sub_resource type="Animation" id="Animation_f6e3r"] -resource_name = "disappear" -length = 0.5 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.5), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_t7v2m"] -_data = { -&"RESET": SubResource("Animation_hkoa3"), -&"disappear": SubResource("Animation_f6e3r") -} - -[node name="Ribbon" type="Control"] -layout_mode = 3 -anchors_preset = 0 -script = ExtResource("1_01cej") - -[node name="Label" type="Label" parent="."] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -29.0 -offset_top = -40.0 -offset_right = 29.0 -offset_bottom = -15.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_styles/normal = SubResource("StyleBoxFlat_i71ea") -text = "Copied!" -label_settings = SubResource("LabelSettings_egetl") -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Timer" type="Timer" parent="."] -one_shot = true -autostart = true - -[node name="AnimationPlayer" type="AnimationPlayer" parent="."] -root_node = NodePath("../Label") -libraries = { -&"": SubResource("AnimationLibrary_t7v2m") -} - -[connection signal="timeout" from="Timer" to="." method="_on_timeout"] -[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_finished"] diff --git a/common/ui/search_bar.gd b/common/ui/search_bar.gd new file mode 100644 index 00000000..20d6dcd6 --- /dev/null +++ b/common/ui/search_bar.gd @@ -0,0 +1,5 @@ +class_name SearchBar extends LineEdit + + +func _ready() -> void: + placeholder_text = "Search" diff --git a/common/ui/search_bar.gd.uid b/common/ui/search_bar.gd.uid new file mode 100644 index 00000000..efba23b7 --- /dev/null +++ b/common/ui/search_bar.gd.uid @@ -0,0 +1 @@ +uid://bw5hfxuolu4p3 diff --git a/common/layouts/split_container.gd b/common/ui/split_container.gd similarity index 100% rename from common/layouts/split_container.gd rename to common/ui/split_container.gd diff --git a/common/layouts/split_container.gd.uid b/common/ui/split_container.gd.uid similarity index 100% rename from common/layouts/split_container.gd.uid rename to common/ui/split_container.gd.uid diff --git a/common/ui/status_bar/status_bar.gd b/common/ui/status_bar/status_bar.gd new file mode 100644 index 00000000..5371bbc6 --- /dev/null +++ b/common/ui/status_bar/status_bar.gd @@ -0,0 +1,10 @@ +extends PanelContainer + + +func _ready() -> void: + EventBus.show_status_bar.connect(_on_show_status_bar) + visible = ConfigManager.get_config("show_status_bar") + + +func _on_show_status_bar(_visible: bool) -> void: + visible = ConfigManager.get_config("show_status_bar") diff --git a/common/ui/status_bar/status_bar.gd.uid b/common/ui/status_bar/status_bar.gd.uid new file mode 100644 index 00000000..6c8c3662 --- /dev/null +++ b/common/ui/status_bar/status_bar.gd.uid @@ -0,0 +1 @@ +uid://cd5ku56gijql7 diff --git a/common/ui/tooltip_label.gd b/common/ui/tooltip_label.gd new file mode 100644 index 00000000..836b1885 --- /dev/null +++ b/common/ui/tooltip_label.gd @@ -0,0 +1,12 @@ +class_name TooltipLabel extends Label + + +func _ready() -> void: + Tooltip.tooltip_update.connect(_on_tooltip_update) + + +func _on_tooltip_update() -> void: + if not Tooltip.text: + await get_tree().create_timer(0.1).timeout + + text = Tooltip.text.replace("\n", " ") diff --git a/common/ui/tooltip_label.gd.uid b/common/ui/tooltip_label.gd.uid new file mode 100644 index 00000000..84e4f496 --- /dev/null +++ b/common/ui/tooltip_label.gd.uid @@ -0,0 +1 @@ +uid://dvcbv5akqxtp5 diff --git a/common/ui/version_label.gd b/common/ui/version_label.gd new file mode 100644 index 00000000..136c3060 --- /dev/null +++ b/common/ui/version_label.gd @@ -0,0 +1,5 @@ +extends Label + + +func _ready() -> void: + text = ProjectSettings.get_setting("application/config/version") diff --git a/common/ui/version_label.gd.uid b/common/ui/version_label.gd.uid new file mode 100644 index 00000000..75a58ca2 --- /dev/null +++ b/common/ui/version_label.gd.uid @@ -0,0 +1 @@ +uid://db0p0vwsnoa06 diff --git a/common/ui/vertical_placement_indicator.tscn b/common/ui/vertical_placement_indicator.tscn deleted file mode 100644 index 5681f3aa..00000000 --- a/common/ui/vertical_placement_indicator.tscn +++ /dev/null @@ -1,57 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dxh14t4020yrk"] - -[ext_resource type="Texture2D" uid="uid://mjb34j4suabp" path="res://common/layouts/character_edit/assets/triangle_pointer.svg" id="1_8h1l7"] - -[node name="HPlacementIndicator" type="Control"] -z_index = 2 -layout_mode = 3 -anchor_bottom = 0.031 -offset_bottom = 2.2 -size_flags_vertical = 3 -metadata/_edit_horizontal_guides_ = [2.0] - -[node name="ColorRect2" type="ColorRect" parent="."] -custom_minimum_size = Vector2(1, 0) -layout_mode = 1 -anchors_preset = 9 -anchor_bottom = 1.0 -offset_left = -2.0 -offset_right = 2.0 -offset_bottom = -2.0 -grow_vertical = 2 -color = Color(0, 0, 0, 1) - -[node name="TextureRect" type="TextureRect" parent="."] -layout_mode = 0 -offset_left = -7.0 -offset_right = 8.0 -offset_bottom = 7.0 -texture = ExtResource("1_8h1l7") -expand_mode = 2 -stretch_mode = 5 - -[node name="TextureRect2" type="TextureRect" parent="."] -layout_mode = 1 -anchors_preset = -1 -anchor_top = 1.0 -anchor_bottom = 1.019 -offset_left = -7.0 -offset_top = -8.0 -offset_right = 8.0 -offset_bottom = -1.513 -texture = ExtResource("1_8h1l7") -expand_mode = 2 -stretch_mode = 5 -flip_v = true - -[node name="ColorRect" type="ColorRect" parent="."] -custom_minimum_size = Vector2(1, 0) -layout_mode = 1 -anchors_preset = 9 -anchor_bottom = 1.0 -offset_left = -1.0 -offset_top = 2.0 -offset_right = 1.0 -offset_bottom = -3.0 -grow_vertical = 2 -color = Color(0.835294, 0.317647, 0.376471, 1) diff --git a/common/ui/windows/file_dialog/file_dialog.gd b/common/ui/windows/file_dialog/file_dialog.gd new file mode 100644 index 00000000..a8cd31b6 --- /dev/null +++ b/common/ui/windows/file_dialog/file_dialog.gd @@ -0,0 +1,93 @@ +class_name GlobalFileDialog extends FileDialog + +var _callback: Callable + + +func _ready() -> void: + EventBus.save_file_request.connect(_on_save_file_request) + EventBus.open_file_request.connect(_on_open_file_request) + EventBus.open_files_request.connect(_on_open_files_request) + EventBus.open_dir_request.connect(_on_open_dir_request) + + +func save_file( + callback: Callable, filter_list: PackedStringArray = [], root_subdir: String = "", options: Array = [] +) -> void: + _on_save_file_request(callback, filter_list, root_subdir, options) + + +func _on_save_file_request( + callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "", options: Array = [] +) -> void: + title = "Save" + ok_button_text = "Save" + file_mode = FileDialog.FILE_MODE_SAVE_FILE + _core_request(callable, filter_list, root_subdir, options) + + +func _on_open_file_request( + callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "", options: Array = [] +) -> void: + title = "Open" + ok_button_text = "Open" + file_mode = FileDialog.FILE_MODE_OPEN_FILE + _core_request(callable, filter_list, root_subdir, options) + + +func _on_open_files_request( + callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "", options: Array = [] +) -> void: + title = "Open" + ok_button_text = "Open" + file_mode = FileDialog.FILE_MODE_OPEN_FILES + + if not files_selected.is_connected(_on_files_selected): + files_selected.connect(_on_files_selected) + + _core_request(callable, filter_list, root_subdir, options) + + +func _on_open_dir_request( + callable: Callable, root_subdir: String = "", options: Array = [] +) -> void: + title = "Open" + ok_button_text = "Open" + file_mode = FileDialog.FILE_MODE_OPEN_DIR + + _core_request(callable, [], root_subdir, options) + + +func _core_request( + callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "", options: Array = [] +) -> void: + if not root_subdir.ends_with(PathUtil.get_separator()): + root_subdir += PathUtil.get_separator() + + clear_filters() + + _callback = callable + filters = filter_list + current_path = root_subdir + option_count = 0 + + for option: Dictionary in options: + var opt_name: String = option.get("name", "") as String + var opt_values: PackedStringArray = option.get("values", []) as PackedStringArray + var opt_default_value_index: int = option.get("default_value_index", 0) as int + add_option(opt_name, opt_values, opt_default_value_index) + + popup_centered() + + +func _on_file_selected(path: String) -> void: + if file_mode == FILE_MODE_SAVE_FILE: + FileAccess.open(path, FileAccess.WRITE) + _callback.call(path as String) + + +func _on_files_selected(paths: PackedStringArray) -> void: + _callback.call(paths as Array) + + +func _on_dir_selected(dir: String) -> void: + _callback.call(dir) diff --git a/common/windows/file_dialog/file_dialog.gd.uid b/common/ui/windows/file_dialog/file_dialog.gd.uid similarity index 100% rename from common/windows/file_dialog/file_dialog.gd.uid rename to common/ui/windows/file_dialog/file_dialog.gd.uid diff --git a/common/windows/file_dialog/file_dialog.tscn b/common/ui/windows/file_dialog/file_dialog.tscn similarity index 76% rename from common/windows/file_dialog/file_dialog.tscn rename to common/ui/windows/file_dialog/file_dialog.tscn index a2e73c9a..239769f7 100644 --- a/common/windows/file_dialog/file_dialog.tscn +++ b/common/ui/windows/file_dialog/file_dialog.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=2 format=3 uid="uid://bhf5b42cv02m4"] +[gd_scene format=3 uid="uid://bhf5b42cv02m4"] -[ext_resource type="Script" uid="uid://b2l7hjfyrnr3x" path="res://common/windows/file_dialog/file_dialog.gd" id="1_mjdqk"] +[ext_resource type="Script" uid="uid://b2l7hjfyrnr3x" path="res://common/ui/windows/file_dialog/file_dialog.gd" id="1_mjdqk"] [node name="FileDialog" type="FileDialog"] auto_translate_mode = 1 diff --git a/common/ui/windows/graph_node_picker/graph_node_picker.gd b/common/ui/windows/graph_node_picker/graph_node_picker.gd new file mode 100644 index 00000000..561e3d7a --- /dev/null +++ b/common/ui/windows/graph_node_picker/graph_node_picker.gd @@ -0,0 +1,90 @@ +class_name GraphNodePicker extends MonologueWindow + +@onready var node_tree := %Tree + +## The node in which the picker was spawned/dragged from. +var from_node: String +## The port in which the picker was spawned/dragged from. +var from_port: int +## Mouse release global position. +var release: Variant = null +## Release position adjusted to the graph's scroll and zoom. +var graph_release: Variant = null +## Center position of the graph. +var center: Variant = null + + +func _ready() -> void: + super._ready() + hide() + force_native = true + EventBus.enable_picker_mode.connect(_on_enable_picker_mode) + + +func _on_enable_picker_mode( + node: String = "", + port: int = -1, + mouse_pos: Variant = null, + graph_release_pos: Variant = null, + center_pos: Variant = null, + center_window: bool = false +) -> void: + open_for_node(node, port, mouse_pos, graph_release_pos, center_pos, center_window) + + +func close() -> void: + hide() + + +func flush() -> void: + from_node = "" + from_port = -1 + release = null + graph_release = null + center = null + + +func open_for_node( + node: String = "", + port: int = -1, + mouse_pos: Variant = null, + graph_release_pos: Variant = null, + center_pos: Variant = null, + _center_window: bool = false +) -> void: + flush() + from_node = node + from_port = port + release = mouse_pos + graph_release = graph_release_pos + center = center_pos + + if node_tree: + node_tree.reload_tree() + + popup() + move_to_center() + + var root_window := get_tree().get_root() + if root_window: + current_screen = root_window.current_screen + + grab_focus() + + +func _on_close_requested() -> void: + close() + + +func _on_cancel_button_pressed() -> void: + close() + + +func _on_create_button_pressed() -> void: + if node_tree.create_selected_descriptor(): + close() + + +func _on_visibility_changed() -> void: + var root_screen: int = get_tree().get_root().current_screen + current_screen = root_screen diff --git a/common/windows/graph_node_picker/graph_node_picker.gd.uid b/common/ui/windows/graph_node_picker/graph_node_picker.gd.uid similarity index 100% rename from common/windows/graph_node_picker/graph_node_picker.gd.uid rename to common/ui/windows/graph_node_picker/graph_node_picker.gd.uid diff --git a/common/ui/windows/graph_node_picker/graph_node_picker.tscn b/common/ui/windows/graph_node_picker/graph_node_picker.tscn new file mode 100644 index 00000000..1b73ac19 --- /dev/null +++ b/common/ui/windows/graph_node_picker/graph_node_picker.tscn @@ -0,0 +1,68 @@ +[gd_scene format=3 uid="uid://cmpsaafag7cwl"] + +[ext_resource type="Script" uid="uid://bcagysl3b0ncf" path="res://common/ui/windows/graph_node_picker/graph_node_picker.gd" id="1_5oqa8"] +[ext_resource type="Script" uid="uid://dqy8aovapnh5h" path="res://common/ui/windows/graph_node_picker/graph_node_tree.gd" id="2_5kkcj"] + +[node name="GraphNodePicker" type="Window" unique_id=1073528209] +auto_translate_mode = 1 +oversampling_override = 1.0 +title = "Create Graph Node" +position = Vector2i(0, 36) +size = Vector2i(345, 425) +transient = true +transient_to_focused = true +exclusive = true +unresizable = true +popup_window = true +extend_to_title = true +keep_title_visible = true +content_scale_mode = 1 +script = ExtResource("1_5oqa8") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=913745854] +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 +theme_type_variation = &"TreeContainer" + +[node name="VBox" type="VBoxContainer" parent="PanelContainer" unique_id=275582391] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 10 + +[node name="SearchBar" type="LineEdit" parent="PanelContainer/VBox" unique_id=2034907761] +layout_mode = 2 +placeholder_text = "Search" + +[node name="Tree" type="Tree" parent="PanelContainer/VBox" unique_id=598794160] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +hide_root = true +script = ExtResource("2_5kkcj") + +[node name="HBox" type="HBoxContainer" parent="PanelContainer/VBox" unique_id=1295967926] +layout_mode = 2 +theme_override_constants/separation = 25 +alignment = 1 + +[node name="CreateButton" type="Button" parent="PanelContainer/VBox/HBox" unique_id=1474710004] +unique_name_in_owner = true +layout_mode = 2 +disabled = true +text = "Create" + +[node name="CancelButton" type="Button" parent="PanelContainer/VBox/HBox" unique_id=1371885642] +layout_mode = 2 +text = "Cancel" + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="text_changed" from="PanelContainer/VBox/SearchBar" to="PanelContainer/VBox/Tree" method="_on_search_bar_text_changed"] +[connection signal="item_selected" from="PanelContainer/VBox/Tree" to="PanelContainer/VBox/Tree" method="_on_item_selected"] +[connection signal="pressed" from="PanelContainer/VBox/HBox/CreateButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="PanelContainer/VBox/HBox/CreateButton" to="PanelContainer/VBox/Tree" method="_create"] +[connection signal="pressed" from="PanelContainer/VBox/HBox/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/common/ui/windows/graph_node_picker/graph_node_tree.gd b/common/ui/windows/graph_node_picker/graph_node_tree.gd new file mode 100644 index 00000000..20df9366 --- /dev/null +++ b/common/ui/windows/graph_node_picker/graph_node_tree.gd @@ -0,0 +1,121 @@ +class_name GraphNodeTree +extends Tree + +@onready var create_btn: Button = %CreateButton +@onready var window: GraphNodePicker = $"../../.." + +var _first_item_found: bool = false + + +func _ready() -> void: + reload_tree() + + +func reload_tree() -> void: + clear() + _first_item_found = false + var root := create_item() + create_btn.disabled = true + + var categories: PackedStringArray = NodeBucket.get_categories(true) + if categories.is_empty(): + _add_placeholder(root, "No nodes available") + return + + for category: String in categories: + var category_item := create_item(root) + category_item.set_text(0, category) + category_item.collapsed = true + category_item.set_selectable(0, false) + var descriptors: Array = NodeBucket.get_descriptors_by_category(category) + for descriptor: BucketDescriptor in descriptors: + _create_descriptor_item(category_item, descriptor) + + deselect_all() + + +func _add_placeholder(parent: TreeItem, text: String) -> void: + var placeholder := create_item(parent) + placeholder.set_text(0, text) + placeholder.set_selectable(0, false) + + +func _create_descriptor_item(parent: TreeItem, descriptor: BucketDescriptor) -> void: + var item := create_item(parent) + item.set_text(0, descriptor.display_name) + if descriptor.icon: + item.set_icon(0, descriptor.icon) + item.set_metadata(0, descriptor.name) + + +func _create() -> bool: + var selected := get_selected() + if selected == null: + return false + var descriptor_name: Variant = selected.get_metadata(0) + if descriptor_name == null: + return false + EventBus.add_graph_node.emit(String(descriptor_name), window) + deselect_all() + return true + + +func create_selected_descriptor() -> bool: + return _create() + + +func _on_item_activated() -> void: + var item: TreeItem = get_selected() + if item == null: + return + if item.get_child_count() > 0: + item.collapsed = !item.collapsed + return + if _create(): + window.close() + + +func _on_item_selected() -> void: + var item: TreeItem = get_selected() + if item == null: + create_btn.disabled = true + return + create_btn.disabled = item.get_metadata(0) == null + + +func _on_search_bar_text_changed(new_text: String) -> void: + if not new_text.lstrip(" "): + _recursive_show_item(get_root()) + return + + _first_item_found = false + _recursive_item_match(new_text, get_root()) + + +func _recursive_item_match(text: String, item: TreeItem) -> bool: + var match_text: bool = false + + if item.get_child_count() > 0: + for child: TreeItem in item.get_children(): + var child_match: bool = _recursive_item_match(text, child) + if child_match: + match_text = true + elif item.get_text(0).containsn(text): + match_text = true + if not _first_item_found: + item.select(0) + _first_item_found = true + + item.visible = match_text + if match_text: + item.collapsed = false + + return match_text + + +func _recursive_show_item(item: TreeItem) -> void: + item.visible = true + if not get_root() == item: + item.collapsed = true + for child: TreeItem in item.get_children(): + _recursive_show_item(child) diff --git a/common/windows/graph_node_picker/graph_node_tree.gd.uid b/common/ui/windows/graph_node_picker/graph_node_tree.gd.uid similarity index 100% rename from common/windows/graph_node_picker/graph_node_tree.gd.uid rename to common/ui/windows/graph_node_picker/graph_node_tree.gd.uid diff --git a/common/ui/windows/monologue_window.gd b/common/ui/windows/monologue_window.gd new file mode 100644 index 00000000..5899f886 --- /dev/null +++ b/common/ui/windows/monologue_window.gd @@ -0,0 +1,32 @@ +class_name MonologueWindow extends Window + + +func _ready() -> void: + EventBus.window_out.connect(_on_window_out) + + get_parent().connect("resized", _on_resized) + update_size.call_deferred() + visibility_changed.connect(_on_visibility_changed) + _on_visibility_changed() + + App._update_window(get_window(), false) + + +func update_size() -> void: + size.x = size.x + App._update_window(get_window(), false) + + +func _on_resized() -> void: + update_size() + + +func _on_visibility_changed() -> void: + if visible: + EventBus.show_dimmer.emit() + return + EventBus.hide_dimmer.emit() + + +func _on_window_out() -> void: + hide() diff --git a/common/windows/monologue_window.gd.uid b/common/ui/windows/monologue_window.gd.uid similarity index 100% rename from common/windows/monologue_window.gd.uid rename to common/ui/windows/monologue_window.gd.uid diff --git a/common/ui/windows/preview_window/preview_window.gd b/common/ui/windows/preview_window/preview_window.gd new file mode 100644 index 00000000..628086a8 --- /dev/null +++ b/common/ui/windows/preview_window/preview_window.gd @@ -0,0 +1,34 @@ +extends MonologueWindow + +@export var dns_checkbox: CheckBox + + +func _ready() -> void: + # TODO + EventBus.window_out.connect(hide) + return + #var version: Variant = ProjectSettings.get("application/config/version") + #var is_pre_release: bool = version.split("-").size() > 1 +# + #var do_not_show: Variant = App.preferences.get_value("Preview", "do_not_show", false) + #var last_version: Variant = App.preferences.get_value("Preview", "last_version", "") + #var is_new: bool = version != last_version + #visible = is_pre_release and (not do_not_show or is_new) + #grab_focus() +# + #super._ready() + + +func _on_button_pressed() -> void: + # TODO + if dns_checkbox and false: + var checked: bool = dns_checkbox.button_pressed + var version: Variant = ProjectSettings.get("application/config/version") + App.preferences.set_value("Preview", "do_not_show", checked) + App.preferences.set_value("Preview", "last_version", version) + App.preferences.save(Constants.PREFERENCES_PATH) + hide() + + +func _on_rich_text_label_meta_clicked(meta: Variant) -> void: + OS.shell_open(str(meta)) diff --git a/common/windows/preview_window/preview_window.gd.uid b/common/ui/windows/preview_window/preview_window.gd.uid similarity index 100% rename from common/windows/preview_window/preview_window.gd.uid rename to common/ui/windows/preview_window/preview_window.gd.uid diff --git a/common/windows/preview_window/preview_window.tscn b/common/ui/windows/preview_window/preview_window.tscn similarity index 93% rename from common/windows/preview_window/preview_window.tscn rename to common/ui/windows/preview_window/preview_window.tscn index 934916dc..e06fc9b9 100644 --- a/common/windows/preview_window/preview_window.tscn +++ b/common/ui/windows/preview_window/preview_window.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=2 format=3 uid="uid://d3f7d4bb40iht"] +[gd_scene format=3 uid="uid://d3f7d4bb40iht"] -[ext_resource type="Script" uid="uid://c7bl8kcyl4fxb" path="res://common/windows/preview_window/preview_window.gd" id="1_15o47"] +[ext_resource type="Script" uid="uid://c7bl8kcyl4fxb" path="res://common/ui/windows/preview_window/preview_window.gd" id="1_15o47"] [node name="PreviewWindow" type="Window" node_paths=PackedStringArray("dns_checkbox")] auto_translate_mode = 1 diff --git a/common/ui/windows/prompt_window/prompt_window.gd b/common/ui/windows/prompt_window/prompt_window.gd new file mode 100644 index 00000000..234a1f97 --- /dev/null +++ b/common/ui/windows/prompt_window/prompt_window.gd @@ -0,0 +1,69 @@ +class_name Prompt extends MonologueWindow + +enum { + CONFIRMED, + DENIED, + CANCELLED +} + +signal confirmed +signal denied +signal cancelled + +@onready var title_label: Label = %TitleLabel +@onready var description_label: Label = %DescriptionLabel +@onready var confirm_button: Button = %ConfirmButton +@onready var deny_button: Button = %DenyButton +@onready var cancel_button: Button = %CancelButton + +var _callback: Callable + + +func _ready() -> void: + EventBus.ask_dialog.connect(_on_ask_request) + EventBus.window_out.connect(_on_cancel_button_pressed) + + +func _on_confirm_button_pressed() -> void: + hide() + confirmed.emit() + _callback_handler(Prompt.CONFIRMED) + + +func _on_deny_button_pressed() -> void: + hide() + denied.emit() + _callback_handler(Prompt.DENIED) + + +func _on_cancel_button_pressed() -> void: + if not visible: + return + + hide() + cancelled.emit() + _callback_handler(Prompt.CANCELLED) + + +func _callback_handler(response: int) -> void: + if not _callback: + Log.error("Prompt window has no callback.") + return + + if _callback.get_argument_count() < 1: + Log.error("Invalid callback.") + return + + _callback.call(response) + + +func _on_ask_request(callback: Callable, header: String, description: String, confirm_text: String = "Yes", deny_text: String = "No", cancel_text: String = "Cancel") -> void: + _callback = callback + + title_label.text = header + description_label.text = description + confirm_button.text = confirm_text + deny_button.text = deny_text + cancel_button.text = cancel_text + + show() diff --git a/common/windows/prompt_window/prompt_window.gd.uid b/common/ui/windows/prompt_window/prompt_window.gd.uid similarity index 100% rename from common/windows/prompt_window/prompt_window.gd.uid rename to common/ui/windows/prompt_window/prompt_window.gd.uid diff --git a/common/ui/windows/prompt_window/prompt_window.tscn b/common/ui/windows/prompt_window/prompt_window.tscn new file mode 100644 index 00000000..a755b66c --- /dev/null +++ b/common/ui/windows/prompt_window/prompt_window.tscn @@ -0,0 +1,104 @@ +[gd_scene format=3 uid="uid://bkreq3xdr7gxw"] + +[ext_resource type="Script" uid="uid://bb5cq5tmook8s" path="res://common/ui/windows/prompt_window/prompt_window.gd" id="1_u0ucq"] +[ext_resource type="Texture2D" uid="uid://bou7qelcr5dwn" path="res://icon.png" id="2_kx1ym"] + +[node name="PromptWindow" type="Window" unique_id=2120395406] +transparent_bg = true +oversampling_override = 1.0 +initial_position = 2 +size = Vector2i(798, 226) +wrap_controls = true +unresizable = true +borderless = true +always_on_top = true +transparent = true +popup_window = true +script = ExtResource("1_u0ucq") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=488027916] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -331.5 +offset_top = -73.0 +offset_right = 331.5 +offset_bottom = 73.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_type_variation = &"OuterPanel" + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=1352960012] +layout_mode = 2 +theme_override_constants/margin_left = 25 +theme_override_constants/margin_top = 25 +theme_override_constants/margin_right = 25 +theme_override_constants/margin_bottom = 25 + +[node name="HBox" type="HBoxContainer" parent="PanelContainer/MarginContainer" unique_id=264572394] +layout_mode = 2 +theme_override_constants/separation = 25 + +[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/HBox" unique_id=1691622229] +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +texture = ExtResource("2_kx1ym") +expand_mode = 1 +stretch_mode = 5 + +[node name="VBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/HBox" unique_id=125036158] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +theme_type_variation = &"VBoxContainer_Big" + +[node name="LabelHBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/HBox/VBox" unique_id=186543798] +layout_mode = 2 +theme_type_variation = &"VBoxContainer_Medium" + +[node name="TitleLabel" type="Label" parent="PanelContainer/MarginContainer/HBox/VBox/LabelHBox" unique_id=1794345615] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"HeaderSmall" +text = "Save changes?" +clip_text = true +text_overrun_behavior = 2 + +[node name="DescriptionLabel" type="Label" parent="PanelContainer/MarginContainer/HBox/VBox/LabelHBox" unique_id=2108403130] +unique_name_in_owner = true +custom_minimum_size = Vector2(500, 75) +layout_mode = 2 +theme_type_variation = &"NoteLabel" +text = "The document you have opened will be closed. Do you want to save the changes?" +autowrap_mode = 3 + +[node name="HBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/HBox/VBox" unique_id=1773416990] +layout_mode = 2 +theme_type_variation = &"HBoxContainer_Big" + +[node name="ConfirmButton" type="Button" parent="PanelContainer/MarginContainer/HBox/VBox/HBox" unique_id=1491844496] +unique_name_in_owner = true +custom_minimum_size = Vector2(125, 0) +layout_mode = 2 +text = "Yes" + +[node name="DenyButton" type="Button" parent="PanelContainer/MarginContainer/HBox/VBox/HBox" unique_id=768820840] +unique_name_in_owner = true +custom_minimum_size = Vector2(125, 0) +layout_mode = 2 +text = "No" + +[node name="CancelButton" type="Button" parent="PanelContainer/MarginContainer/HBox/VBox/HBox" unique_id=1596929283] +unique_name_in_owner = true +custom_minimum_size = Vector2(125, 0) +layout_mode = 2 +text = "Cancel" + +[connection signal="tree_exited" from="." to="." method="_on_tree_exited"] +[connection signal="pressed" from="PanelContainer/MarginContainer/HBox/VBox/HBox/ConfirmButton" to="." method="_on_confirm_button_pressed"] +[connection signal="pressed" from="PanelContainer/MarginContainer/HBox/VBox/HBox/DenyButton" to="." method="_on_deny_button_pressed"] +[connection signal="pressed" from="PanelContainer/MarginContainer/HBox/VBox/HBox/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/common/ui/windows/welcome_window/recent_file_button.tscn b/common/ui/windows/welcome_window/recent_file_button.tscn new file mode 100644 index 00000000..b2dd0cc2 --- /dev/null +++ b/common/ui/windows/welcome_window/recent_file_button.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3 uid="uid://dqp3uifnpuc3b"] + +[ext_resource type="Texture2D" uid="uid://c77fmeslcdgo7" path="res://ui/assets/icons/file.svg" id="1_qxcco"] + +[node name="RecentFileButton" type="Button" unique_id=79523968] +text = "default.json" +icon = ExtResource("1_qxcco") +alignment = 0 diff --git a/common/ui/windows/welcome_window/recent_file_container.gd b/common/ui/windows/welcome_window/recent_file_container.gd new file mode 100644 index 00000000..bc57c8f1 --- /dev/null +++ b/common/ui/windows/welcome_window/recent_file_container.gd @@ -0,0 +1,46 @@ +class_name RecentFilesContainer extends VBoxContainer + +@export var button_container: Control +@export var save_path: String = Constants.HISTORY_PATH + +@onready var button_scene: PackedScene = preload("uid://dqp3uifnpuc3b") + + +func _ready() -> void: + refresh() + + +func create_button(filepath: String) -> Button: + var btn: Button = button_scene.instantiate() + var path: String = filepath.replace("\\", "/") + path = path.replace("//", "/") + var paths: Array = path.split("/") + var btn_text: String = "" + btn_text = paths.back() + + btn.text = btn_text + btn.pressed.connect(_on_project_btn_pressed.bind(filepath)) + btn.tooltip_text = filepath + button_container.add_child(btn) + return btn + + +## Load the recent file history save and create buttons for it. +func load_history() -> void: + var data: Array = ProjectManager.get_history() + + for path: Variant in data.slice(0, 3): + create_button(path) + + +## Remake the recent file list. +func refresh() -> void: + for child: Node in button_container.get_children(): + child.queue_free() + + load_history() + + +func _on_project_btn_pressed(path: String) -> void: + ProjectManager.load_project_from_path(path) + diff --git a/common/windows/welcome_window/recent_file_container.gd.uid b/common/ui/windows/welcome_window/recent_file_container.gd.uid similarity index 100% rename from common/windows/welcome_window/recent_file_container.gd.uid rename to common/ui/windows/welcome_window/recent_file_container.gd.uid diff --git a/common/windows/welcome_window/recent_file_container.tscn b/common/ui/windows/welcome_window/recent_file_container.tscn similarity index 82% rename from common/windows/welcome_window/recent_file_container.tscn rename to common/ui/windows/welcome_window/recent_file_container.tscn index a651d55a..95981d09 100644 --- a/common/windows/welcome_window/recent_file_container.tscn +++ b/common/ui/windows/welcome_window/recent_file_container.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=3 format=3 uid="uid://dbq34pvukxcce"] +[gd_scene format=3 uid="uid://dbq34pvukxcce"] + +[ext_resource type="Script" uid="uid://dpp03lxtpf520" path="res://common/ui/windows/welcome_window/recent_file_container.gd" id="1_0a36f"] -[ext_resource type="Script" uid="uid://dpp03lxtpf520" path="res://common/windows/welcome_window/recent_file_container.gd" id="1_0a36f"] [sub_resource type="LabelSettings" id="LabelSettings_1l82f"] font_color = Color(0.572549, 0.572549, 0.572549, 1) diff --git a/common/ui/windows/welcome_window/welcome_window.gd b/common/ui/windows/welcome_window/welcome_window.gd new file mode 100644 index 00000000..d41f2a7c --- /dev/null +++ b/common/ui/windows/welcome_window/welcome_window.gd @@ -0,0 +1,49 @@ +class_name WelcomeWindow extends MonologueWindow + +## Callback for loading projects after file selection. +var file_callback: Callable = func(path: String) -> void: EventBus.load_project.emit(path) + +@onready var close_button: BaseButton = %CloseButton +@onready var recent_files: RecentFilesContainer = %RecentProjectsContainer +@onready var version_label: Label = %VersionLabel + +var is_startup: bool = false + + +func _ready() -> void: + super._ready() + is_startup = true + version_label.text = "v" + ProjectSettings.get("application/config/version") + EventBus.show_welcome.connect(show) + EventBus.hide_welcome.connect(_on_hide) + EventBus.window_out.connect(_on_hide) + + +func _input(_event: InputEvent) -> void: + if Input.is_key_pressed(KEY_ESCAPE) and not is_startup: + hide() + + +func _on_hide() -> void: + is_startup = false + hide() + + +func _on_new_file_btn_pressed() -> void: + EventBus.save_file_request.emit(load_callback, ["*.json"]) + + +func _on_open_file_btn_pressed() -> void: + EventBus.open_file_request.emit(load_callback, ["*.json"]) + + +func load_callback(path: String) -> void: + EventBus.load_project.emit(path) + + +func _on_github_btn_pressed() -> void: + OS.shell_open("https://github.com/monologue-tool/monologue") + + +func _on_bug_report_btn_pressed() -> void: + OS.shell_open("https://github.com/monologue-tool/monologue/issues/new?template=BUG-REPORT.yml") diff --git a/common/windows/welcome_window/welcome_window.gd.uid b/common/ui/windows/welcome_window/welcome_window.gd.uid similarity index 100% rename from common/windows/welcome_window/welcome_window.gd.uid rename to common/ui/windows/welcome_window/welcome_window.gd.uid diff --git a/common/ui/windows/welcome_window/welcome_window.tscn b/common/ui/windows/welcome_window/welcome_window.tscn new file mode 100644 index 00000000..6f3b74a3 --- /dev/null +++ b/common/ui/windows/welcome_window/welcome_window.tscn @@ -0,0 +1,155 @@ +[gd_scene format=3 uid="uid://bqqcww601rcx5"] + +[ext_resource type="Script" uid="uid://d5rdj8rv35j2" path="res://common/ui/windows/welcome_window/welcome_window.gd" id="1_hscvo"] +[ext_resource type="Texture2D" uid="uid://bddodmw1pm42r" path="res://title_banner@0.5x.png" id="2_ked33"] +[ext_resource type="Texture2D" uid="uid://faci8gjgsxub" path="res://ui/assets/icons/file_plus.svg" id="3_myesc"] +[ext_resource type="Texture2D" uid="uid://bbw07aw88fo0p" path="res://ui/assets/icons/folder_search.svg" id="4_b8nur"] +[ext_resource type="PackedScene" uid="uid://dspmmme0jspdx" path="res://common/ui/button_close.tscn" id="5_h3u5k"] +[ext_resource type="Script" uid="uid://dpp03lxtpf520" path="res://common/ui/windows/welcome_window/recent_file_container.gd" id="5_kbbyc"] +[ext_resource type="Texture2D" uid="uid://e3jit73s1eld" path="res://ui/assets/icons/globe.svg" id="6_drngv"] + +[node name="WelcomeWindow" type="Window" unique_id=2049348513] +auto_translate_mode = 1 +transparent_bg = true +oversampling_override = 1.0 +initial_position = 2 +size = Vector2i(400, 300) +wrap_controls = true +transient = true +transient_to_focused = true +exclusive = true +unresizable = true +borderless = true +always_on_top = true +transparent = true +script = ExtResource("1_hscvo") + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=304586190] +clip_children = 2 +custom_minimum_size = Vector2(400, 300) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 2 +theme_type_variation = &"OuterPanel" + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer" unique_id=1396188399] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBox" type="VBoxContainer" parent="PanelContainer/MarginContainer" unique_id=931898420] +layout_mode = 2 +theme_override_constants/separation = 18 + +[node name="VBoxBanner" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox" unique_id=601017340] +layout_mode = 2 + +[node name="Banner" type="TextureRect" parent="PanelContainer/MarginContainer/VBox/VBoxBanner" unique_id=1955073093] +layout_mode = 2 +texture = ExtResource("2_ked33") +expand_mode = 5 +stretch_mode = 4 + +[node name="VersionLabel" type="Label" parent="PanelContainer/MarginContainer/VBox/VBoxBanner" unique_id=2121328899] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +theme_type_variation = &"NoteLabel" +text = "v.X.X.X" + +[node name="VBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox" unique_id=1914830556] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 10 + +[node name="HBoxTop" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox" unique_id=2030833797] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="NewProjectContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop" unique_id=333831418] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/NewProjectContainer" unique_id=1290412333] +layout_mode = 2 +theme_type_variation = &"NoteLabel" +text = "New Project" + +[node name="VBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/NewProjectContainer" unique_id=979534612] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="NewDefaultBtn" type="Button" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/NewProjectContainer/VBox" unique_id=1461901955] +layout_mode = 2 +text = "Default" +icon = ExtResource("3_myesc") +alignment = 0 + +[node name="New" type="Button" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/NewProjectContainer/VBox" unique_id=565168940] +layout_mode = 2 +text = "Advanced" +icon = ExtResource("3_myesc") +alignment = 0 + +[node name="RecentProjectsContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop" unique_id=1536484239 node_paths=PackedStringArray("button_container")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("5_kbbyc") +button_container = NodePath("RecentFilesContainer") + +[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/RecentProjectsContainer" unique_id=1144685476] +layout_mode = 2 +theme_type_variation = &"NoteLabel" +text = "Recent Projects" + +[node name="RecentFilesContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxTop/RecentProjectsContainer" unique_id=895787872] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HSeparator" type="HSeparator" parent="PanelContainer/MarginContainer/VBox/VBox" unique_id=929910609] +layout_mode = 2 + +[node name="HBoxBottom" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox" unique_id=166630770] +layout_mode = 2 + +[node name="VBoxOpen" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom" unique_id=840436526] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="OpenFileBtn" type="Button" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom/VBoxOpen" unique_id=524926956] +layout_mode = 2 +text = "Open..." +icon = ExtResource("4_b8nur") +alignment = 0 + +[node name="VBoxLink" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom" unique_id=1152916866] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="GitHubBtn" type="Button" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom/VBoxLink" unique_id=943099583] +layout_mode = 2 +text = "GitHub" +icon = ExtResource("6_drngv") +alignment = 0 + +[node name="BugReportBtn" type="Button" parent="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom/VBoxLink" unique_id=1758014313] +layout_mode = 2 +text = "Report Bug" +icon = ExtResource("6_drngv") +alignment = 0 + +[node name="CloseButton" parent="PanelContainer/MarginContainer" unique_id=2052377836 instance=ExtResource("5_h3u5k")] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 0 + +[connection signal="pressed" from="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom/VBoxLink/GitHubBtn" to="." method="_on_github_btn_pressed"] +[connection signal="pressed" from="PanelContainer/MarginContainer/VBox/VBox/HBoxBottom/VBoxLink/BugReportBtn" to="." method="_on_bug_report_btn_pressed"] diff --git a/common/windows/file_dialog/file_dialog.gd b/common/windows/file_dialog/file_dialog.gd deleted file mode 100644 index 6de2b963..00000000 --- a/common/windows/file_dialog/file_dialog.gd +++ /dev/null @@ -1,62 +0,0 @@ -class_name GlobalFileDialog extends FileDialog - -var _callback: Callable - - -func _ready(): - GlobalSignal.add_listener("save_file_request", _on_save_file_request) - GlobalSignal.add_listener("open_file_request", _on_open_file_request) - GlobalSignal.add_listener("open_files_request", _on_open_files_request) - - -func _on_save_file_request( - callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "" -) -> void: - title = "Save" - ok_button_text = "Save" - file_mode = FileDialog.FILE_MODE_SAVE_FILE - _core_request(callable, filter_list, root_subdir) - - -func _on_open_file_request( - callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "" -) -> void: - title = "Open" - ok_button_text = "Open" - file_mode = FileDialog.FILE_MODE_OPEN_FILE - _core_request(callable, filter_list, root_subdir) - - -func _on_open_files_request( - callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = "" -) -> void: - title = "Open" - ok_button_text = "Open" - file_mode = FileDialog.FILE_MODE_OPEN_FILES - - if not files_selected.is_connected(_on_files_selected): - files_selected.connect(_on_files_selected) - - _core_request(callable, filter_list, root_subdir) - - -func _core_request(callable: Callable, filter_list: PackedStringArray = [], - root_subdir: String = "") -> void: - if not root_subdir.ends_with(Path.get_separator()): - root_subdir += Path.get_separator() - - _callback = callable - filters = filter_list - current_path = root_subdir - - popup_centered() - - -func _on_file_selected(path: String) -> void: - if file_mode == FILE_MODE_SAVE_FILE: - FileAccess.open(path, FileAccess.WRITE) - _callback.call(path as String) - - -func _on_files_selected(paths: PackedStringArray) -> void: - _callback.call(paths as Array) diff --git a/common/windows/graph_node_picker/graph_node_picker.gd b/common/windows/graph_node_picker/graph_node_picker.gd deleted file mode 100644 index f54a5215..00000000 --- a/common/windows/graph_node_picker/graph_node_picker.gd +++ /dev/null @@ -1,74 +0,0 @@ -class_name GraphNodePicker extends Window - -## Reference to the tab switcher so that the picker knows which tab it is in. -@export var switcher: GraphEditSwitcher - -@onready var dimmer := $"../Dimmer" - -## The node in which the picker was spawned/dragged from. -var from_node: String -## The port in which the picker was spawned/dragged from. -var from_port: int -## Mouse release global position. -var release = null -## Release position adjusted to the graph's scroll and zoom. -var graph_release = null -## Center position of the graph. -var center = null - - -func _ready(): - hide() - force_native = true - GlobalSignal.add_listener("enable_picker_mode", _on_enable_picker_mode) - - -func _on_enable_picker_mode( - node: String = "", port: int = -1, mouse_pos = null, graph_release_pos = null, center_pos = null, center_window: bool = false -): - if switcher.current.file_path and (not dimmer or not dimmer.visible): - from_node = node - from_port = port - release = mouse_pos - graph_release = graph_release_pos - center = center_pos - - if from_node != "": - position = Vector2i(release) + get_tree().get_root().position - else: - var mouse_position = Vector2i(get_parent().get_global_mouse_position()) - position = get_tree().get_root().position + mouse_position - current_screen = get_tree().get_root().current_screen - show() - - if center_window: - move_to_center() - - -func close() -> void: - hide() - - -func flush() -> void: - from_node = "" - from_port = -1 - release = null - graph_release = null - center = null - - -func _on_close_requested() -> void: - close() - - -func _on_cancel_button_pressed() -> void: - close() - - -func _on_create_button_pressed() -> void: - close() - - -func _on_visibility_changed() -> void: - var root_screen: int = get_tree().get_root().current_screen - current_screen = root_screen diff --git a/common/windows/graph_node_picker/graph_node_picker.tscn b/common/windows/graph_node_picker/graph_node_picker.tscn deleted file mode 100644 index 1e3d505d..00000000 --- a/common/windows/graph_node_picker/graph_node_picker.tscn +++ /dev/null @@ -1,65 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://cmpsaafag7cwl"] - -[ext_resource type="Script" uid="uid://bcagysl3b0ncf" path="res://common/windows/graph_node_picker/graph_node_picker.gd" id="1_5oqa8"] -[ext_resource type="Script" uid="uid://dqy8aovapnh5h" path="res://common/windows/graph_node_picker/graph_node_tree.gd" id="2_5kkcj"] - -[node name="GraphNodePicker" type="Window"] -auto_translate_mode = 1 -title = "Create Graph Node" -position = Vector2i(0, 36) -size = Vector2i(345, 425) -transient = true -transient_to_focused = true -exclusive = true -extend_to_title = true -keep_title_visible = true -content_scale_mode = 1 -script = ExtResource("1_5oqa8") - -[node name="PanelContainer" type="PanelContainer" parent="."] -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 -theme_type_variation = &"TreeContainer" - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 10 - -[node name="SearchBar" type="LineEdit" parent="PanelContainer/VBoxContainer"] -layout_mode = 2 -placeholder_text = "Search" - -[node name="Tree" type="Tree" parent="PanelContainer/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 -hide_root = true -script = ExtResource("2_5kkcj") - -[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 25 -alignment = 1 - -[node name="CreateButton" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -disabled = true -text = "Create" - -[node name="CancelButton" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -text = "Cancel" - -[connection signal="close_requested" from="." to="." method="_on_close_requested"] -[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] -[connection signal="text_changed" from="PanelContainer/VBoxContainer/SearchBar" to="PanelContainer/VBoxContainer/Tree" method="_on_search_bar_text_changed"] -[connection signal="item_selected" from="PanelContainer/VBoxContainer/Tree" to="PanelContainer/VBoxContainer/Tree" method="_on_item_selected"] -[connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer/CreateButton" to="." method="_on_create_button_pressed"] -[connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer/CreateButton" to="PanelContainer/VBoxContainer/Tree" method="_create"] -[connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/common/windows/graph_node_picker/graph_node_tree.gd b/common/windows/graph_node_picker/graph_node_tree.gd deleted file mode 100644 index 7544a5a9..00000000 --- a/common/windows/graph_node_picker/graph_node_tree.gd +++ /dev/null @@ -1,134 +0,0 @@ -extends Tree - -@onready var create_btn: Button = %CreateButton -@onready var window: GraphNodePicker = $"../../.." - -## The data to build the tree -## An oject can contain keys with name "text", "value", icon" and "children". -var _data = [ - { - "text": "Narration", - "children": - [ - {"text": "Sentence", "icon": "text.svg"}, - {"text": "Choice", "icon": "choice.svg"}, - ] - }, - { - "text": "Logic", - "children": - [ - {"text": "Action", "icon": "action.svg"}, - {"text": "Condition", "icon": "condition.svg"}, - {"text": "Random", "icon": "dice.svg"}, - {"text": "Setter", "icon": "toggle.svg"}, - ] - }, - { - "text": "Flow", - "children": - [ - {"text": "Event", "icon": "calendar.svg"}, - {"text": "Bridge", "icon": "link.svg"}, - {"text": "EndPath", "icon": "exit.svg"}, - {"text": "Wait", "icon": "time.svg"}, - ] - }, - { - "text": "Audio and Visuals", - "children": - [ - {"text": "Audio", "icon": "recording.svg"}, - {"text": "Background", "icon": "picture.svg"}, - {"text": "Character", "icon": "character.svg"}, - ] - }, - { - "text": "Helpers", - "children": - [ - {"text": "Comment", "icon": "comment.svg"}, - {"text": "Reroute", "icon": "path.svg"}, - ] - } -] - -var _first_item_found: bool = false - - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - var root = create_item() - _recusive_load_data(_data, root) - deselect_all() - - -func _recusive_load_data(items: Array, tree_parent: TreeItem) -> void: - for obj: Dictionary in items: - var tree_item = create_item(tree_parent) - tree_item.collapsed = true - - if obj.has("text"): - tree_item.set_text(0, obj.get("text")) - if obj.has("icon"): - var icon_texture = load("res://ui/assets/icons/" + obj.get("icon")) - tree_item.set_icon(0, icon_texture) - if obj.has("children"): - _recusive_load_data(obj.get("children"), tree_item) - - -func _create() -> void: - var node_type = get_selected().get_text(0) - GlobalSignal.emit("add_graph_node", [node_type, window]) - - -func _on_item_activated() -> void: - var item: TreeItem = get_selected() - if item.get_child_count() > 0: - item.collapsed = !item.collapsed - else: - _create() - window.close() - - -func _on_item_selected() -> void: - var item: TreeItem = get_selected() - create_btn.disabled = item.get_child_count() > 0 - - -func _on_search_bar_text_changed(new_text: String) -> void: - if not new_text.lstrip(" "): - _recursive_show_item(get_root()) - return - - _first_item_found = false - _recursive_item_match(new_text, get_root()) - - -func _recursive_item_match(text: String, item: TreeItem) -> bool: - var match_text: bool = false - - if item.get_child_count() > 0: - for child in item.get_children(): - var child_match: bool = _recursive_item_match(text, child) - if child_match: - match_text = true - elif item.get_text(0).containsn(text): - match_text = true - if not _first_item_found: - item.select(0) - _first_item_found = true - - item.visible = match_text - if match_text: - item.collapsed = false - - return match_text - - -func _recursive_show_item(item: TreeItem) -> void: - item.visible = true - if not get_root() == item: - item.collapsed = true - for child in item.get_children(): - _recursive_show_item(child) diff --git a/common/windows/monologue_window.gd b/common/windows/monologue_window.gd deleted file mode 100644 index e8724dc6..00000000 --- a/common/windows/monologue_window.gd +++ /dev/null @@ -1,24 +0,0 @@ -class_name MonologueWindow extends Window - - -func _ready() -> void: - get_parent().connect("resized", _on_resized) - update_size.call_deferred() - visibility_changed.connect(_on_visibility_changed) - _on_visibility_changed() - - -func update_size() -> void: - move_to_center() - size.x = size.x - - -func _on_resized(): - update_size() - - -func _on_visibility_changed(): - if visible: - GlobalSignal.emit("show_dimmer", [self]) - return - GlobalSignal.emit("hide_dimmer", [self]) diff --git a/common/windows/preview_window/preview_window.gd b/common/windows/preview_window/preview_window.gd deleted file mode 100644 index 6bb6553f..00000000 --- a/common/windows/preview_window/preview_window.gd +++ /dev/null @@ -1,30 +0,0 @@ -extends MonologueWindow - -@export var dns_checkbox: CheckBox - - -func _ready() -> void: - var version = ProjectSettings.get("application/config/version") - var is_pre_release = version.split("-").size() > 1 - - var do_not_show = App.preferences.get_value("Preview", "do_not_show", false) - var last_version = App.preferences.get_value("Preview", "last_version", "") - var is_new = version != last_version - visible = is_pre_release and (not do_not_show or is_new) - grab_focus() - - super._ready() - - -func _on_button_pressed() -> void: - if dns_checkbox: - var checked = dns_checkbox.button_pressed - var version = ProjectSettings.get("application/config/version") - App.preferences.set_value("Preview", "do_not_show", checked) - App.preferences.set_value("Preview", "last_version", version) - App.preferences.save(Constants.PREFERENCES_PATH) - hide() - - -func _on_rich_text_label_meta_clicked(meta: Variant) -> void: - OS.shell_open(str(meta)) diff --git a/common/windows/prompt_window/prompt_window.gd b/common/windows/prompt_window/prompt_window.gd deleted file mode 100644 index 0353d8c5..00000000 --- a/common/windows/prompt_window/prompt_window.gd +++ /dev/null @@ -1,39 +0,0 @@ -class_name PromptWindow extends MonologueWindow - -signal confirmed -signal denied -signal cancelled - -const SAVE_PROMPT = "%s has been modified." - -@onready var title_label = %TitleLabel -@onready var description_label = %DescriptionLabel -@onready var confirm_button = %ConfirmButton -@onready var deny_button = %DenyButton -@onready var cancel_button = %CancelButton - - -func prompt_save(filename: String) -> void: - if title_label: - title_label.text = SAVE_PROMPT % Util.truncate_filename(filename.get_file()) - description_label.text = "The document you have opened will be closed. Do you want to save the changes?" - show() - - -func _on_confirm_button_pressed() -> void: - queue_free() - confirmed.emit() - - -func _on_deny_button_pressed() -> void: - queue_free() - denied.emit() - - -func _on_cancel_button_pressed() -> void: - queue_free() - cancelled.emit() - - -func _on_tree_exited() -> void: - GlobalSignal.emit("hide_dimmer", [self]) diff --git a/common/windows/prompt_window/prompt_window.tscn b/common/windows/prompt_window/prompt_window.tscn deleted file mode 100644 index b0e7177b..00000000 --- a/common/windows/prompt_window/prompt_window.tscn +++ /dev/null @@ -1,91 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://bkreq3xdr7gxw"] - -[ext_resource type="Script" uid="uid://bb5cq5tmook8s" path="res://common/windows/prompt_window/prompt_window.gd" id="1_u0ucq"] -[ext_resource type="Texture2D" uid="uid://cfc0nhupnhixg" path="res://icon_min.png" id="2_kx1ym"] - -[node name="PromptWindow" type="Window"] -transparent_bg = true -initial_position = 2 -size = Vector2i(650, 206) -wrap_controls = true -unresizable = true -borderless = true -always_on_top = true -transparent = true -popup_window = true -script = ExtResource("1_u0ucq") - -[node name="PanelContainer" type="PanelContainer" parent="."] -anchors_preset = -1 -anchor_right = 1.0 -anchor_bottom = 0.903 -offset_bottom = -0.0180054 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -theme_type_variation = &"OuterPanel" - -[node name="HBox" type="HBoxContainer" parent="PanelContainer"] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Big" - -[node name="TextureRect" type="TextureRect" parent="PanelContainer/HBox"] -custom_minimum_size = Vector2(80, 0) -layout_mode = 2 -texture = ExtResource("2_kx1ym") -expand_mode = 1 -stretch_mode = 5 - -[node name="VBox" type="VBoxContainer" parent="PanelContainer/HBox"] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -theme_type_variation = &"VBoxContainer_Big" - -[node name="LabelHBox" type="VBoxContainer" parent="PanelContainer/HBox/VBox"] -layout_mode = 2 -theme_type_variation = &"VBoxContainer_Medium" - -[node name="TitleLabel" type="Label" parent="PanelContainer/HBox/VBox/LabelHBox"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"HeaderSmall" -text = "Save changes?" -clip_text = true -text_overrun_behavior = 2 - -[node name="DescriptionLabel" type="Label" parent="PanelContainer/HBox/VBox/LabelHBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(500, 75) -layout_mode = 2 -theme_type_variation = &"Label_Secondary" -text = "The document you have opened will be closed. Do you want to save the changes?" -autowrap_mode = 3 - -[node name="HBox" type="HBoxContainer" parent="PanelContainer/HBox/VBox"] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Big" - -[node name="ConfirmButton" type="Button" parent="PanelContainer/HBox/VBox/HBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(125, 0) -layout_mode = 2 -text = "Yes" - -[node name="DenyButton" type="Button" parent="PanelContainer/HBox/VBox/HBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(125, 0) -layout_mode = 2 -text = "No" - -[node name="CancelButton" type="Button" parent="PanelContainer/HBox/VBox/HBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(125, 0) -layout_mode = 2 -text = "Cancel" - -[connection signal="tree_exited" from="." to="." method="_on_tree_exited"] -[connection signal="pressed" from="PanelContainer/HBox/VBox/HBox/ConfirmButton" to="." method="_on_confirm_button_pressed"] -[connection signal="pressed" from="PanelContainer/HBox/VBox/HBox/DenyButton" to="." method="_on_deny_button_pressed"] -[connection signal="pressed" from="PanelContainer/HBox/VBox/HBox/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/common/windows/welcome_window/recent_file_button.tscn b/common/windows/welcome_window/recent_file_button.tscn deleted file mode 100644 index 847b7612..00000000 --- a/common/windows/welcome_window/recent_file_button.tscn +++ /dev/null @@ -1,10 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dqp3uifnpuc3b"] - -[ext_resource type="Texture2D" uid="uid://c77fmeslcdgo7" path="res://ui/assets/icons/file.svg" id="1_qxcco"] - -[node name="RecentFileButton" type="Button"] -mouse_default_cursor_shape = 2 -theme_override_constants/h_separation = 15 -text = "default.json" -icon = ExtResource("1_qxcco") -alignment = 0 diff --git a/common/windows/welcome_window/recent_file_container.gd b/common/windows/welcome_window/recent_file_container.gd deleted file mode 100644 index 67fcb49d..00000000 --- a/common/windows/welcome_window/recent_file_container.gd +++ /dev/null @@ -1,87 +0,0 @@ -class_name RecentFilesContainer extends VBoxContainer - -@export var button_container: Control -@export var save_path: String = Constants.HISTORY_PATH - -@onready var button_scene: PackedScene = preload( - "res://common/windows/welcome_window/recent_file_button.tscn" -) - -var recent_filepaths: Array = [] - - -func _ready() -> void: - GlobalSignal.add_listener("load_successful", add) - refresh() - - -## Adds a new filepath as recent file and saves it to the history file. -func add(filepath: String) -> void: - var file = FileAccess.open(save_path, FileAccess.WRITE) - if file: - recent_filepaths.erase(filepath) - recent_filepaths.push_front(filepath) - file.store_string(JSON.stringify(recent_filepaths.slice(0, 10))) - file.close() - refresh() - - -func create_button(filepath: String) -> Button: - var btn = button_scene.instantiate() - var btn_text = filepath.replace("\\", "/") - btn_text = btn_text.replace("//", "/") - btn_text = btn_text.split("/") - if btn_text.size() >= 2: - btn_text = btn_text.slice(-2, btn_text.size()) - btn_text = btn_text[0].path_join(btn_text[1]) - else: - btn_text = btn_text.back() - - btn.text = Util.truncate_filename(btn_text) - btn.pressed.connect(GlobalSignal.emit.bind("load_project", [filepath])) - button_container.add_child(btn) - return btn - - -## Create the recent file history save in user directory if it doesn't exist. -func create_file() -> void: - if not FileAccess.file_exists(save_path): - FileAccess.open(save_path, FileAccess.WRITE) - - -## Load the recent file history save and create buttons for it. -func load_file() -> void: - var file = FileAccess.open(save_path, FileAccess.READ) - if file: - var data = parse_history(file.get_as_text()) - file.close() - - for path in data.slice(0, 3): - recent_filepaths.append(path) - create_button(path) - - -## Return only the recent files that still exist as a JSON array. -func parse_history(text: String) -> Array: - var data = JSON.parse_string(text) - if data is Array: - return data.filter(func(p): return FileAccess.file_exists(p)) - return [] - - -## Remake the recent file list. -func refresh() -> void: - for child in button_container.get_children(): - child.queue_free() - recent_filepaths.clear() - create_file() - load_file() - show_or_hide() - - -## Show container if recent file buttons are present, otherwise hide it. -func show_or_hide() -> void: - if button_container.get_child_count() > 0: - show() - else: - hide() diff --git a/common/windows/welcome_window/welcome_window.gd b/common/windows/welcome_window/welcome_window.gd deleted file mode 100644 index 74d8a102..00000000 --- a/common/windows/welcome_window/welcome_window.gd +++ /dev/null @@ -1,41 +0,0 @@ -class_name WelcomeWindow extends MonologueWindow - -## Callback for loading projects after file selection. -var file_callback = func(path): GlobalSignal.emit("load_project", [path]) - -@onready var close_button: BaseButton = %CloseButton -@onready var recent_files: RecentFilesContainer = %RecentFilesContainer -@onready var version_label: Label = %VersionLabel - -var is_startup: bool = false - - -func _ready(): - super._ready() - is_startup = true - version_label.text = "v" + ProjectSettings.get("application/config/version") - GlobalSignal.add_listener("show_welcome", show) - GlobalSignal.add_listener("hide_welcome", _on_hide) - - -func _input(_event: InputEvent) -> void: - if Input.is_key_pressed(KEY_ESCAPE) and not is_startup: - GlobalSignal.emit("last_tab") - hide() - - -func _on_hide() -> void: - is_startup = false - hide() - - -func _on_new_file_btn_pressed() -> void: - GlobalSignal.emit("save_file_request", [load_callback, ["*.json"]]) - - -func _on_open_file_btn_pressed() -> void: - GlobalSignal.emit("open_file_request", [load_callback, ["*.json"]]) - - -func load_callback(path: String) -> void: - GlobalSignal.emit("load_project", [path]) diff --git a/common/windows/welcome_window/welcome_window.tscn b/common/windows/welcome_window/welcome_window.tscn deleted file mode 100644 index 7a4bc96d..00000000 --- a/common/windows/welcome_window/welcome_window.tscn +++ /dev/null @@ -1,109 +0,0 @@ -[gd_scene load_steps=7 format=3 uid="uid://bqqcww601rcx5"] - -[ext_resource type="Script" uid="uid://d5rdj8rv35j2" path="res://common/windows/welcome_window/welcome_window.gd" id="1_hscvo"] -[ext_resource type="Texture2D" uid="uid://bddodmw1pm42r" path="res://title_banner@0.5x.png" id="2_ked33"] -[ext_resource type="Texture2D" uid="uid://faci8gjgsxub" path="res://ui/assets/icons/file_plus.svg" id="3_myesc"] -[ext_resource type="Texture2D" uid="uid://bbw07aw88fo0p" path="res://ui/assets/icons/folder_search.svg" id="4_b8nur"] -[ext_resource type="PackedScene" uid="uid://dspmmme0jspdx" path="res://common/ui/buttons/close_button.tscn" id="5_h3u5k"] -[ext_resource type="Script" uid="uid://dpp03lxtpf520" path="res://common/windows/welcome_window/recent_file_container.gd" id="5_kbbyc"] - -[node name="WelcomeWindow" type="Window"] -auto_translate_mode = 1 -transparent_bg = true -initial_position = 2 -size = Vector2i(450, 297) -wrap_controls = true -transient = true -transient_to_focused = true -exclusive = true -unresizable = true -borderless = true -always_on_top = true -transparent = true -content_scale_mode = 2 -content_scale_aspect = 1 -script = ExtResource("1_hscvo") - -[node name="PanelContainer" type="PanelContainer" parent="."] -clip_children = 2 -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 -theme_type_variation = &"OuterPanel" - -[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] -layout_mode = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] -layout_mode = 2 -theme_type_variation = &"VBoxContainer_Medium" - -[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer"] -custom_minimum_size = Vector2(0, 100) -layout_mode = 2 -texture = ExtResource("2_ked33") -expand_mode = 5 -stretch_mode = 4 - -[node name="CenterContainer" type="CenterContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer"] -layout_mode = 2 -theme_override_constants/separation = 10 - -[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 40 - -[node name="NewFileBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -mouse_default_cursor_shape = 2 -text = "New File" -icon = ExtResource("3_myesc") - -[node name="OpenFileBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/HBoxContainer"] -layout_mode = 2 -mouse_default_cursor_shape = 2 -text = "Open File..." -icon = ExtResource("4_b8nur") - -[node name="RecentFilesContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer" node_paths=PackedStringArray("button_container")] -unique_name_in_owner = true -visible = false -layout_mode = 2 -script = ExtResource("5_kbbyc") -button_container = NodePath("RecentFilesButtonContainer") - -[node name="RecentFilesLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/RecentFilesContainer"] -layout_mode = 2 -theme_type_variation = &"Label_Secondary" -text = "Recent files" - -[node name="RecentFilesButtonContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/RecentFilesContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_override_constants/separation = 3 - -[node name="VersionLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -size_flags_horizontal = 8 -size_flags_vertical = 8 -theme_type_variation = &"Label_Secondary" -text = "v.X.X.X" - -[node name="CloseButton" parent="PanelContainer/MarginContainer" instance=ExtResource("5_h3u5k")] -unique_name_in_owner = true -visible = false -layout_mode = 2 -size_flags_horizontal = 8 -size_flags_vertical = 0 - -[connection signal="pressed" from="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/HBoxContainer/NewFileBtn" to="." method="_on_new_file_btn_pressed"] -[connection signal="pressed" from="PanelContainer/MarginContainer/VBoxContainer/CenterContainer/VBoxContainer/HBoxContainer/OpenFileBtn" to="." method="_on_open_file_btn_pressed"] diff --git a/default_env.tres b/default_env.tres index e546763b..6eef40e6 100644 --- a/default_env.tres +++ b/default_env.tres @@ -1,4 +1,4 @@ -[gd_resource type="Environment" load_steps=2 format=3 uid="uid://bwbt6k2asyaf1"] +[gd_resource type="Environment" format=3 uid="uid://bwbt6k2asyaf1"] [sub_resource type="Sky" id="1"] diff --git a/examples/actions_and_options.json b/examples/actions_and_options.json deleted file mode 100644 index c21eebee..00000000 --- a/examples/actions_and_options.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "EditorVersion": "2.0.0-rc3", - "RootNodeID": "cbfb0a4b-7517-4411-a447-314d106fe280", - "ListNodes": [ - { - "$type": "NodeAction", - "ID": "1c4d520d-d844-4548-a77a-9d5adb3de793", - "NextID": "03ececaa-05c3-4d41-94ac-105a2f89ee16", - "Action": { - "$type": "ActionOption", - "OptionID": "ef2470ef-71ca-463e-85d4-810b3470905d", - "Value": true - }, - "EditorPosition": { - "x": 1200, - "y": 140 - } - }, - { - "$type": "NodeSentence", - "ID": "03ececaa-05c3-4d41-94ac-105a2f89ee16", - "NextID": "e642bbaa-7db8-4138-a96e-6f12afc0e423", - "Sentence": "You've just grab a beautiful knife !", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "EditorPosition": { - "x": 1560, - "y": 160 - } - }, - { - "$type": "NodeSentence", - "ID": "067f2655-e704-4fd5-bec7-e136d19a048d", - "NextID": "a0f15054-7355-4093-af2d-a97a875c2fe9", - "Sentence": "Hello buddy, how are y-", - "SpeakerID": 2, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "EditorPosition": { - "x": 1380, - "y": -120 - } - }, - { - "$type": "NodeEndPath", - "ID": "c5bed1c0-5981-4e6e-a96b-c97dca976356", - "NextStoryName": "", - "EditorPosition": { - "x": 1820, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "4405d244-2acf-4008-b712-0a2041ea9511", - "NextID": "c5bed1c0-5981-4e6e-a96b-c97dca976356", - "Sentence": "You grab the knife by his blade. You have an emoragy and you'll be dead in 20sec.", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "EditorPosition": { - "x": 1380, - "y": 20 - } - }, - { - "$type": "NodeRoot", - "ID": "cbfb0a4b-7517-4411-a447-314d106fe280", - "NextID": "98bc9817-7b7d-4dea-8bb7-626192069677", - "EditorPosition": { - "x": 0, - "y": 0 - } - }, - { - "$type": "NodeBridgeIn", - "ID": "2d5b66bd-4b97-422b-9395-73c1a01ac4fa", - "NextID": "7cf4528b-86eb-4160-958b-f8f0e264bc2b", - "NumberSelector": 1, - "EditorPosition": { - "x": 1640, - "y": 280 - } - }, - { - "$type": "NodeBridgeOut", - "ID": "7cf4528b-86eb-4160-958b-f8f0e264bc2b", - "NextID": "f6f21557-ad44-4b97-a5fe-ec4da6a4278c", - "NumberSelector": 1, - "EditorPosition": { - "x": 600, - "y": 140 - } - }, - { - "$type": "NodeSentence", - "ID": "f19ca553-55f3-41d1-87df-6e434bdb25db", - "NextID": "2d5b66bd-4b97-422b-9395-73c1a01ac4fa", - "Sentence": "There are satanic inscriptions", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "EditorPosition": { - "x": 1200, - "y": 280 - } - }, - { - "$type": "NodeBridgeIn", - "ID": "e642bbaa-7db8-4138-a96e-6f12afc0e423", - "NextID": "7cf4528b-86eb-4160-958b-f8f0e264bc2b", - "NumberSelector": 1, - "EditorPosition": { - "x": 2000, - "y": 160 - } - }, - { - "$type": "NodeEndPath", - "ID": "a0f15054-7355-4093-af2d-a97a875c2fe9", - "NextStoryName": "", - "EditorPosition": { - "x": 1820, - "y": -160 - } - }, - { - "$type": "NodeSentence", - "ID": "98bc9817-7b7d-4dea-8bb7-626192069677", - "NextID": "f6f21557-ad44-4b97-a5fe-ec4da6a4278c", - "Sentence": "Welcome in my Vault!", - "SpeakerID": 1, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "EditorPosition": { - "x": 380, - "y": 0 - } - }, - { - "$type": "NodeChoice", - "ID": "f6f21557-ad44-4b97-a5fe-ec4da6a4278c", - "OptionsID": [ - "ef2470ef-71ca-463e-85d4-810b3470905d", - "3246d4ae-e869-49af-904e-97e784890078", - "8fac677e-859c-458f-bdf0-c35b774b48a0" - ], - "EditorPosition": { - "x": 820, - "y": 0 - } - }, - { - "$type": "NodeOption", - "ID": "ef2470ef-71ca-463e-85d4-810b3470905d", - "NextID": "8a2776c4-91a7-4d41-b5d7-9c581ad69605", - "Sentence": "Kill everyone", - "Enable": false, - "OneShot": false - }, - { - "$type": "NodeOption", - "ID": "3246d4ae-e869-49af-904e-97e784890078", - "NextID": "1c4d520d-d844-4548-a77a-9d5adb3de793", - "Sentence": "Take a knife", - "Enable": true, - "OneShot": true - }, - { - "$type": "NodeOption", - "ID": "8fac677e-859c-458f-bdf0-c35b774b48a0", - "NextID": "f19ca553-55f3-41d1-87df-6e434bdb25db", - "Sentence": "Look under the carpet", - "Enable": true, - "OneShot": false - }, - { - "$type": "NodeDiceRoll", - "ID": "8a2776c4-91a7-4d41-b5d7-9c581ad69605", - "Skill": "", - "Target": 50, - "PassID": "067f2655-e704-4fd5-bec7-e136d19a048d", - "FailID": "4405d244-2acf-4008-b712-0a2041ea9511", - "EditorPosition": { - "x": 1200, - "y": 0 - } - } - ], - "Characters": [ - { - "Reference": "_NARRATOR", - "ID": 0 - }, - { - "Reference": "Vault Boy", - "ID": 1 - }, - { - "Reference": "Random 1", - "ID": 2 - } - ], - "Variables": [ - { - "Name": "Hey", - "Value": 0, - "Type": "Integer" - } - ] -} diff --git a/examples/mr_sharpener/assets/audios/kalimba.wav b/examples/mr_sharpener/assets/audios/kalimba.wav deleted file mode 100644 index 89c64667..00000000 Binary files a/examples/mr_sharpener/assets/audios/kalimba.wav and /dev/null differ diff --git a/examples/mr_sharpener/assets/audios/kalimba.wav.import b/examples/mr_sharpener/assets/audios/kalimba.wav.import deleted file mode 100644 index 3a56fd34..00000000 --- a/examples/mr_sharpener/assets/audios/kalimba.wav.import +++ /dev/null @@ -1,24 +0,0 @@ -[remap] - -importer="wav" -type="AudioStreamWAV" -uid="uid://ndpv213mov7p" -path="res://.godot/imported/kalimba.wav-f31ac538b867971d91f8c3cd46c73f87.sample" - -[deps] - -source_file="res://examples/mr_sharpener/assets/audios/kalimba.wav" -dest_files=["res://.godot/imported/kalimba.wav-f31ac538b867971d91f8c3cd46c73f87.sample"] - -[params] - -force/8_bit=false -force/mono=false -force/max_rate=false -force/max_rate_hz=44100 -edit/trim=false -edit/normalize=false -edit/loop_mode=0 -edit/loop_begin=0 -edit/loop_end=-1 -compress/mode=0 diff --git a/examples/mr_sharpener/assets/audios/mystery_sting.ogg b/examples/mr_sharpener/assets/audios/mystery_sting.ogg deleted file mode 100644 index 391682e2..00000000 Binary files a/examples/mr_sharpener/assets/audios/mystery_sting.ogg and /dev/null differ diff --git a/examples/mr_sharpener/assets/audios/mystery_sting.ogg.import b/examples/mr_sharpener/assets/audios/mystery_sting.ogg.import deleted file mode 100644 index c19f566b..00000000 --- a/examples/mr_sharpener/assets/audios/mystery_sting.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://doehvwbkfdygp" -path="res://.godot/imported/mystery_sting.ogg-3f87a2256476e6cf08d040c6c5b29efe.oggvorbisstr" - -[deps] - -source_file="res://examples/mr_sharpener/assets/audios/mystery_sting.ogg" -dest_files=["res://.godot/imported/mystery_sting.ogg-3f87a2256476e6cf08d040c6c5b29efe.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/examples/mr_sharpener/assets/audios/on_the_road.mp3 b/examples/mr_sharpener/assets/audios/on_the_road.mp3 deleted file mode 100644 index 7fb5dd66..00000000 Binary files a/examples/mr_sharpener/assets/audios/on_the_road.mp3 and /dev/null differ diff --git a/examples/mr_sharpener/assets/audios/on_the_road.mp3.import b/examples/mr_sharpener/assets/audios/on_the_road.mp3.import deleted file mode 100644 index a4c3de48..00000000 --- a/examples/mr_sharpener/assets/audios/on_the_road.mp3.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="mp3" -type="AudioStreamMP3" -uid="uid://7rr4jxqqsg7j" -path="res://.godot/imported/on_the_road.mp3-e9fdc664706e3b792a7b180e59edfa1b.mp3str" - -[deps] - -source_file="res://examples/mr_sharpener/assets/audios/on_the_road.mp3" -dest_files=["res://.godot/imported/on_the_road.mp3-e9fdc664706e3b792a7b180e59edfa1b.mp3str"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/examples/mr_sharpener/assets/audios/sound.mp3 b/examples/mr_sharpener/assets/audios/sound.mp3 deleted file mode 100644 index 0355c43b..00000000 Binary files a/examples/mr_sharpener/assets/audios/sound.mp3 and /dev/null differ diff --git a/examples/mr_sharpener/assets/audios/sound.mp3.import b/examples/mr_sharpener/assets/audios/sound.mp3.import deleted file mode 100644 index 6d63cadd..00000000 --- a/examples/mr_sharpener/assets/audios/sound.mp3.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="mp3" -type="AudioStreamMP3" -uid="uid://bo1ayso3a6hhr" -path="res://.godot/imported/sound.mp3-8c44d760b624d3cd980d9aff823b74e9.mp3str" - -[deps] - -source_file="res://examples/mr_sharpener/assets/audios/sound.mp3" -dest_files=["res://.godot/imported/sound.mp3-8c44d760b624d3cd980d9aff823b74e9.mp3str"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/examples/mr_sharpener/ending_01.json b/examples/mr_sharpener/ending_01.json deleted file mode 100644 index 158330eb..00000000 --- a/examples/mr_sharpener/ending_01.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "EditorVersion": "2.3.0", - "RootNodeID": "68357886-641a-43f2-a8b8-c5e39c147b9a", - "ListNodes": [ - { - "$type": "NodeSentence", - "ID": "c2f2a7d7-7c07-4b80-8bb9-5eab6fbc4218", - "NextID": "cc7949b2-7eed-4e82-aff8-1b90eebba1cb", - "Sentence": "After years of working to prevent harassment, hatred of others and drug use, Mr.Sharpener married another sharpener and lived a happy life.", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "VoicelinePath": "", - "EditorPosition": { - "x": 380, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "cc7949b2-7eed-4e82-aff8-1b90eebba1cb", - "NextID": -1, - "Sentence": "But the battle he had undertaken had only just begun.\n\n[i]To be continued...[/i]", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "VoicelinePath": "", - "EditorPosition": { - "x": 820, - "y": 0 - } - }, - { - "$type": "NodeRoot", - "ID": "68357886-641a-43f2-a8b8-c5e39c147b9a", - "NextID": "c2f2a7d7-7c07-4b80-8bb9-5eab6fbc4218", - "EditorPosition": { - "x": 0, - "y": 0 - } - } - ], - "Characters": [ - { - "Reference": "_NARRATOR", - "ID": 0 - } - ], - "Variables": [] -} \ No newline at end of file diff --git a/examples/mr_sharpener/ending_02.json b/examples/mr_sharpener/ending_02.json deleted file mode 100644 index e7d9d407..00000000 --- a/examples/mr_sharpener/ending_02.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "EditorVersion": "2.3.0", - "RootNodeID": "d0bf7ac1-5741-4025-8573-159b3ea859f6", - "ListNodes": [ - { - "$type": "NodeRoot", - "ID": "d0bf7ac1-5741-4025-8573-159b3ea859f6", - "NextID": "a7b523e7-8ef6-4bb9-81d9-57a3b5692a80", - "EditorPosition": { - "x": 0, - "y": 0 - } - }, - { - "$type": "NodeCondition", - "ID": "a7b523e7-8ef6-4bb9-81d9-57a3b5692a80", - "IfNextID": "8e78b0cd-361f-4db3-a58c-ebb43a6abb8e", - "ElseNextID": "366000c3-8beb-4f7e-a794-6dc969b5c928", - "Condition": { - "Variable": "isDead", - "Operator": "==", - "Value": true - }, - "EditorPosition": { - "x": 420, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "17ffbe5d-7fd8-4300-a1bb-78a94cc4f1a1", - "NextID": -1, - "Sentence": "[i]To be continued...[/i]", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "VoicelinePath": "", - "EditorPosition": { - "x": 1440, - "y": 20 - } - }, - { - "$type": "NodeSentence", - "ID": "366000c3-8beb-4f7e-a794-6dc969b5c928", - "NextID": "17ffbe5d-7fd8-4300-a1bb-78a94cc4f1a1", - "Sentence": "Once out of prison, Mr. Sharpener created a pencil-hating movement that spread across the continent. Pencil shavings spread across the floor. The world became a theater of violence, and no one saw the end of it. People lived in constant fear and hide from what Mr. Sharpener had created.", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "VoicelinePath": "", - "EditorPosition": { - "x": 880, - "y": 80 - } - }, - { - "$type": "NodeSentence", - "ID": "8e78b0cd-361f-4db3-a58c-ebb43a6abb8e", - "NextID": "17ffbe5d-7fd8-4300-a1bb-78a94cc4f1a1", - "Sentence": "Mr. Sharpener's death created a pencil-hating movement that spread across the continent. Pencil shavings spread across the floor. The world became a theater of violence, and no one saw the end of it. People lived in constant fear and hide from what Mr. Sharpener had created.", - "SpeakerID": 0, - "DisplaySpeakerName": "", - "DisplayVariant": "", - "VoicelinePath": "", - "EditorPosition": { - "x": 880, - "y": -180 - } - } - ], - "Characters": [ - { - "Reference": "_NARRATOR", - "ID": 0 - } - ], - "Variables": [ - { - "Name": "isDead", - "Value": false, - "Type": "Boolean" - } - ] -} \ No newline at end of file diff --git a/examples/mr_sharpener/ending_03.json b/examples/mr_sharpener/ending_03.json deleted file mode 100644 index 5fd15f01..00000000 --- a/examples/mr_sharpener/ending_03.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "EditorVersion": "2.3.0", - "RootNodeID": "a9b3070b-7e19-4697-8c97-d178c050b530", - "ListNodes": [ - { - "$type": "NodeRoot", - "ID": "a9b3070b-7e19-4697-8c97-d178c050b530", - "NextID": -1, - "EditorPosition": { - "x": 0, - "y": 0 - } - } - ], - "Characters": [ - { - "Reference": "_NARRATOR", - "ID": 0 - } - ], - "Variables": [] -} \ No newline at end of file diff --git a/examples/mr_sharpener/intro.json b/examples/mr_sharpener/intro.json deleted file mode 100644 index bcabbfcc..00000000 --- a/examples/mr_sharpener/intro.json +++ /dev/null @@ -1,401 +0,0 @@ -{ - "EditorVersion": "2.3.0", - "RootNodeID": "cbfb0a4b-7517-4411-a447-314d106fe280", - "ListNodes": [ - { - "$type": "NodeSentence", - "ID": "749ea0bf-a997-4c47-9a13-c9221b4b6381", - "NextID": "73d66209-10f3-4e14-82c4-5a42e7de7049", - "Speaker": 0, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "Sadly, Mr.Sharpener sank into the desire to do harm and is now in prison to answer for all the harm he has caused. He could have done with a little help...", - "Voiceline": "", - "EditorPosition": { - "x": 3120, - "y": 390 - } - }, - { - "$type": "NodeRoot", - "ID": "cbfb0a4b-7517-4411-a447-314d106fe280", - "NextID": "57c841c9-0ebf-464c-aa25-24c2f7dfb0a8", - "EditorPosition": { - "x": -420, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "98bc9817-7b7d-4dea-8bb7-626192069677", - "NextID": "15805e75-d3b3-49f5-9d2e-3851e97bdefc", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "[shake rate=20.0 level=5]I... I need to hurt...\nGi-give me a pencil...[shake]", - "Voiceline": "", - "EditorPosition": { - "x": 750, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "3e47d27d-0bd4-40e6-bb9e-d0337e0f91fe", - "NextID": -1, - "Speaker": 0, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "Mr.Sharpener got help and is no longer crazy.", - "Voiceline": "", - "EditorPosition": { - "x": 0, - "y": 600 - } - }, - { - "$type": "NodeSentence", - "ID": "0d3de1b9-35cd-4536-8b7e-a3cbf400c87b", - "NextID": "98bc9817-7b7d-4dea-8bb7-626192069677", - "Speaker": 0, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "You see a sharpener in front of you. He appears to be mad.", - "Voiceline": "", - "EditorPosition": { - "x": 300, - "y": 0 - } - }, - { - "$type": "NodeAudio", - "ID": "57c841c9-0ebf-464c-aa25-24c2f7dfb0a8", - "NextID": "0d3de1b9-35cd-4536-8b7e-a3cbf400c87b", - "Loop": true, - "Volume": -10, - "Pitch": 1.2, - "Audio": "on_the_road.mp3", - "EditorPosition": { - "x": -30, - "y": 0 - } - }, - { - "$type": "NodeComment", - "ID": "04eb395b-9548-4d10-b399-5e1b5337dca0", - "NextID": -1, - "Comment": "You can use BBCode inside NodeSentence", - "EditorPosition": { - "x": 750, - "y": -150 - } - }, - { - "$type": "NodeBridgeOut", - "ID": "4f2d9e0e-4d3c-4bdd-a60e-2d2225604ad6", - "NextID": "15805e75-d3b3-49f5-9d2e-3851e97bdefc", - "NumberSelector": 1, - "EditorPosition": { - "x": 960, - "y": 150 - } - }, - { - "$type": "NodeChoice", - "ID": "15805e75-d3b3-49f5-9d2e-3851e97bdefc", - "NextID": "7e24e282-b823-45c9-8a5c-aec03fd1b64a", - "OptionsID": [ - "db255a89-210b-4092-90fc-5f73578f4210", - "8d501872-3726-4697-bc51-a5e706ed4306" - ], - "EditorPosition": { - "x": 1200, - "y": 0 - } - }, - { - "$type": "NodeOption", - "ID": "db255a89-210b-4092-90fc-5f73578f4210", - "NextID": "7e24e282-b823-45c9-8a5c-aec03fd1b64a", - "Option": "Dude, you need help I think", - "EnableByDefault": true, - "OneShot": true, - "EditorPosition": { - "x": 0, - "y": 0 - } - }, - { - "$type": "NodeOption", - "ID": "8d501872-3726-4697-bc51-a5e706ed4306", - "NextID": "55d5315b-896b-40c3-a59d-c890cebc15a7", - "Option": "I have a pencil...", - "EnableByDefault": true, - "OneShot": false, - "EditorPosition": { - "x": 0, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "7e24e282-b823-45c9-8a5c-aec03fd1b64a", - "NextID": "05f9fa16-04ba-466c-8490-79110a676865", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "You think ?", - "Voiceline": "", - "EditorPosition": { - "x": 1560, - "y": 0 - } - }, - { - "$type": "NodeCondition", - "ID": "55d5315b-896b-40c3-a59d-c890cebc15a7", - "IfNextID": "907c77fb-5887-47da-bed4-a85b6cf50dc5", - "ElseNextID": "889a22da-0ea8-42f7-a8c8-fa26d339fef0", - "Variable": "TookHelp", - "Operator": "==", - "Value": true, - "EditorPosition": { - "x": 1560, - "y": 150 - } - }, - { - "$type": "NodeSentence", - "ID": "889a22da-0ea8-42f7-a8c8-fa26d339fef0", - "NextID": "ce58e64a-62bd-4383-9331-99df9ed625bd", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "Oh, thanks", - "Voiceline": "", - "EditorPosition": { - "x": 1920, - "y": 300 - } - }, - { - "$type": "NodeSentence", - "ID": "907c77fb-5887-47da-bed4-a85b6cf50dc5", - "NextID": "239b62c5-7be3-49fa-8dcf-c19e23a639a6", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "Hey man no, I've got help now. ", - "Voiceline": "", - "EditorPosition": { - "x": 1920, - "y": 150 - } - }, - { - "$type": "NodeSentence", - "ID": "05f9fa16-04ba-466c-8490-79110a676865", - "NextID": "8ac967a5-fa9c-4d72-b149-28296d409e06", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "Thanks for the advice men, I'll take some help !", - "Voiceline": "", - "EditorPosition": { - "x": 2010, - "y": 0 - } - }, - { - "$type": "NodeSetter", - "ID": "8ac967a5-fa9c-4d72-b149-28296d409e06", - "NextID": "b539d88c-4032-4a18-a146-212a25741cd2", - "SetType": "Variable", - "Variable": "TookHelp", - "Operator": "=", - "Value": true, - "EditorPosition": { - "x": 2460, - "y": 0 - } - }, - { - "$type": "NodeComment", - "ID": "d7e8856e-adeb-4e5f-83a3-766381d0f3ea", - "NextID": -1, - "Comment": "Setting TookHelp to true will trigger an event and cause the ConditionNode to return true.", - "EditorPosition": { - "x": 2400, - "y": -150 - } - }, - { - "$type": "NodeAudio", - "ID": "b539d88c-4032-4a18-a146-212a25741cd2", - "NextID": "44244013-8168-4507-af13-7c24f62a75c2", - "Loop": false, - "Volume": 10, - "Pitch": 1, - "Audio": "sound.mp3", - "EditorPosition": { - "x": 2820, - "y": 0 - } - }, - { - "$type": "NodeSentence", - "ID": "239b62c5-7be3-49fa-8dcf-c19e23a639a6", - "NextID": "aca21898-2092-44cc-845f-3a7d804d500b", - "Speaker": 1, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "It's not right to want to hurt people", - "Voiceline": "", - "EditorPosition": { - "x": 2370, - "y": 150 - } - }, - { - "$type": "NodeEndPath", - "ID": "aca21898-2092-44cc-845f-3a7d804d500b", - "NextID": -1, - "NextStory": "ending_01.json", - "EditorPosition": { - "x": 2820, - "y": 150 - } - }, - { - "$type": "NodeRandom", - "ID": "moai3YwB8Z", - "Outputs": [ - { - "ID": 0, - "Weight": 50, - "NextID": "2078d00f-3af9-4d6e-88ac-fb0679ce625a" - }, - { - "ID": 1, - "Weight": 50, - "NextID": "749ea0bf-a997-4c47-9a13-c9221b4b6381" - } - ], - "EditorPosition": { - "x": 2880, - "y": 300 - } - }, - { - "$type": "NodeWait", - "ID": "ce58e64a-62bd-4383-9331-99df9ed625bd", - "NextID": "moai3YwB8Z", - "Time": 1, - "EditorPosition": { - "x": 2640, - "y": 300 - } - }, - { - "$type": "NodeSentence", - "ID": "2078d00f-3af9-4d6e-88ac-fb0679ce625a", - "NextID": "f6a10820-98e9-4f3e-b1fb-60392c14fe64", - "Speaker": 0, - "DisplayName": "", - "DisplayVariant": "", - "Sentence": "When the police ordered Mr. Sharpener to give himself up, he did not hear them and continued to harm those around him. Unfortunately, the police had no choice but to end his life.", - "Voiceline": "", - "EditorPosition": { - "x": 3120, - "y": 180 - } - }, - { - "$type": "NodeComment", - "ID": "44f20380-db65-4442-9036-cfa47a98e2f0", - "NextID": -1, - "Comment": "A random bad ending", - "EditorPosition": { - "x": 3120, - "y": 600 - } - }, - { - "$type": "NodeBridgeIn", - "ID": "44244013-8168-4507-af13-7c24f62a75c2", - "NextID": "4f2d9e0e-4d3c-4bdd-a60e-2d2225604ad6", - "NumberSelector": 1, - "EditorPosition": { - "x": 3090, - "y": 0 - } - }, - { - "$type": "NodeSetter", - "ID": "f6a10820-98e9-4f3e-b1fb-60392c14fe64", - "NextID": "73d66209-10f3-4e14-82c4-5a42e7de7049", - "SetType": "Variable", - "Variable": "isDead", - "Operator": "=", - "Value": true, - "EditorPosition": { - "x": 3570, - "y": 240 - } - }, - { - "$type": "NodeEvent", - "ID": "8db56180-b83d-429b-b6c1-1395b348996c", - "NextID": "3e47d27d-0bd4-40e6-bb9e-d0337e0f91fe", - "Variable": "TookHelp", - "Operator": "==", - "Value": true, - "EditorPosition": { - "x": -390, - "y": 600 - } - }, - { - "$type": "NodeComment", - "ID": "11812fee-1f97-4b71-84b8-6df22dfda46e", - "NextID": -1, - "Comment": "Write a message when the player save Mr.sharpener.", - "EditorPosition": { - "x": -420, - "y": 450 - } - }, - { - "$type": "NodeEndPath", - "ID": "73d66209-10f3-4e14-82c4-5a42e7de7049", - "NextID": -1, - "NextStory": "ending_02.json", - "EditorPosition": { - "x": 3960, - "y": 420 - } - } - ], - "Characters": [ - { - "Reference": "_NARRATOR", - "ID": 0 - }, - { - "Reference": "Mr.Sharpener", - "ID": 1 - } - ], - "Variables": [ - { - "Name": "TookHelp", - "Value": false, - "Type": "Boolean" - }, - { - "Name": "isDead", - "Value": false, - "Type": "Boolean" - } - ] -} \ No newline at end of file diff --git a/export_presets.cfg b/export_presets.cfg index 358e4ecd..6dcdca8b 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -3,15 +3,19 @@ name="Monologue win x64" platform="Windows Desktop" runnable=true -advanced_options=false dedicated_server=false custom_features="" export_filter="exclude" -export_files=PackedStringArray("res://addons/Todo_Manager/doc/images/example1.png", "res://addons/Todo_Manager/doc/images/example2.png", "res://addons/Todo_Manager/doc/images/Instruct1.png", "res://addons/Todo_Manager/doc/images/Instruct2.png", "res://addons/Todo_Manager/doc/images/Instruct3.png", "res://addons/Todo_Manager/doc/images/Instruct4.png", "res://addons/Todo_Manager/doc/images/Instruct5.png", "res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png", "res://addons/Todo_Manager/UI/ColourPicker.tscn", "res://addons/Todo_Manager/UI/Dock.tscn", "res://addons/Todo_Manager/UI/Pattern.tscn", "res://addons/Todo_Manager/ColourPicker.gd", "res://addons/Todo_Manager/Current.gd", "res://addons/Todo_Manager/Dock.gd", "res://addons/Todo_Manager/Pattern.gd", "res://addons/Todo_Manager/plugin.gd", "res://addons/Todo_Manager/Project.gd", "res://addons/Todo_Manager/todoItem_class.gd", "res://addons/Todo_Manager/todo_class.gd", "res://addons/gdUnit4/bin/GdUnitBuildTool.gd", "res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdConsole.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommand.gd", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdFunctionDoubler.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitClassDoubler.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractions.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.tscn", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitScriptType.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/report/template/css/logo.png", "res://addons/gdUnit4/src/report/GdUnitByPathReport.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlReport.gd", "res://addons/gdUnit4/src/report/GdUnitReportSummary.gd", "res://addons/gdUnit4/src/report/GdUnitTestCaseReport.gd", "res://addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd", "res://addons/gdUnit4/src/report/JUnitXmlReport.gd", "res://addons/gdUnit4/src/report/XmlElement.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://addons/script-ide/icon/class.svg", "res://addons/script-ide/icon/constant.svg", "res://addons/script-ide/icon/export.svg", "res://addons/script-ide/icon/func.svg", "res://addons/script-ide/icon/func_get.svg", "res://addons/script-ide/icon/func_set.svg", "res://addons/script-ide/icon/keyword.svg", "res://addons/script-ide/icon/property.svg", "res://addons/script-ide/icon/signal.svg", "res://addons/script-ide/plugin.gd", "res://addons/script-ide/Popup.gd", "res://addons/ColorPreview/ColorHelper.gd", "res://addons/ColorPreview/Picker.tscn", "res://addons/ColorPreview/ColorPreview.gd", "res://unit/empty_test.tscn", "res://unit/test_action_node.gd", "res://unit/test_choice_node.gd", "res://unit/test_graph_edit_switcher.gd", "res://unit/test_graph_node_picker.gd", "res://unit/test_language_switcher.gd", "res://unit/test_monologue_process.gd", "res://unit/test_option_node.gd", "res://unit/test_path.gd", "res://unit/test_recent_file_container.gd", "res://unit/test_sentence_node.gd", "res://examples/mr_sharpener/assets/audios/kalimba.wav", "res://examples/mr_sharpener/assets/audios/mystery_sting.ogg", "res://examples/mr_sharpener/assets/audios/on_the_road.mp3", "res://examples/mr_sharpener/assets/audios/sound.mp3", "res://examples/mr_sharpener/ending_01.json", "res://examples/mr_sharpener/ending_02.json", "res://examples/mr_sharpener/ending_03.json", "res://examples/mr_sharpener/intro.json", "res://examples/actions_and_options.json") +export_files=PackedStringArray("res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://unit/empty_test.tscn") include_filter="" exclude_filter="examples, unit, addons/Todo_Manager, addons/gdUnit4" export_path="./monologue_win_x64.exe" patches=PackedStringArray() +patch_delta_encoding=false +patch_delta_compression_level_zstd=19 +patch_delta_min_reduction=0.1 +patch_delta_include_filters="*" +patch_delta_exclude_filters="" encryption_include_filters="" encryption_exclude_filters="" seed=0 @@ -27,6 +31,7 @@ debug/export_console_wrapper=1 binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false +shader_baker/enabled=false binary_format/architecture="x86_64" codesign/enable=false codesign/timestamp=true @@ -75,15 +80,19 @@ texture_format/etc2=false name="Monologue win arm64" platform="Windows Desktop" runnable=false -advanced_options=false dedicated_server=false custom_features="" export_filter="exclude" -export_files=PackedStringArray("res://addons/ColorPreview/ColorHelper.gd", "res://addons/ColorPreview/ColorPreview.gd", "res://addons/ColorPreview/Picker.tscn", "res://addons/gdUnit4/bin/GdUnitBuildTool.gd", "res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdConsole.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommand.gd", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdFunctionDoubler.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitClassDoubler.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractions.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.tscn", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitScriptType.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/report/template/css/logo.png", "res://addons/gdUnit4/src/report/GdUnitByPathReport.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlReport.gd", "res://addons/gdUnit4/src/report/GdUnitReportSummary.gd", "res://addons/gdUnit4/src/report/GdUnitTestCaseReport.gd", "res://addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd", "res://addons/gdUnit4/src/report/JUnitXmlReport.gd", "res://addons/gdUnit4/src/report/XmlElement.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://addons/script-ide/icon/class.svg", "res://addons/script-ide/icon/constant.svg", "res://addons/script-ide/icon/export.svg", "res://addons/script-ide/icon/func.svg", "res://addons/script-ide/icon/func_get.svg", "res://addons/script-ide/icon/func_set.svg", "res://addons/script-ide/icon/keyword.svg", "res://addons/script-ide/icon/property.svg", "res://addons/script-ide/icon/signal.svg", "res://addons/script-ide/plugin.gd", "res://addons/script-ide/Popup.gd", "res://addons/Todo_Manager/doc/images/example1.png", "res://addons/Todo_Manager/doc/images/example2.png", "res://addons/Todo_Manager/doc/images/Instruct1.png", "res://addons/Todo_Manager/doc/images/Instruct2.png", "res://addons/Todo_Manager/doc/images/Instruct3.png", "res://addons/Todo_Manager/doc/images/Instruct4.png", "res://addons/Todo_Manager/doc/images/Instruct5.png", "res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png", "res://addons/Todo_Manager/UI/ColourPicker.tscn", "res://addons/Todo_Manager/UI/Dock.tscn", "res://addons/Todo_Manager/UI/Pattern.tscn", "res://addons/Todo_Manager/ColourPicker.gd", "res://addons/Todo_Manager/Current.gd", "res://addons/Todo_Manager/Dock.gd", "res://addons/Todo_Manager/Pattern.gd", "res://addons/Todo_Manager/plugin.gd", "res://addons/Todo_Manager/Project.gd", "res://addons/Todo_Manager/todoItem_class.gd", "res://addons/Todo_Manager/todo_class.gd", "res://unit/empty_test.tscn", "res://unit/test_action_node.gd", "res://unit/test_choice_node.gd", "res://unit/test_graph_edit_switcher.gd", "res://unit/test_graph_node_picker.gd", "res://unit/test_language_switcher.gd", "res://unit/test_monologue_process.gd", "res://unit/test_option_node.gd", "res://unit/test_path.gd", "res://unit/test_recent_file_container.gd", "res://unit/test_sentence_node.gd", "res://examples/mr_sharpener/assets/audios/kalimba.wav", "res://examples/mr_sharpener/assets/audios/mystery_sting.ogg", "res://examples/mr_sharpener/assets/audios/on_the_road.mp3", "res://examples/mr_sharpener/assets/audios/sound.mp3", "res://examples/mr_sharpener/ending_01.json", "res://examples/mr_sharpener/ending_02.json", "res://examples/mr_sharpener/ending_03.json", "res://examples/mr_sharpener/intro.json", "res://examples/actions_and_options.json") +export_files=PackedStringArray("res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://unit/empty_test.tscn") include_filter="" exclude_filter="examples, unit, addons/Todo_Manager, addons/gdUnit4" export_path="./monologue_win_arm64.exe" patches=PackedStringArray() +patch_delta_encoding=false +patch_delta_compression_level_zstd=19 +patch_delta_min_reduction=0.1 +patch_delta_include_filters="*" +patch_delta_exclude_filters="" encryption_include_filters="" encryption_exclude_filters="" seed=0 @@ -99,6 +108,7 @@ debug/export_console_wrapper=1 binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false +shader_baker/enabled=false binary_format/architecture="arm64" codesign/enable=false codesign/timestamp=true @@ -143,15 +153,19 @@ Remove-Item -Recurse -Force '{temp_dir}'" name="Monologue Linux/X11 x64" platform="Linux" runnable=true -advanced_options=false dedicated_server=false custom_features="" export_filter="exclude" -export_files=PackedStringArray("res://unit/test_sentence_node.gd", "res://unit/test_recent_file_container.gd", "res://unit/test_path.gd", "res://unit/test_option_node.gd", "res://unit/test_monologue_process.gd", "res://unit/test_graph_node_picker.gd", "res://unit/test_graph_edit_switcher.gd", "res://unit/test_choice_node.gd", "res://unit/test_action_node.gd", "res://addons/ColorPreview/ColorHelper.gd", "res://addons/ColorPreview/ColorPreview.gd", "res://addons/ColorPreview/Picker.tscn", "res://addons/gdUnit4/bin/GdUnitBuildTool.gd", "res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdConsole.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommand.gd", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdFunctionDoubler.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitClassDoubler.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractions.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.tscn", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitScriptType.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/report/template/css/logo.png", "res://addons/gdUnit4/src/report/GdUnitByPathReport.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlReport.gd", "res://addons/gdUnit4/src/report/GdUnitReportSummary.gd", "res://addons/gdUnit4/src/report/GdUnitTestCaseReport.gd", "res://addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd", "res://addons/gdUnit4/src/report/JUnitXmlReport.gd", "res://addons/gdUnit4/src/report/XmlElement.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://addons/script-ide/icon/class.svg", "res://addons/script-ide/icon/constant.svg", "res://addons/script-ide/icon/export.svg", "res://addons/script-ide/icon/func.svg", "res://addons/script-ide/icon/func_get.svg", "res://addons/script-ide/icon/func_set.svg", "res://addons/script-ide/icon/keyword.svg", "res://addons/script-ide/icon/property.svg", "res://addons/script-ide/icon/signal.svg", "res://addons/script-ide/plugin.gd", "res://addons/script-ide/Popup.gd", "res://addons/Todo_Manager/doc/images/example1.png", "res://addons/Todo_Manager/doc/images/example2.png", "res://addons/Todo_Manager/doc/images/Instruct1.png", "res://addons/Todo_Manager/doc/images/Instruct2.png", "res://addons/Todo_Manager/doc/images/Instruct3.png", "res://addons/Todo_Manager/doc/images/Instruct4.png", "res://addons/Todo_Manager/doc/images/Instruct5.png", "res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png", "res://addons/Todo_Manager/UI/ColourPicker.tscn", "res://addons/Todo_Manager/UI/Dock.tscn", "res://addons/Todo_Manager/UI/Pattern.tscn", "res://addons/Todo_Manager/ColourPicker.gd", "res://addons/Todo_Manager/Current.gd", "res://addons/Todo_Manager/Dock.gd", "res://addons/Todo_Manager/Pattern.gd", "res://addons/Todo_Manager/plugin.gd", "res://addons/Todo_Manager/Project.gd", "res://addons/Todo_Manager/todoItem_class.gd", "res://addons/Todo_Manager/todo_class.gd", "res://unit/empty_test.tscn", "res://unit/test_language_switcher.gd", "res://examples/mr_sharpener/assets/audios/kalimba.wav", "res://examples/mr_sharpener/assets/audios/mystery_sting.ogg", "res://examples/mr_sharpener/assets/audios/on_the_road.mp3", "res://examples/mr_sharpener/assets/audios/sound.mp3", "res://examples/mr_sharpener/ending_01.json", "res://examples/mr_sharpener/ending_02.json", "res://examples/mr_sharpener/ending_03.json", "res://examples/mr_sharpener/intro.json", "res://examples/actions_and_options.json") +export_files=PackedStringArray("res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://unit/empty_test.tscn") include_filter="" exclude_filter="examples, unit, addons/Todo_Manager, addons/gdUnit4" export_path="./monologue_linux.x86_64" patches=PackedStringArray() +patch_delta_encoding=false +patch_delta_compression_level_zstd=19 +patch_delta_min_reduction=0.1 +patch_delta_include_filters="*" +patch_delta_exclude_filters="" encryption_include_filters="" encryption_exclude_filters="" seed=0 @@ -167,6 +181,7 @@ debug/export_console_wrapper=1 binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false +shader_baker/enabled=false binary_format/architecture="x86_64" ssh_remote_deploy/enabled=false ssh_remote_deploy/host="user@host_ip" @@ -190,15 +205,19 @@ texture_format/etc2=false name="Monologue Linux/X11 arm64" platform="Linux" runnable=false -advanced_options=false dedicated_server=false custom_features="" export_filter="exclude" -export_files=PackedStringArray("res://addons/script-ide/icon/class.svg", "res://addons/script-ide/icon/constant.svg", "res://addons/script-ide/icon/export.svg", "res://addons/script-ide/icon/func.svg", "res://addons/script-ide/icon/func_get.svg", "res://addons/script-ide/icon/func_set.svg", "res://addons/script-ide/icon/keyword.svg", "res://addons/script-ide/icon/property.svg", "res://addons/script-ide/icon/signal.svg", "res://addons/script-ide/plugin.gd", "res://addons/script-ide/Popup.gd", "res://addons/ColorPreview/ColorHelper.gd", "res://addons/ColorPreview/ColorPreview.gd", "res://addons/ColorPreview/Picker.tscn", "res://addons/Todo_Manager/doc/images/example1.png", "res://addons/Todo_Manager/doc/images/example2.png", "res://addons/Todo_Manager/doc/images/Instruct1.png", "res://addons/Todo_Manager/doc/images/Instruct2.png", "res://addons/Todo_Manager/doc/images/Instruct3.png", "res://addons/Todo_Manager/doc/images/Instruct4.png", "res://addons/Todo_Manager/doc/images/Instruct5.png", "res://addons/Todo_Manager/doc/images/TODO_Manager_Logo.png", "res://addons/Todo_Manager/UI/ColourPicker.tscn", "res://addons/Todo_Manager/UI/Dock.tscn", "res://addons/Todo_Manager/UI/Pattern.tscn", "res://addons/Todo_Manager/ColourPicker.gd", "res://addons/Todo_Manager/Current.gd", "res://addons/Todo_Manager/Dock.gd", "res://addons/Todo_Manager/Pattern.gd", "res://addons/Todo_Manager/plugin.gd", "res://addons/Todo_Manager/Project.gd", "res://addons/Todo_Manager/todoItem_class.gd", "res://addons/Todo_Manager/todo_class.gd", "res://unit/empty_test.tscn", "res://unit/test_action_node.gd", "res://unit/test_choice_node.gd", "res://unit/test_graph_edit_switcher.gd", "res://unit/test_graph_node_picker.gd", "res://unit/test_language_switcher.gd", "res://unit/test_monologue_process.gd", "res://unit/test_option_node.gd", "res://unit/test_path.gd", "res://unit/test_recent_file_container.gd", "res://unit/test_sentence_node.gd", "res://addons/gdUnit4/bin/GdUnitBuildTool.gd", "res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdConsole.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommand.gd", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestAdded.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestRemoved.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverTestSuiteAdded.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterizedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/parameterized/GdUnitTestCaseParameterSetTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdFunctionDoubler.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitClassDoubler.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractions.gd", "res://addons/gdUnit4/src/core/GdUnitObjectInteractionsTemplate.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.gd", "res://addons/gdUnit4/src/core/GdUnitRunner.tscn", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitScriptType.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/mono/GdUnit4CSharpApiLoader.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitResourceDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestCaseDto.gd", "res://addons/gdUnit4/src/network/rpc/dtos/GdUnitTestSuiteDto.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitTestSuite.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/report/template/css/logo.png", "res://addons/gdUnit4/src/report/GdUnitByPathReport.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlPatterns.gd", "res://addons/gdUnit4/src/report/GdUnitHtmlReport.gd", "res://addons/gdUnit4/src/report/GdUnitReportSummary.gd", "res://addons/gdUnit4/src/report/GdUnitTestCaseReport.gd", "res://addons/gdUnit4/src/report/GdUnitTestSuiteReport.gd", "res://addons/gdUnit4/src/report/JUnitXmlReport.gd", "res://addons/gdUnit4/src/report/XmlElement.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Bold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-BoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLight.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ExtraLightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Italic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Light.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-LightItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Medium.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-MediumItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Regular.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBold.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-SemiBoldItalic.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-Thin.ttf", "res://addons/gdUnit4/src/update/assets/fonts/static/RobotoMono-ThinItalic.ttf", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd", "res://examples/mr_sharpener/assets/audios/kalimba.wav", "res://examples/mr_sharpener/assets/audios/mystery_sting.ogg", "res://examples/mr_sharpener/assets/audios/on_the_road.mp3", "res://examples/mr_sharpener/assets/audios/sound.mp3", "res://examples/mr_sharpener/ending_01.json", "res://examples/mr_sharpener/ending_02.json", "res://examples/mr_sharpener/ending_03.json", "res://examples/mr_sharpener/intro.json", "res://examples/actions_and_options.json") +export_files=PackedStringArray("res://unit/empty_test.tscn", "res://addons/gdUnit4/bin/GdUnitCmdTool.gd", "res://addons/gdUnit4/bin/GdUnitCopyLog.gd", "res://addons/gdUnit4/src/asserts/CallBackValueProvider.gd", "res://addons/gdUnit4/src/asserts/DefaultValueProvider.gd", "res://addons/gdUnit4/src/asserts/GdAssertMessages.gd", "res://addons/gdUnit4/src/asserts/GdAssertReports.gd", "res://addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitAssertions.gd", "res://addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "res://addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd", "res://addons/gdUnit4/src/asserts/ValueProvider.gd", "res://addons/gdUnit4/src/cmd/CmdArgumentParser.gd", "res://addons/gdUnit4/src/cmd/CmdCommand.gd", "res://addons/gdUnit4/src/cmd/CmdCommandHandler.gd", "res://addons/gdUnit4/src/cmd/CmdOption.gd", "res://addons/gdUnit4/src/cmd/CmdOptions.gd", "res://addons/gdUnit4/src/core/assets/touch-button.png", "res://addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd", "res://addons/gdUnit4/src/core/command/GdUnitShortcut.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd", "res://addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd", "res://addons/gdUnit4/src/core/event/GdUnitEvent.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventInit.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventStop.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd", "res://addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/fuzzed/GdUnitTestCaseFuzzedTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/single/GdUnitTestCaseSingleTestStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd", "res://addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd", "res://addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd", "res://addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd", "res://addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd", "res://addons/gdUnit4/src/core/parse/GdClassDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionArgument.gd", "res://addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd", "res://addons/gdUnit4/src/core/parse/GdScriptParser.gd", "res://addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd", "res://addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd", "res://addons/gdUnit4/src/core/report/GdUnitReport.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteDefaultTemplate.gd", "res://addons/gdUnit4/src/core/templates/test_suite/GdUnitTestSuiteTemplate.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd", "res://addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd", "res://addons/gdUnit4/src/core/_TestCase.gd", "res://addons/gdUnit4/src/core/GdArrayTools.gd", "res://addons/gdUnit4/src/core/GdDiffTool.gd", "res://addons/gdUnit4/src/core/GdObjects.gd", "res://addons/gdUnit4/src/core/GdUnit4Version.gd", "res://addons/gdUnit4/src/core/GdUnitFileAccess.gd", "res://addons/gdUnit4/src/core/GdUnitProperty.gd", "res://addons/gdUnit4/src/core/GdUnitResult.gd", "res://addons/gdUnit4/src/core/GdUnitRunnerConfig.gd", "res://addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd", "res://addons/gdUnit4/src/core/GdUnitSettings.gd", "res://addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd", "res://addons/gdUnit4/src/core/GdUnitSignalCollector.gd", "res://addons/gdUnit4/src/core/GdUnitSignals.gd", "res://addons/gdUnit4/src/core/GdUnitSingleton.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd", "res://addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd", "res://addons/gdUnit4/src/core/GdUnitTools.gd", "res://addons/gdUnit4/src/core/GodotVersionFixures.gd", "res://addons/gdUnit4/src/core/LocalTime.gd", "res://addons/gdUnit4/src/doubler/CallableDoubler.gd", "res://addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd", "res://addons/gdUnit4/src/fuzzers/FloatFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/IntFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/StringFuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd", "res://addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd", "res://addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd", "res://addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd", "res://addons/gdUnit4/src/mocking/GdUnitMock.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd", "res://addons/gdUnit4/src/mocking/GdUnitMockImpl.gd", "res://addons/gdUnit4/src/monitor/ErrorLogEntry.gd", "res://addons/gdUnit4/src/monitor/GdUnitMonitor.gd", "res://addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd", "res://addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd", "res://addons/gdUnit4/src/network/rpc/RPC.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientConnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd", "res://addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd", "res://addons/gdUnit4/src/network/rpc/RPCMessage.gd", "res://addons/gdUnit4/src/network/GdUnitServer.gd", "res://addons/gdUnit4/src/network/GdUnitServer.tscn", "res://addons/gdUnit4/src/network/GdUnitServerConstants.gd", "res://addons/gdUnit4/src/network/GdUnitTask.gd", "res://addons/gdUnit4/src/network/GdUnitTcpClient.gd", "res://addons/gdUnit4/src/network/GdUnitTcpServer.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd", "res://addons/gdUnit4/src/spy/GdUnitSpyImpl.gd", "res://addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.gd", "res://addons/gdUnit4/src/ui/parts/InspectorMonitor.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorProgressBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.gd", "res://addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn", "res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd", "res://addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitInputCapture.tscn", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd", "res://addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn", "res://addons/gdUnit4/src/ui/settings/logo.png", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd", "res://addons/gdUnit4/src/ui/templates/TestSuiteTemplate.tscn", "res://addons/gdUnit4/src/ui/GdUnitConsole.gd", "res://addons/gdUnit4/src/ui/GdUnitConsole.tscn", "res://addons/gdUnit4/src/ui/GdUnitFonts.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.gd", "res://addons/gdUnit4/src/ui/GdUnitInspector.tscn", "res://addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd", "res://addons/gdUnit4/src/ui/GdUnitUiTools.gd", "res://addons/gdUnit4/src/ui/ScriptEditorControls.gd", "res://addons/gdUnit4/src/update/assets/border_bottom.png", "res://addons/gdUnit4/src/update/assets/border_top.png", "res://addons/gdUnit4/src/update/assets/dot1.png", "res://addons/gdUnit4/src/update/assets/dot2.png", "res://addons/gdUnit4/src/update/assets/embedded.png", "res://addons/gdUnit4/src/update/assets/horizontal-line2.png", "res://addons/gdUnit4/src/update/GdMarkDownReader.gd", "res://addons/gdUnit4/src/update/GdUnitPatch.gd", "res://addons/gdUnit4/src/update/GdUnitPatcher.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.gd", "res://addons/gdUnit4/src/update/GdUnitUpdate.tscn", "res://addons/gdUnit4/src/update/GdUnitUpdateClient.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.gd", "res://addons/gdUnit4/src/update/GdUnitUpdateNotify.tscn", "res://addons/gdUnit4/src/Comparator.gd", "res://addons/gdUnit4/src/Fuzzers.gd", "res://addons/gdUnit4/src/GdUnitArrayAssert.gd", "res://addons/gdUnit4/src/GdUnitAssert.gd", "res://addons/gdUnit4/src/GdUnitAwaiter.gd", "res://addons/gdUnit4/src/GdUnitBoolAssert.gd", "res://addons/gdUnit4/src/GdUnitConstants.gd", "res://addons/gdUnit4/src/GdUnitDictionaryAssert.gd", "res://addons/gdUnit4/src/GdUnitFailureAssert.gd", "res://addons/gdUnit4/src/GdUnitFileAssert.gd", "res://addons/gdUnit4/src/GdUnitFloatAssert.gd", "res://addons/gdUnit4/src/GdUnitFuncAssert.gd", "res://addons/gdUnit4/src/GdUnitGodotErrorAssert.gd", "res://addons/gdUnit4/src/GdUnitIntAssert.gd", "res://addons/gdUnit4/src/GdUnitObjectAssert.gd", "res://addons/gdUnit4/src/GdUnitResultAssert.gd", "res://addons/gdUnit4/src/GdUnitSceneRunner.gd", "res://addons/gdUnit4/src/GdUnitSignalAssert.gd", "res://addons/gdUnit4/src/GdUnitStringAssert.gd", "res://addons/gdUnit4/src/GdUnitTestSuite.gd", "res://addons/gdUnit4/src/GdUnitTuple.gd", "res://addons/gdUnit4/src/GdUnitValueExtractor.gd", "res://addons/gdUnit4/src/GdUnitVectorAssert.gd", "res://addons/gdUnit4/plugin.gd") include_filter="" exclude_filter="examples, unit, addons/Todo_Manager, addons/gdUnit4" export_path="./monologue_linux.arm64" patches=PackedStringArray() +patch_delta_encoding=false +patch_delta_compression_level_zstd=19 +patch_delta_min_reduction=0.1 +patch_delta_include_filters="*" +patch_delta_exclude_filters="" encryption_include_filters="" encryption_exclude_filters="" seed=0 @@ -214,6 +233,7 @@ debug/export_console_wrapper=1 binary_format/embed_pck=true texture_format/s3tc_bptc=true texture_format/etc2_astc=false +shader_baker/enabled=false binary_format/architecture="arm64" ssh_remote_deploy/enabled=false ssh_remote_deploy/host="user@host_ip" diff --git a/icon.png b/icon.png index d4d10e60..0545feba 100644 Binary files a/icon.png and b/icon.png differ diff --git a/icon.png.import b/icon.png.import index 72ba513e..d2ead189 100644 --- a/icon.png.import +++ b/icon.png.import @@ -2,7 +2,7 @@ importer="texture" type="CompressedTexture2D" -uid="uid://c1tt7cwyc3s5u" +uid="uid://bou7qelcr5dwn" path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" metadata={ "vram_texture": false @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/icon_min.png b/icon_min.png deleted file mode 100644 index 39ce4300..00000000 Binary files a/icon_min.png and /dev/null differ diff --git a/icon_min.png.import b/icon_min.png.import deleted file mode 100644 index 105d800f..00000000 --- a/icon_min.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cfc0nhupnhixg" -path="res://.godot/imported/icon_min.png-d31382ea5491ba6cb1c00baeef6393a0.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon_min.png" -dest_files=["res://.godot/imported/icon_min.png-d31382ea5491ba6cb1c00baeef6393a0.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/logic/history/add_language_history.gd b/logic/history/add_language_history.gd deleted file mode 100644 index 532580cf..00000000 --- a/logic/history/add_language_history.gd +++ /dev/null @@ -1,38 +0,0 @@ -class_name AddLanguageHistory extends MonologueHistory - -## Graph edit which owns the languages. -var graph_edit: MonologueGraphEdit -## Node name of the language option that is being added. -var node_name: String -## Name of the language to be added. -var language_name: String - - -func _init(graph: MonologueGraphEdit, locale: String = "") -> void: - graph_edit = graph - language_name = locale - - _undo_callback = delete_language_option - _redo_callback = add_language_option - - -func add_language_option() -> void: - var option = GlobalVariables.language_switcher.add_language(language_name) - if node_name: - option.name = node_name - else: - node_name = option.name - graph_edit.languages = GlobalVariables.language_switcher.get_languages().keys() - GlobalSignal.emit("show_languages") - - -func delete_language_option() -> void: - var node = GlobalVariables.language_switcher.get_by_node_name(node_name) - GlobalVariables.language_switcher.remove_language(node) - graph_edit.languages.erase(language_name) - GlobalSignal.emit("show_languages") - - # update selection if selected_index exceeds language count - var languages = GlobalVariables.language_switcher.get_languages().keys() - if GlobalVariables.language_switcher.selected_index >= languages.size(): - GlobalVariables.language_switcher.select_by_locale(languages.back()) diff --git a/logic/history/add_language_history.gd.uid b/logic/history/add_language_history.gd.uid deleted file mode 100644 index 572d8cf1..00000000 --- a/logic/history/add_language_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4633r81npf2c diff --git a/logic/history/add_node_history.gd b/logic/history/add_node_history.gd deleted file mode 100644 index 56448372..00000000 --- a/logic/history/add_node_history.gd +++ /dev/null @@ -1,120 +0,0 @@ -## A special type of action history for handing creation and deletion -## of graph nodes. -class_name AddNodeHistory -extends MonologueHistory - -## Reference to the graph edit node that this action should apply to. -var graph_edit: MonologueGraphEdit - -## List of node references to be deleted on undo. -var deletion_nodes: Array[MonologueGraphNode] -## Dictionary of node name keys to node data dicts to be restored on redo. -var restoration_data: Dictionary -## Dictionary of inbound_connections to be restored on redo. -var inbound_connections: Dictionary -## Dictionary of out_connections to be restored on redo. -var outbound_connections: Dictionary - -## The disconnected node name when this new node was created from picker. -var picker_from_node: String -## The disconnected port when this new node was created from picker. -var picker_from_port: int -## The node names that picker_from_node was connected to on to_port 0. -var picker_to_names: PackedStringArray - - -func _init(graph: MonologueGraphEdit, nodes: Array[MonologueGraphNode]): - graph_edit = graph - deletion_nodes = nodes - - # store node data (JSON values, connections, and options) - for node in nodes: - _record_connections(node) - restoration_data[node.name] = node._to_dict() - if "options" in node: - restoration_data[node.name]["Options"] = node.options.value - - _undo_callback = _delete_callback_for_tracked_nodes - _redo_callback = func() -> Array[MonologueGraphNode]: - # the first key is the node type to re-add, it will handle - # auxilliary creations (e.g. BridgeInNode) - var node_name = restoration_data.keys().front() - var node_type = restoration_data[node_name].get("$type") - return graph_edit.add_node(node_type.trim_prefix("Node"), false) - - -func redo(): - _revert_picker(false) - - # track readded nodes and repopulate their data - deletion_nodes = super.redo() - # iterating this way, it will go through proper order in restoration_data - for i in range(deletion_nodes.size()): - var node_name = restoration_data.keys()[i] - var node_data = restoration_data[node_name] - - # restore node name - deletion_nodes[i].name = node_name - - # restore node data _from_dict() - deletion_nodes[i]._from_dict(node_data) - - # for ChoiceNode, _from_dict() clears options and loads from JSON save - # but it's okay, we can restore the changes in options from here - var options = node_data.get("Options") - if options: - deletion_nodes[i].options.value = options - deletion_nodes[i]._refresh(options) - - # restore graph node connections - _restore_connections(node_name) - - return deletion_nodes - - -## This represents the undo callback after adding a node. It is defined this -## way so that it reads [member deletion_nodes] which will be updated on -## every redo, instead of bound arguments in the callback. -func _delete_callback_for_tracked_nodes(): - for i in deletion_nodes.size(): - var node = deletion_nodes[i] - # restore node reference if it was broken by some other action - # this can be caused by many undo/redo re-creating the node, thus our - # reference here becomes outdated, but we can get it back by searching - # the graph for its node name - if not node: - node = graph_edit.get_node(restoration_data.keys()[i]) - _record_connections(node) # record connections first before freeing!! - restoration_data[node.name] = graph_edit.free_graphnode(node) - - _revert_picker() - return deletion_nodes - - -## Quick method to record all inbound and outbound connections of a given node. -func _record_connections(node: MonologueGraphNode): - var name = node.name - inbound_connections[name] = graph_edit.get_all_inbound_connections(name) - outbound_connections[name] = graph_edit.get_all_outbound_connections(name) - - -## Restore graph connections to a given node name. -func _restore_connections(node_name: String): - var inbound_links = inbound_connections.get(node_name) - var outbound_links = outbound_connections.get(node_name) - - var connections = inbound_links + outbound_links - for co in connections: - graph_edit.propagate_connection( - co.get("from_node"), co.get("from_port"), co.get("to_node"), co.get("to_port") - ) - - -## Reverts the severed connection from when this node was created by picker. -func _revert_picker(reconnect: bool = true): - if picker_from_node: - for to_name in picker_to_names: - if reconnect: - graph_edit.propagate_connection(picker_from_node, picker_from_port, to_name, 0) - else: - graph_edit.disconnect_node(picker_from_node, picker_from_port, to_name, 0) diff --git a/logic/history/add_node_history.gd.uid b/logic/history/add_node_history.gd.uid deleted file mode 100644 index 91c9d439..00000000 --- a/logic/history/add_node_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bvt17thyklv diff --git a/logic/history/character_history.gd b/logic/history/character_history.gd deleted file mode 100644 index e35bf3d6..00000000 --- a/logic/history/character_history.gd +++ /dev/null @@ -1,45 +0,0 @@ -class_name CharacterHistory extends PropertyHistory - -## Character index. -var character_index: int = -1 - - -func _init( - index: int, graph: MonologueGraphEdit, path: NodePath, change_list: Array[PropertyChange] -) -> void: - super(graph, path, change_list) - character_index = index - - -func change_properties() -> void: - super.change_properties() - _display_character() - - -func revert_properties() -> void: - super.revert_properties() - _display_character() - - -func set_property(node: Variant, property: String, value: Variant) -> void: - super.set_property(node, property, value) - _update_character(property, value) - - -func _display_character() -> void: - var node = graph_edit.get_node(node_path) - var property = node[changes[0].property] - if property.field and property.field.is_visible_in_tree(): - GlobalSignal.emit("reload_character_edit", [character_index]) - else: - graph_edit.set_selected(graph_edit.get_root_node()) - GlobalSignal.emit("open_character_edit", [graph_edit, character_index]) - - -func _hide_unrelated_windows() -> void: - pass # intentionally overriden to do nothing, do not remove! - - -func _update_character(property: String, value: Variant) -> void: - var key = Util.to_key_name(property) - graph_edit.characters[character_index]["Character"][key] = value diff --git a/logic/history/character_history.gd.uid b/logic/history/character_history.gd.uid deleted file mode 100644 index 518351fe..00000000 --- a/logic/history/character_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://vcrp48ewp4g0 diff --git a/logic/history/delete_language_history.gd b/logic/history/delete_language_history.gd deleted file mode 100644 index 106706d9..00000000 --- a/logic/history/delete_language_history.gd +++ /dev/null @@ -1,29 +0,0 @@ -class_name DeleteLanguageHistory extends AddLanguageHistory - -var restoration: Dictionary = {} -var choices: Dictionary = {} - - -func _init(graph: MonologueGraphEdit, locale: String, path: String) -> void: - super(graph, locale) - node_name = path - - -func undo(): - var redo_result = super.redo() - for localizable in restoration: - if is_instance_valid(localizable): - localizable.raw_data = restoration.get(localizable).duplicate(true) - - for choice in choices: - choice.restore_options(choices.get(choice)) - - return redo_result - - -func redo(): - for localizable in restoration: - var language_node = GlobalVariables.language_switcher.get_by_node_name(node_name) - if is_instance_valid(localizable) and language_node.language_name == language_name: - localizable.raw_data.erase(node_name) - return super.undo() diff --git a/logic/history/delete_language_history.gd.uid b/logic/history/delete_language_history.gd.uid deleted file mode 100644 index 19807d4f..00000000 --- a/logic/history/delete_language_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://frro2optaw0w diff --git a/logic/history/delete_node_history.gd b/logic/history/delete_node_history.gd deleted file mode 100644 index 2c5f8058..00000000 --- a/logic/history/delete_node_history.gd +++ /dev/null @@ -1,28 +0,0 @@ -## Like AddNodeHistory but doesn't use [method MonologueGraphEdit.add_node] -## to restore nodes. It uses the graph edit's add_child() method directly. -## It also reverses the undo() and redo() functions. -class_name DeleteNodeHistory -extends AddNodeHistory - - -func _init(graph: MonologueGraphEdit, nodes: Array[MonologueGraphNode]): - super(graph, nodes) - # difference in this history is that the restoration directly calls - # graph edit's add_child() for each individual node - _redo_callback = func() -> Array[MonologueGraphNode]: - var created_nodes: Array[MonologueGraphNode] = [] - for name in restoration_data.keys(): - var type = restoration_data[name].get("$type").trim_prefix("Node") - var node = Constants.NODE_SCENES.get(type) - var inst = node.instantiate() - graph_edit.add_child(inst) - created_nodes.append(inst) - return created_nodes - - -func undo(): - return super.redo() - - -func redo(): - return super.undo() diff --git a/logic/history/delete_node_history.gd.uid b/logic/history/delete_node_history.gd.uid deleted file mode 100644 index 6f9fd742..00000000 --- a/logic/history/delete_node_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dtrnecgdgmf3g diff --git a/logic/history/history_handle.gd b/logic/history/history_handle.gd deleted file mode 100644 index b630e3b2..00000000 --- a/logic/history/history_handle.gd +++ /dev/null @@ -1,21 +0,0 @@ -## Extends Godot's [UndoRedo] class with ability to record more complicated -## Monologue interactions via custom [MonologueHistory]. -## -## To start using, call [method UndoRedo.create_action] first, then call -## [method add_prepared_history] for prepared Monologue interactions. -## Finally, call [method UndoRedo.commit_action] to execute into history. -## -## It can be tedious to always call the various built-in methods like -## [method UndoRedo.add_do_method] because of many moving parts in the -## Monologue architecture. That's why there are custom [MonologueHistory] -## classes to separate bigger operations/data into its own section. -class_name HistoryHandler extends UndoRedo - - -## Extracts the undo/redo callback methods from the given [param history] -## and registers them to the currently created action. -## Updates to related properties and references are the concern of -## [param history], we don't need to care about that here. -func add_prepared_history(history: MonologueHistory): - add_do_method(history.redo) - add_undo_method(history.undo) diff --git a/logic/history/history_handle.gd.uid b/logic/history/history_handle.gd.uid deleted file mode 100644 index 34b19deb..00000000 --- a/logic/history/history_handle.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ckmhfstvemjtv diff --git a/logic/history/language_history.gd b/logic/history/language_history.gd deleted file mode 100644 index 0400700c..00000000 --- a/logic/history/language_history.gd +++ /dev/null @@ -1,35 +0,0 @@ -class_name LanguageHistory extends MonologueHistory - -## Graph edit which owns the languages. -var graph_edit: MonologueGraphEdit -## Dictionary of old node names to language names. -var old_references: Dictionary -## Dictionary of new node names to language names. -var new_references: Dictionary - - -func _init(graph: MonologueGraphEdit, old: Dictionary, new: Dictionary) -> void: - graph_edit = graph - # old and new params are dictionaries of language names to object reference - old_references = _convert_to_node_reference(old) - new_references = _convert_to_node_reference(new) - - _undo_callback = revert_language_list - _redo_callback = change_language_list - - -func change_language_list() -> void: - graph_edit.languages = new_references.values() - pass - - -func revert_language_list() -> void: - graph_edit.languages = old_references.values() - pass - - -func _convert_to_node_reference(language_data: Dictionary) -> Dictionary: - var new_dict = {} - for language_name in language_data: - new_dict[language_data.get(language_name).name] = language_name - return new_dict diff --git a/logic/history/language_history.gd.uid b/logic/history/language_history.gd.uid deleted file mode 100644 index 3bf64d1e..00000000 --- a/logic/history/language_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://byqqkvkeo00wr diff --git a/logic/history/modify_language_history.gd b/logic/history/modify_language_history.gd deleted file mode 100644 index 97cfe4fd..00000000 --- a/logic/history/modify_language_history.gd +++ /dev/null @@ -1,36 +0,0 @@ -class_name ModifyLanguageHistory extends MonologueHistory - -## Graph edit which owns the languages. -var graph_edit: MonologueGraphEdit -## Node path to the language option that is being modified. -var node_name: String -## Previous language name. -var before_name: String -## Name of the language to be modified to. -var after_name: String - - -func _init(graph: MonologueGraphEdit, path: String, old_name: String, new_name: String) -> void: - graph_edit = graph - node_name = path - before_name = old_name - after_name = new_name - - _undo_callback = revert - _redo_callback = change - - -func change() -> void: - _set_name(after_name) - - -func revert() -> void: - _set_name(before_name) - - -func _set_name(locale: String) -> void: - var option = GlobalVariables.language_switcher.get_by_node_name(node_name) - var index = option.get_index() - option.set_language_name(locale) - graph_edit.languages[index] = locale - GlobalSignal.emit("show_languages") diff --git a/logic/history/modify_language_history.gd.uid b/logic/history/modify_language_history.gd.uid deleted file mode 100644 index af0a6ed2..00000000 --- a/logic/history/modify_language_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bp3kl35ad0c6w diff --git a/logic/history/monologue_history.gd b/logic/history/monologue_history.gd deleted file mode 100644 index 2ee4b7e4..00000000 --- a/logic/history/monologue_history.gd +++ /dev/null @@ -1,26 +0,0 @@ -## A reference object that stores callbacks for undo and redo as a pair. -## This streamlines some operations and provide more control over node data. -class_name MonologueHistory -extends RefCounted - -## Function to call on undo. Arguments should be bound beforehand. -var _undo_callback: Callable -## Function to call on redo. Arguments should be bound beforehand. -var _redo_callback: Callable - - -## Must have undo and redo [Callable] that has bound arguments. -## Use [method Callable.bind()] to create them. -func _init(undo_function: Callable, redo_function: Callable): - _undo_callback = undo_function - _redo_callback = redo_function - - -## General interface method for undo-ing. -func undo() -> Variant: - return _undo_callback.call() - - -## General interface method for redo-ing. -func redo() -> Variant: - return _redo_callback.call() diff --git a/logic/history/monologue_history.gd.uid b/logic/history/monologue_history.gd.uid deleted file mode 100644 index 2fc94c55..00000000 --- a/logic/history/monologue_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ce24opbg7cm2v diff --git a/logic/history/portrait_history.gd b/logic/history/portrait_history.gd deleted file mode 100644 index 65203ae7..00000000 --- a/logic/history/portrait_history.gd +++ /dev/null @@ -1,40 +0,0 @@ -class_name PortraitHistory extends CharacterHistory - -## Portrait index. -var portrait_index: int = -1 - - -func _init( - _character_index: int, - _portrait_index: int, - graph: MonologueGraphEdit, - path: NodePath, - change_list: Array[PropertyChange] -) -> void: - super(_character_index, graph, path, change_list) - portrait_index = _portrait_index - - -func change_properties() -> void: - super.change_properties() - _display_portrait() - - -func revert_properties() -> void: - super.revert_properties() - _display_portrait() - - -func set_property(node: Variant, property: String, value: Variant) -> void: - super.set_property(node, property, value) - var character_dict = graph_edit.characters[character_index]["Character"] - var key = Util.to_key_name(property) - character_dict["Portraits"][portrait_index]["Portrait"][key] = value - - -func _display_portrait() -> void: - GlobalSignal.emit("select_portrait_option", [portrait_index]) - - -func _update_character(_property: String, _value: Variant) -> void: - pass # intentionally overriden to do nothing, do not remove! diff --git a/logic/history/portrait_history.gd.uid b/logic/history/portrait_history.gd.uid deleted file mode 100644 index fce2b98f..00000000 --- a/logic/history/portrait_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ds8542ygwfqp2 diff --git a/logic/history/property_change.gd b/logic/history/property_change.gd deleted file mode 100644 index b6aad879..00000000 --- a/logic/history/property_change.gd +++ /dev/null @@ -1,16 +0,0 @@ -## Structures a before/after change in a node's property. -class_name PropertyChange -extends RefCounted - -## Name of property/variable to change. -var property: String -## Value before change. -var before: Variant -## Value after change. -var after: Variant - - -func _init(property_name, old, new): - property = property_name - before = old - after = new diff --git a/logic/history/property_change.gd.uid b/logic/history/property_change.gd.uid deleted file mode 100644 index 69faa8e7..00000000 --- a/logic/history/property_change.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://h4jikvg536tu diff --git a/logic/history/property_history.gd b/logic/history/property_history.gd deleted file mode 100644 index 55e0d293..00000000 --- a/logic/history/property_history.gd +++ /dev/null @@ -1,70 +0,0 @@ -class_name PropertyHistory extends MonologueHistory - -## Graph that owns the node whose properties have changed. -var graph_edit: MonologueGraphEdit -## Name of the graph node in the [member graph_edit]. -var node_path: NodePath -## List of property changes to make on [member node_name]. -var changes: Array[PropertyChange] -## Selected locale in the language switcher when this change was made. -var locale: String = LanguageSwitcher.DEFAULT_LOCALE - - -func _init(graph: MonologueGraphEdit, path: NodePath, change_list: Array[PropertyChange]) -> void: - graph_edit = graph - node_path = path - changes = change_list - - _undo_callback = revert_properties - _redo_callback = change_properties - - if GlobalVariables.language_switcher: - locale = str(GlobalVariables.language_switcher.get_current_language()) - - -func change_properties() -> void: - var language := str(GlobalVariables.language_switcher.get_current_language()) - reset_language() - var node = graph_edit.get_node(node_path) - for change in changes: - set_property(node, change.property, change.after) - _hide_unrelated_windows() - - refresh_properties(node, language) - - -func revert_properties() -> void: - var language := str(GlobalVariables.language_switcher.get_current_language()) - reset_language() - var node = graph_edit.get_node(node_path) - for change in changes: - set_property(node, change.property, change.before) - _hide_unrelated_windows() - - refresh_properties(node, language) - - -func reset_language() -> void: - if GlobalVariables.language_switcher: - GlobalVariables.language_switcher.select_by_locale(locale, false) - - -func set_property(node: Variant, property: String, value: Variant) -> void: - node[property].propagate(value) - node[property].value = value - - -func refresh_properties(node: Variant, language: String) -> void: - var properties: PackedStringArray = [] - if node is MonologueGraphNode: - # if language is the same, we can do partial refresh with given properties - # otherwise, full refresh so other controls can reflect the language change - if locale == language: - properties = changes.map(func(c): return c.property) - else: - properties = changes.map(func(c): return c.property) - GlobalSignal.emit.call_deferred("refresh", [node, properties]) - - -func _hide_unrelated_windows() -> void: - GlobalSignal.emit("close_character_edit") diff --git a/logic/history/property_history.gd.uid b/logic/history/property_history.gd.uid deleted file mode 100644 index 4b344658..00000000 --- a/logic/history/property_history.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://pn64mcgcqt1w diff --git a/logic/node_converter.gd b/logic/node_converter.gd deleted file mode 100644 index 4dc4adf7..00000000 --- a/logic/node_converter.gd +++ /dev/null @@ -1,104 +0,0 @@ -## Converts v2.x node data into v3.x node data for loading old projects. -class_name NodeConverter extends RefCounted - - -## Reads raw node data and returns a node instance of the new type. -## Returns the original dictionary if nothing to convert. -func convert_node(node_dict: Dictionary) -> Dictionary: - match node_dict.get("$type"): - "NodeAction": - return convert_action(node_dict) - "NodeDiceRoll": - return convert_dice_roll(node_dict) - return node_dict - - -func convert_action(dict: Dictionary) -> Dictionary: - var action_dict = dict.get("Action") - if action_dict: - var value = action_dict.get("Value") - dict.erase("Action") - - var action_type = action_dict.get("$type") - match action_type: - "ActionOption": - dict["$type"] = "NodeSetter" - dict["SetType"] = "Option" - dict["OptionID"] = action_dict.get("OptionID") - dict["Enable"] = value - "ActionVariable": - dict["$type"] = "NodeSetter" - dict["SetType"] = "Variable" - dict["Variable"] = action_dict.get("Variable") - dict["Operator"] = action_dict.get("Operator") - dict["Value"] = value - "ActionCustom": - match action_dict.get("CustomType"): - "PlayAudio": - dict["$type"] = "NodeAudio" - dict["Loop"] = action_dict.get("Loop") - dict["Volume"] = action_dict.get("Volume") - dict["Pitch"] = action_dict.get("Pitch") - dict["Audio"] = value - "UpdateBackground": - dict["$type"] = "NodeBackground" - dict["Image"] = value - "Other": - dict["Action"] = value - dict["Arguments"] = [] - "ActionTimer": - dict["$type"] = "NodeWait" - dict["Time"] = value - - return dict - - -func convert_dice_roll(dict: Dictionary) -> Dictionary: - var pass_chance = dict.get("Target", 50) - var fail_chance = 100 - pass_chance - - var pass_dict = {"Weight": pass_chance, "NextID": dict.get("PassID", -1)} - var fail_dict = {"Weight": fail_chance, "NextID": dict.get("FailID", -1)} - if pass_chance >= fail_chance: - pass_dict["ID"] = 0 - fail_dict["ID"] = 1 - dict["Outputs"] = [pass_dict, fail_dict] - else: - fail_dict["ID"] = 0 - pass_dict["ID"] = 1 - dict["Outputs"] = [fail_dict, pass_dict] - - dict["$type"] = "NodeRandom" - dict.erase("Skill") - dict.erase("Target") - dict.erase("PassID") - dict.erase("FailID") - return dict - - -func convert_characters(list: Array) -> Array: - var all_ids: Array = [] - - for character in list: - if character.has("Reference"): - continue - - all_ids.append(character.get("ID")) - - for character in list: - if not character.has("Reference"): - continue - var list_idx: int = list.find(character) - - var character_name: String = character.get("Reference", "undefined") - - var new_character = { - "ID": IDGen.generate(5), - "EditorIndex": character.get("ID"), - "Protected": character_name == "_NARRATOR", - "Character": {"Name": character_name} - } - - list[list_idx] = new_character - - return list diff --git a/logic/node_converter.gd.uid b/logic/node_converter.gd.uid deleted file mode 100644 index 745be6aa..00000000 --- a/logic/node_converter.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dt6s8e0qnf7ki diff --git a/logic/shaders/texture_rect_clip.tres b/logic/shaders/texture_rect_clip.tres deleted file mode 100644 index e1bda242..00000000 --- a/logic/shaders/texture_rect_clip.tres +++ /dev/null @@ -1,23 +0,0 @@ -[gd_resource type="Shader" format=3 uid="uid://bsso8dloc4bce"] - -[resource] -code = "shader_type canvas_item; - -uniform float corner_scale: hint_range(0., 1.) = 0.; - -bool isOut(float x, float y) { - return pow(x, 2.) + pow(y, 2.) > pow(corner_scale * .5, 2.); -} - -void fragment() { - float s = corner_scale * .5; - - if ( - (UV.x < s && UV.y < s && isOut(UV.x - s, UV.y - s)) || - (UV.x < s && UV.y > 1. - s && isOut(UV.x - s, UV.y - 1. + s)) || - (UV.x > 1. - s && UV.y < s && isOut(UV.x - 1. + s, UV.y - s)) || - (UV.x > 1. - s && UV.y > 1. - s && isOut(UV.x - 1. + s, UV.y - 1. + s)) - ) { - COLOR.a = 0.; - } -}" diff --git a/nodes/abstract_character/abstract_character.gd b/nodes/abstract_character/abstract_character.gd deleted file mode 100644 index 3f42d28f..00000000 --- a/nodes/abstract_character/abstract_character.gd +++ /dev/null @@ -1,71 +0,0 @@ -## Character data builder. -class_name MonologueCharacter extends RefCounted - -const CHARACTER_FIELD := preload( - "res://common/ui/fields/character_field/monologue_character_field.tscn" -) - -var character := Property.new(CHARACTER_FIELD, {}, {}) -var id := Property.new(MonologueGraphNode.LINE, {}, IDGen.generate()) -var idx := Property.new(MonologueGraphNode.SPINBOX, {}, 0) -var protected := Property.new(MonologueGraphNode.TOGGLE, {}, false) - -var custom_delete_button: - get: - return character.field.delete_button - -var graph: MonologueGraphEdit -var root: RootNode - - -func _init(node: RootNode): - root = node - graph = node.get_parent() - character.connect("change", update_character) - character.connect("display", graph.set_selected.bind(root)) - character.connect("shown", _on_character_field_shown) - character.setters["graph_edit"] = graph - - -func _on_character_field_shown() -> void: - character.field.delete_button.visible = !protected.value - character.field.name_edit.editable = !protected.value - - -func update_character(old_value: Variant, new_value: Variant): - var old_list = root.characters.value.duplicate(true) - var new_list = root.characters.value.duplicate(true) - new_list[idx.value]["Character"] = new_value - - graph.undo_redo.create_action("Character %s => %s" % [str(old_value), str(new_value)]) - graph.undo_redo.add_do_property(root.characters, "value", new_list) - graph.undo_redo.add_do_method(root.characters.propagate.bind(new_list)) - graph.undo_redo.add_do_method(graph.set_selected.bind(root)) - graph.undo_redo.add_do_method(GlobalSignal.emit.bind("close_character_edit")) - graph.undo_redo.add_undo_property(root.characters, "value", old_list) - graph.undo_redo.add_undo_method(root.characters.propagate.bind(old_list)) - graph.undo_redo.add_undo_method(graph.set_selected.bind(root)) - graph.undo_redo.add_undo_method(GlobalSignal.emit.bind("close_character_edit")) - graph.undo_redo.commit_action() - - -func get_property_names() -> PackedStringArray: - return ["character"] - - -func _from_dict(dict: Dictionary) -> void: - if dict.get("ID") is String: - id.value = dict.get("ID") - character.value = dict.get("Character") - protected.value = dict.get("Protected") - idx.value = dict.get("EditorIndex") - character.setters["character_index"] = idx.value - - -func _to_dict(): - return { - "ID": id.value, - "Protected": protected.value, - "Character": character.value, - "EditorIndex": idx.value - } diff --git a/nodes/abstract_character/abstract_character.gd.uid b/nodes/abstract_character/abstract_character.gd.uid deleted file mode 100644 index 3a5330ce..00000000 --- a/nodes/abstract_character/abstract_character.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bwg2j62ue4e3v diff --git a/nodes/abstract_character/asbtract_character.gd.uid b/nodes/abstract_character/asbtract_character.gd.uid deleted file mode 100644 index 31adf8f8..00000000 --- a/nodes/abstract_character/asbtract_character.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhg4nsmhomi15 diff --git a/nodes/abstract_variable/abstract_variable.gd b/nodes/abstract_variable/abstract_variable.gd deleted file mode 100644 index 90142122..00000000 --- a/nodes/abstract_variable/abstract_variable.gd +++ /dev/null @@ -1,119 +0,0 @@ -## Common abstract class for nodes that deal with variables. -class_name AbstractVariableNode extends MonologueGraphNode - -var variable := Property.new(DROPDOWN) -var operator := Property.new(DROPDOWN, {}, "=") -var value := Property.new(LINE) - -var last_boolean: bool -var last_number: float -var last_string: String - - -func _ready(): - variable.connect("preview", get_variable_label().set_text) - operator.callers["set_items"] = [get_operator_options()] - operator.connect("preview", get_operator_label().set_text) - operator.connect("shown", value_morph) - value.connect("preview", record_morph) - super._ready() - - -func get_default_text(new_value: Variant, default: String) -> String: - var is_not_string = new_value is not String - return str(new_value) if is_not_string or new_value != "" else default - - -func get_variable_label() -> Label: - return null - - -## Returns the variable's typestring from the graph edit. -func get_variable_type(variable_name: String) -> String: - for data in get_graph_edit().variables: - if data.get("Name") == variable_name: - return data.get("Type") - return "" - - -func get_operator_label() -> Label: - return null - - -func get_operator_options() -> Array[Dictionary]: - return [ - {"id": 0, "text": "="}, - {"id": 1, "text": "+="}, - {"id": 2, "text": "-="}, - {"id": 3, "text": "*="}, - {"id": 4, "text": "/="}, - ] - - -func get_operator_disabler() -> PackedInt32Array: - return [1, 2, 3, 4] - - -func get_value_label() -> Label: - return null - - -## Reset the variable dropdown to the first value and return its type. -func reset_variable() -> String: - if get_graph_edit().variables: - variable.value = get_graph_edit().variables[0].get("Name") - return get_graph_edit().variables[0].get("Type") - else: - variable.value = "" - return "" - - -func record_morph(new_value: Variant): - match typeof(new_value): - TYPE_BOOL: - last_boolean = new_value - TYPE_INT, TYPE_FLOAT: - last_number = new_value - TYPE_STRING: - last_string = new_value - - # display integer without decimals - match get_variable_type(variable.value): - "Integer": - new_value = int(new_value) if new_value else 0 - get_value_label().text = str(new_value) - - -func value_morph(selected_name: Variant = variable.value) -> void: - var selected_type = get_variable_type(selected_name) - if not selected_type: - selected_type = reset_variable() - - match selected_type: - "Boolean": - operator.invoke("disable_items", [get_operator_disabler()]) - value.morph(TOGGLE) - value.value = last_boolean - value.propagate(last_boolean, false) - get_value_label().text = str(last_boolean) - "Integer": - operator.invoke("disable_items", [[]]) - value.morph(SPINBOX) - value.value = int(last_number) - value.propagate(int(last_number), false) - get_value_label().text = str(int(last_number)) - "String": - operator.invoke("disable_items", [get_operator_disabler()]) - value.morph(LINE) - value.value = last_string - value.propagate(last_string, false) - get_value_label().text = get_default_text(last_string, "value") - - -func _update() -> void: - variable.callers["set_items"] = [get_graph_edit().variables, "Name", "ID", "Type"] - value_morph(variable.value) - get_variable_label().text = get_default_text(variable.value, "variable") - get_operator_label().text = get_default_text(operator.value, "operator") - get_value_label().text = get_default_text(value.value, "value") - super._update() diff --git a/nodes/abstract_variable/abstract_variable.gd.uid b/nodes/abstract_variable/abstract_variable.gd.uid deleted file mode 100644 index aa967836..00000000 --- a/nodes/abstract_variable/abstract_variable.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bh8bqa4yv070b diff --git a/nodes/action_node/action_node.gd b/nodes/action_node/action_node.gd deleted file mode 100644 index af062956..00000000 --- a/nodes/action_node/action_node.gd +++ /dev/null @@ -1,68 +0,0 @@ -@icon("res://ui/assets/icons/action.svg") -class_name ActionNode extends MonologueGraphNode - -@export var arg_box: VBoxContainer -@export var action_label: Label -@export var no_args_label: Label - -var action := Property.new(LINE) -var arguments := Property.new(LIST, {}, []) -var _argument_references = [] - - -func _ready(): - node_type = "NodeAction" - super._ready() - - action.connect("preview", _set_action_text) - arguments.setters["add_callback"] = add_argument - arguments.setters["get_callback"] = get_arguments - arguments.connect("preview", load_arguments) - - -func add_argument(data: Dictionary = {}) -> MonologueArgument: - var argument = MonologueArgument.new(self) - if data: - argument._from_dict(data) - argument.index = _argument_references.size() - _argument_references.append(argument) - return argument - - -func get_arguments(): - return _argument_references - - -func load_arguments(new_argument_list: Array): - _argument_references.clear() - for argument in new_argument_list: - add_argument(argument) - arguments.value = new_argument_list - _update.call_deferred() - - -func _from_dict(dict: Dictionary) -> void: - for key in dict.keys(): - var property = get(key.to_snake_case()) - if property is Property: - property.value = dict.get(key) - - _load_position(dict) - load_arguments(arguments.value) - - -func _set_action_text(new_text: String = action.value) -> void: - action_label.text = new_text if new_text else "custom action" - - -func _update() -> void: - _set_action_text() - no_args_label.visible = _argument_references.is_empty() - for child in arg_box.get_children(): - arg_box.remove_child(child) - child.queue_free() - - for i in range(_argument_references.size()): - arguments.value[i]["Value"] = _argument_references[i].value.value - _argument_references[i].create_representation(arg_box) - super._update() diff --git a/nodes/action_node/action_node.gd.uid b/nodes/action_node/action_node.gd.uid deleted file mode 100644 index 052632f4..00000000 --- a/nodes/action_node/action_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cq8g0jelhsidq diff --git a/nodes/action_node/action_node.tscn b/nodes/action_node/action_node.tscn deleted file mode 100644 index 0db0ee52..00000000 --- a/nodes/action_node/action_node.tscn +++ /dev/null @@ -1,62 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dg1if067aop0p"] - -[ext_resource type="Script" uid="uid://cq8g0jelhsidq" path="res://nodes/action_node/action_node.gd" id="3_s1gpj"] - -[node name="ActionNode" type="GraphNode" node_paths=PackedStringArray("arg_box", "action_label", "no_args_label") groups=["graph_nodes"]] -custom_minimum_size = Vector2(360, 0) -offset_right = 343.0 -offset_bottom = 87.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -title = "ActionNode" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -slot/1/left_enabled = false -slot/1/left_type = 0 -slot/1/left_color = Color(1, 1, 1, 1) -slot/1/left_icon = null -slot/1/right_enabled = false -slot/1/right_type = 0 -slot/1/right_color = Color(1, 1, 1, 1) -slot/1/right_icon = null -slot/1/draw_stylebox = true -script = ExtResource("3_s1gpj") -arg_box = NodePath("ArgBox") -action_label = NodePath("CallBox/ActionLabel") -no_args_label = NodePath("CallBox/NoArgsLabel") -titlebar_color = Color(0.733333, 0.392157, 0.188235, 1) - -[node name="CallBox" type="HBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="CallLabel" type="Label" parent="CallBox"] -layout_mode = 2 -text = "Call" - -[node name="ActionLabel" type="Label" parent="CallBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "custom action" - -[node name="WithLabel" type="Label" parent="CallBox"] -layout_mode = 2 -text = "with" - -[node name="NoArgsLabel" type="Label" parent="CallBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "no arguments" - -[node name="ArgBox" type="VBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"VBoxContainer_Small" diff --git a/nodes/audio_node/audio_node.gd b/nodes/audio_node/audio_node.gd deleted file mode 100644 index 9ac2aa88..00000000 --- a/nodes/audio_node/audio_node.gd +++ /dev/null @@ -1,32 +0,0 @@ -class_name AudioNode extends MonologueGraphNode - -var loop := Property.new(TOGGLE, {}, false) -var volume := Property.new(SLIDER, {"suffix": "db", "minimum": -80, "maximum": 24, "step": 0.25}) -var pitch := Property.new(SLIDER, {"default": 1, "minimum": 0, "maximum": 4, "step": 0.1}) -var audio := Property.new(FILE, {"filters": FilePicker.AUDIO}) - -@onready var _audio_label = $HBox/AudioLabel -@onready var _loop_label = $HBox/LoopLabel - - -func _ready(): - node_type = "NodeAudio" - super._ready() - audio.connect("preview", _on_audio_preview) - audio.setters["base_path"] = get_parent().file_path - loop.connect("preview", _on_loop_preview) - _update() - - -func _on_audio_preview(audio_path: Variant): - _audio_label.text = str(audio_path).get_file() if audio_path else "nothing" - - -func _on_loop_preview(is_loop: Variant): - _loop_label.visible = bool(is_loop) - - -func _update(): - _on_audio_preview(audio.value) - _loop_label.visible = loop.value - super._update() diff --git a/nodes/audio_node/audio_node.gd.uid b/nodes/audio_node/audio_node.gd.uid deleted file mode 100644 index 57d91551..00000000 --- a/nodes/audio_node/audio_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bkh2tynw3hgl4 diff --git a/nodes/audio_node/audio_node.tscn b/nodes/audio_node/audio_node.tscn deleted file mode 100644 index 43fe7352..00000000 --- a/nodes/audio_node/audio_node.tscn +++ /dev/null @@ -1,48 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://cu4kxjolgtrml"] - -[ext_resource type="Script" uid="uid://bkh2tynw3hgl4" path="res://nodes/audio_node/audio_node.gd" id="3_su5mj"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[sub_resource type="LabelSettings" id="LabelSettings_v4ya3"] -font_color = Color(0.572549, 0.572549, 0.572549, 1) - -[node name="AudioNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 214.0 -offset_bottom = 82.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme = SubResource("Theme_llmqa") -title = "AudioNode" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("3_su5mj") -titlebar_color = Color(0, 0.505882, 0.501961, 1) - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="Label" type="Label" parent="HBox"] -layout_mode = 2 -text = "Play" - -[node name="AudioLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "nothing" - - -[node name="LoopLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "and loop" - diff --git a/nodes/background_node/background_node.gd b/nodes/background_node/background_node.gd deleted file mode 100644 index 34441d6a..00000000 --- a/nodes/background_node/background_node.gd +++ /dev/null @@ -1,58 +0,0 @@ -class_name BackgroundNode extends MonologueGraphNode - -var image := Property.new(FILE, {"filters": FilePicker.IMAGE}) -var transition := Property.new(DROPDOWN, {}, "No Transition") -var duration := Property.new(SPINBOX, {"step": 0.1, "minimum": 0.0}, 0.0) - -@onready var _path_label = $VBox/HBox/PathLabel -@onready var _preview_rect = $VBox/PreviewRect - - -func _ready(): - node_type = "NodeBackground" - transition.callers["set_items"] = [ - [ - {"id": 0, "text": "No Transition"}, - {"id": 1, "text": "Push Down"}, - {"id": 1, "text": "Push Left"}, - {"id": 1, "text": "Push Right"}, - {"id": 1, "text": "Push Up"}, - {"id": 1, "text": "Simple Fade"}, - ] - ] - transition.connect("preview", _update) - super._ready() - image.setters["base_path"] = get_parent().file_path - image.connect("preview", _on_path_preview) - _update() - - -func _load_image(): - _path_label.text = image.value if image.value else "nothing" - _preview_rect.hide() - size.y = 0 - var base = image.setters.get("base_path") - var path = Path.relative_to_absolute(image.value, base) - if FileAccess.file_exists(path): - var img = ImageLoader.load_image(path) - if img: - _preview_rect.show() - _preview_rect.texture = img - _path_label.text = image.value.get_file() - else: - _preview_rect.hide() - - -func _on_path_preview(path: Variant): - _path_label.text = str(path).get_file() - _load_image.call_deferred() - - -func _update(_value: Variant = null): - _path_label.text = image.value - _load_image() - super._update() - - -func _get_field_groups() -> Array: - return ["image", {"Transition": ["transition", "duration"]}] diff --git a/nodes/background_node/background_node.gd.uid b/nodes/background_node/background_node.gd.uid deleted file mode 100644 index 97d62a11..00000000 --- a/nodes/background_node/background_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dx6dikjrdfg5f diff --git a/nodes/background_node/background_node.tscn b/nodes/background_node/background_node.tscn deleted file mode 100644 index a4913ca7..00000000 --- a/nodes/background_node/background_node.tscn +++ /dev/null @@ -1,53 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://cwpq37000kaqi"] - -[ext_resource type="Script" uid="uid://dx6dikjrdfg5f" path="res://nodes/background_node/background_node.gd" id="3_oaxk1"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[sub_resource type="LabelSettings" id="LabelSettings_ae3bv"] -font_color = Color(0.572549, 0.572549, 0.572549, 1) - -[node name="Background" type="GraphNode" groups=["graph_nodes"]] -custom_minimum_size = Vector2(300, 0) -offset_right = 308.0 -offset_bottom = 72.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme = SubResource("Theme_llmqa") -title = "SetterNode" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("3_oaxk1") -titlebar_color = Color(0, 0.505882, 0.501961, 1) - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -theme_type_variation = &"VBoxContainer_Big" - -[node name="HBox" type="HBoxContainer" parent="VBox"] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="BgLabel" type="Label" parent="VBox/HBox"] -layout_mode = 2 -text = "Set Background to Image" - -[node name="PathLabel" type="Label" parent="VBox/HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "No Image" - -[node name="PreviewRect" type="TextureRect" parent="VBox"] -visible = false -custom_minimum_size = Vector2(0, 200) -layout_mode = 2 -expand_mode = 1 -stretch_mode = 6 diff --git a/nodes/bridge_in_node/bridge_in_node.gd b/nodes/bridge_in_node/bridge_in_node.gd deleted file mode 100644 index 4110101c..00000000 --- a/nodes/bridge_in_node/bridge_in_node.gd +++ /dev/null @@ -1,46 +0,0 @@ -## Continues the dialogue from BridgeIn node to its counterpart BridgeOut node. -@icon("res://ui/assets/icons/link.svg") -class_name BridgeInNode extends MonologueGraphNode - -var bridge_out_scene = preload("res://nodes/bridge_out_node/bridge_out_node.tscn") - -## Spinner control which selects what number to bridge to. -@onready var number_selector := $HBoxContainer/LinkNumber - - -func _ready(): - node_type = "NodeBridgeIn" - title = node_type - super._ready() - - -func add_to(graph): - var created = super.add_to(graph) - var number = graph.get_free_bridge_number() - number_selector.value = number - - var bridge_out = bridge_out_scene.instantiate() - bridge_out.add_to(graph) - bridge_out.number_selector.value = number - created.append(bridge_out) - - return created - - -func _from_dict(dict): - number_selector.value = dict.get("NumberSelector") - super._from_dict(dict) - - -func _load_connections(_data: Dictionary, _key: String = "") -> void: - return # BridgeIn uses NextID covertly, not as a graph connection - - -func _to_fields(dict: Dictionary) -> void: - super._to_fields(dict) - dict["NumberSelector"] = number_selector.value - - -func _to_next(dict: Dictionary, key: String = "NextID") -> void: - var next_node = get_parent().get_linked_bridge_node(number_selector.value) - dict[key] = next_node.id.value if next_node else -1 diff --git a/nodes/bridge_in_node/bridge_in_node.gd.uid b/nodes/bridge_in_node/bridge_in_node.gd.uid deleted file mode 100644 index a9ef5ac4..00000000 --- a/nodes/bridge_in_node/bridge_in_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bqxf6ixm3hcsj diff --git a/nodes/bridge_in_node/bridge_in_node.tscn b/nodes/bridge_in_node/bridge_in_node.tscn deleted file mode 100644 index ebe04d8d..00000000 --- a/nodes/bridge_in_node/bridge_in_node.tscn +++ /dev/null @@ -1,34 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://b0eo5lvnrwidu"] - -[ext_resource type="Script" uid="uid://bqxf6ixm3hcsj" path="res://nodes/bridge_in_node/bridge_in_node.gd" id="1_8y1eo"] -[ext_resource type="PackedScene" uid="uid://wiapsnoaoc44" path="res://common/ui/custom_spinbox/custom_spinbox.tscn" id="2_81r38"] - -[node name="BridgeInNode" type="GraphNode"] -offset_right = 240.0 -offset_bottom = 82.0 -scale = Vector2(0.6, 0.6) -mouse_filter = 1 -title = "BridgeIn" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = false -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_8y1eo") -titlebar_color = Color(0.360784, 0.501961, 0.184314, 1) - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 10 -alignment = 1 - -[node name="Label" type="Label" parent="HBoxContainer"] -layout_mode = 2 -text = "Link to" - -[node name="LinkNumber" parent="HBoxContainer" instance=ExtResource("2_81r38")] -layout_mode = 2 diff --git a/nodes/bridge_out_node/bridge_out_node.gd b/nodes/bridge_out_node/bridge_out_node.gd deleted file mode 100644 index 64fa3e17..00000000 --- a/nodes/bridge_out_node/bridge_out_node.gd +++ /dev/null @@ -1,19 +0,0 @@ -@icon("res://ui/assets/icons/link.svg") -class_name BridgeOutNode extends MonologueGraphNode - -@onready var number_selector := $HBoxContainer/LinkNumber - - -func _ready(): - node_type = "NodeBridgeOut" - super._ready() - - -func _from_dict(dict): - number_selector.value = dict.get("NumberSelector") - super._from_dict(dict) - - -func _to_fields(dict: Dictionary) -> void: - super._to_fields(dict) - dict["NumberSelector"] = number_selector.value diff --git a/nodes/bridge_out_node/bridge_out_node.gd.uid b/nodes/bridge_out_node/bridge_out_node.gd.uid deleted file mode 100644 index 6d817527..00000000 --- a/nodes/bridge_out_node/bridge_out_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bimgycdfh52xb diff --git a/nodes/bridge_out_node/bridge_out_node.tscn b/nodes/bridge_out_node/bridge_out_node.tscn deleted file mode 100644 index 64a7c712..00000000 --- a/nodes/bridge_out_node/bridge_out_node.tscn +++ /dev/null @@ -1,35 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://c75iblt2kuvr2"] - -[ext_resource type="Script" uid="uid://bimgycdfh52xb" path="res://nodes/bridge_out_node/bridge_out_node.gd" id="1_6kxjr"] -[ext_resource type="PackedScene" uid="uid://wiapsnoaoc44" path="res://common/ui/custom_spinbox/custom_spinbox.tscn" id="2_qrxgf"] - -[node name="BridgeOutNode" type="GraphNode"] -offset_right = 185.0 -offset_bottom = 72.0 -scale = Vector2(0.6, 0.6) -mouse_filter = 1 -title = "BridgeOut" -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_6kxjr") -titlebar_color = Color(0.360784, 0.501961, 0.184314, 1) - -[node name="HBoxContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_override_constants/separation = 10 -alignment = 1 - -[node name="Label" type="Label" parent="HBoxContainer"] -layout_mode = 2 -text = "Link to" -justification_flags = 195 - -[node name="LinkNumber" parent="HBoxContainer" instance=ExtResource("2_qrxgf")] -layout_mode = 2 diff --git a/nodes/character_node/character_node.gd b/nodes/character_node/character_node.gd deleted file mode 100644 index 0e8a12ea..00000000 --- a/nodes/character_node/character_node.gd +++ /dev/null @@ -1,152 +0,0 @@ -class_name CharacterNode extends MonologueGraphNode - -var character := Property.new(DROPDOWN, {"store_index": true}, 0) -var action_type := Property.new(DROPDOWN, {}, "Join") - -var _position := Property.new(DROPDOWN, {}, "Left") -var join_animation := Property.new(DROPDOWN, {}, "Default", "Animation Type") -var leave_animation := Property.new(DROPDOWN, {}, "Default", "Animation Type") -var update_animation := Property.new(DROPDOWN, {}, "Default", "Animation Type") -var portrait := Property.new(DROPDOWN, {"late_items": true}) -var duration := Property.new(SPINBOX, {"step": 0.1, "minimum": 0.0}, 0.5) -var _z_index := Property.new(SPINBOX, {"step": 1}, 0) -var mirrored := Property.new(TOGGLE, {}, false) - -var _control_groups = { - "Join": [portrait, _z_index, join_animation, _position, mirrored], - "Leave": [leave_animation], - "Update": [portrait, _z_index, update_animation, _position, mirrored], -} - -@onready var character_name_label := %CharacterNameLabel -@onready var action_type_label := %ActionTypeLabel -@onready var display_container := %DisplayContainer -@onready var position_label := %PositionLabel -@onready var portrait_name_label := %PortraitNameLabel - - -func _ready(): - node_type = "NodeCharacter" - - var characters: Array = get_graph_edit().characters - character.callers["set_items"] = [characters, "Character/Name", "EditorIndex"] - character.connect("preview", _update) - - portrait.connect("preview", _update) - - action_type.callers["set_items"] = [ - [ - {"id": 0, "text": "Join"}, - {"id": 1, "text": "Leave"}, - {"id": 2, "text": "Update"}, - ] - ] - action_type.connect("preview", _show_group) - action_type.connect("preview", _update) - - _position.callers["set_items"] = [ - [ - {"id": 0, "text": "Left"}, - {"id": 1, "text": "Center"}, - {"id": 2, "text": "Right"}, - ] - ] - _position.connect("preview", _update) - - join_animation.callers["set_items"] = [ - [ - {"id": 0, "text": "Default"}, - {"id": 1, "text": "None"}, - {"id": 2, "text": "Fade In"}, - {"id": 3, "text": "Slide In Auto"}, - {"id": 4, "text": "Slide In Down"}, - {"id": 5, "text": "Slide In Left"}, - {"id": 6, "text": "Slide In Right"}, - {"id": 7, "text": "Slide In Up"}, - ] - ] - join_animation.connect("preview", _update) - - leave_animation.callers["set_items"] = [ - [ - {"id": 0, "text": "Default"}, - {"id": 1, "text": "None"}, - {"id": 2, "text": "Fade Out"}, - {"id": 3, "text": "Slide Out Auto"}, - {"id": 4, "text": "Slide Out Down"}, - {"id": 5, "text": "Slide Out Left"}, - {"id": 6, "text": "Slide Out Right"}, - {"id": 7, "text": "Slide Out Up"}, - ] - ] - leave_animation.connect("preview", _update) - - update_animation.callers["set_items"] = [ - [ - {"id": 0, "text": "Default"}, - {"id": 1, "text": "None"}, - {"id": 2, "text": "Bounce"}, - {"id": 3, "text": "Shake"}, - ] - ] - update_animation.connect("preview", _update) - - super._ready() - _show_group() - _update() - - -func _update(_value: Variant = null) -> void: - await get_tree().process_frame - super._update() - - var action: Variant = action_type.value - var characters: Array = get_graph_edit().characters - character.callers["set_items"] = [characters, "Character/Name", "EditorIndex"] - if characters[character.value] and characters[character.value]["Character"].has("Portraits"): - if portrait.field: - portrait.field.set_items(characters[character.value]["Character"]["Portraits"], "Name") - else: - portrait.setters["set_items"] = [ - characters[character.value]["Character"]["Portraits"], "Name" - ] - - display_container.visible = action != "Leave" - character_name_label.text = characters[character.value].get("Character", {}).get( - "Name", "Unknown" - ) - action_type_label.text = action - position_label.text = _position.value - portrait_name_label.text = portrait.value if portrait.value else "Unknown" - - -func _show_group(act_type: Variant = action_type.value) -> void: - for key in _control_groups.keys(): - for property in _control_groups.get(key): - property.visible = false - - for key in _control_groups.keys(): - for property in _control_groups.get(key): - if key == act_type: - property.visible = true - title = node_type - - -func _get_field_groups() -> Array: - return [ - "character", - "action_type", - { - "Display Settings": - [ - "portrait", - "_z_index", - { - "Animation": - ["join_animation", "leave_animation", "update_animation", "duration"] - }, - "_position", - "mirrored" - ] - } - ] diff --git a/nodes/character_node/character_node.gd.uid b/nodes/character_node/character_node.gd.uid deleted file mode 100644 index 5c3db070..00000000 --- a/nodes/character_node/character_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c7bmubh3y8hfx diff --git a/nodes/character_node/character_node.tscn b/nodes/character_node/character_node.tscn deleted file mode 100644 index 07e2dd49..00000000 --- a/nodes/character_node/character_node.tscn +++ /dev/null @@ -1,83 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://gn7806oog6qo"] - -[ext_resource type="Script" uid="uid://c7bmubh3y8hfx" path="res://nodes/character_node/character_node.gd" id="3_3dcof"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ryo67"] -content_margin_left = 10.0 -content_margin_top = 2.0 -content_margin_right = 10.0 -content_margin_bottom = 2.0 -corner_radius_top_left = 2 -corner_radius_top_right = 2 -corner_radius_bottom_right = 2 -corner_radius_bottom_left = 2 - -[node name="CharacterNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 245.0 -offset_bottom = 136.0 -size_flags_vertical = 0 -theme_override_styles/titlebar = SubResource("StyleBoxFlat_ryo67") -title = "NodeCharacter" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("3_3dcof") -titlebar_color = Color(0, 0.505882, 0.501961, 1) - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 2 -theme_type_variation = &"FieldContainer" - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] -layout_mode = 2 - -[node name="CharacterLabel" type="Label" parent="VBoxContainer/HBoxContainer"] -layout_mode = 2 -text = "Character" - -[node name="CharacterNameLabel" type="Label" parent="VBoxContainer/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "Unknown" - -[node name="ActionTypeLabel" type="Label" parent="VBoxContainer/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "Join" - -[node name="DisplayContainer" type="HBoxContainer" parent="VBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 - -[node name="AtLabel" type="Label" parent="VBoxContainer/DisplayContainer"] -layout_mode = 2 -text = "at" - -[node name="PositionLabel" type="Label" parent="VBoxContainer/DisplayContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "Left" - -[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] -layout_mode = 2 - -[node name="PortraitLabel" type="Label" parent="VBoxContainer/HBoxContainer2"] -layout_mode = 2 -text = "with portrait" - -[node name="PortraitNameLabel" type="Label" parent="VBoxContainer/HBoxContainer2"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "Unknown" - -[connection signal="resize_request" from="." to="." method="_on_GraphNode_resize_request"] diff --git a/nodes/choice_node/choice_node.gd b/nodes/choice_node/choice_node.gd deleted file mode 100644 index 21c6e801..00000000 --- a/nodes/choice_node/choice_node.gd +++ /dev/null @@ -1,142 +0,0 @@ -@icon("res://ui/assets/icons/choice.svg") -class_name ChoiceNode extends MonologueGraphNode - -var option_scene = preload("res://nodes/option_node/option_node.tscn") -var options := Property.new(LIST, {}, []) - -## Temporary ID storage for first-time loading of base options in project load. -var _base_id_list: Array = [] - - -func _ready(): - node_type = "NodeChoice" - super._ready() - options.setters["add_callback"] = add_option - options.setters["get_callback"] = get_children - options.callers["set_label_visible"] = [false] - options.connect("preview", _refresh) - editor_position.set_visible(false) - GlobalSignal.add_listener("language_deleted", store_options) - - if get_child_count() <= 0: - options.value.append(add_option()._to_dict()) - options.value.append(add_option()._to_dict()) - - -func add_option(reference: Dictionary = {}) -> OptionNode: - var new_option = option_scene.instantiate() - add_child(new_option, true) - new_option.set_count(new_option.get_index() + 1) - - if reference: - new_option._from_dict(reference) - var value = reference.get("Option", reference.get("Sentence", "")) - if value is Dictionary: - var switcher = GlobalVariables.language_switcher - var locale = str(switcher.get_current_language()) - value = value.get(locale, "") - new_option.preview_label.text = value - link_option(new_option) - - var is_first = get_child_count() <= 1 - set_slot( - get_child_count() - 1, - is_first, - 0, - Color("ffffff"), - true, - 0, - Color("ffffff"), - LEFT_SLOT, - RIGHT_SLOT, - false - ) - return new_option - - -func clear_children(): - for child in get_children(): - remove_child(child) - child.queue_free() - - -func get_option_by_id(option_id: String) -> OptionNode: - for node in get_children(): - if node.id.value == option_id: - return node - return null - - -func link_option(option: OptionNode, link: bool = true): - var index = option.get_index() - if option.next_id.value is String: # non-connections are -1 (int) - var next_node = get_parent().get_node_by_id(option.next_id.value) - if next_node: - if link: - get_parent().connect_node(name, index, next_node.name, 0) - else: - get_parent().disconnect_node(name, index, next_node.name, 0) - - -func store_options(_name, _rest: Dictionary, choices: Dictionary) -> void: - var nodes = get_children().map(func(o): return o._to_dict()) - choices[self] = nodes - - -func reload_preview() -> void: - var nodes = get_children() - for node in nodes: - node.reload_preview() - - -## Update the NextID of this choice node on the given port. -func update_next_id(from_port: int, next_node: MonologueGraphNode): - var option_node = get_child(from_port) - var next_value = -1 - if next_node: - next_value = next_node.id.value - option_node.next_id.value = next_value - options.value[from_port] = option_node._to_dict() - options.propagate(options.value, false) - - -func _from_dict(dict: Dictionary) -> void: - _base_id_list = dict.get("OptionsID", []) - if _base_id_list.size() > 0: - options.value = [] - clear_children() - _load_position(dict) - super._from_dict(dict) - # overridden to prevent _update() from happening too early here - - -func _load_connections(_data: Dictionary, _key: String = "") -> void: - # called after _load_nodes() in MonologueControl, this is used to - # load embedded OptionNodes which automatically forms the connections - for option_id in _base_id_list: - var reference = get_parent().base_options[option_id] - var loaded_option = add_option(reference) - options.value.append(loaded_option._to_dict()) - - -func _refresh(new_options_list: Array): - # disconnect all outbound connections - for connection in get_parent().get_all_outbound_connections(name): - var from_port = connection.get("from_port") - var to_node = connection.get("to_node") - get_parent().disconnect_node(name, from_port, to_node, 0) - clear_children() - - for new_option_data in new_options_list: - add_option(new_option_data) - _update() - - -func restore_options(options_value: Array) -> void: - options.value = options_value - _refresh(options_value) - - -func _to_fields(dict: Dictionary) -> void: - var child_options = get_children().filter(func(c): return c is OptionNode) - dict["OptionsID"] = child_options.map(func(o): return o.id.value) diff --git a/nodes/choice_node/choice_node.gd.uid b/nodes/choice_node/choice_node.gd.uid deleted file mode 100644 index 979d87f2..00000000 --- a/nodes/choice_node/choice_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://hoywtrxb87tk diff --git a/nodes/choice_node/choice_node.tscn b/nodes/choice_node/choice_node.tscn deleted file mode 100644 index 10d58c9d..00000000 --- a/nodes/choice_node/choice_node.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://coq74c18uq3d3"] - -[ext_resource type="Script" uid="uid://hoywtrxb87tk" path="res://nodes/choice_node/choice_node.gd" id="1_nn3v2"] - -[node name="ChoiceNode" type="GraphNode" groups=["graph_nodes"]] -custom_minimum_size = Vector2(400, 60) -offset_right = 400.0 -offset_bottom = 60.0 -size_flags_vertical = 0 -title = "ChoiceNode" -script = ExtResource("1_nn3v2") -titlebar_color = Color(0.835294, 0.317647, 0.376471, 1) diff --git a/nodes/comment_node/comment_node.gd b/nodes/comment_node/comment_node.gd deleted file mode 100644 index 245987c3..00000000 --- a/nodes/comment_node/comment_node.gd +++ /dev/null @@ -1,19 +0,0 @@ -@icon("res://ui/assets/icons/comment.svg") -class_name CommentNode extends MonologueGraphNode - -@onready var comment_edit = $CommentEdit - - -func _ready() -> void: - node_type = "NodeComment" - super._ready() - - -func _from_dict(dict: Dictionary) -> void: - comment_edit.text = dict.get("Comment") - super._from_dict(dict) - - -func _to_fields(dict: Dictionary) -> void: - super._to_fields(dict) - dict["Comment"] = comment_edit.text diff --git a/nodes/comment_node/comment_node.gd.uid b/nodes/comment_node/comment_node.gd.uid deleted file mode 100644 index 80524383..00000000 --- a/nodes/comment_node/comment_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b60bh6vkocfn0 diff --git a/nodes/comment_node/comment_node.tscn b/nodes/comment_node/comment_node.tscn deleted file mode 100644 index ad65a005..00000000 --- a/nodes/comment_node/comment_node.tscn +++ /dev/null @@ -1,31 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://b4ptsysoq7mgm"] - -[ext_resource type="Script" uid="uid://b60bh6vkocfn0" path="res://nodes/comment_node/comment_node.gd" id="3_vd25i"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[node name="CommentNode" type="GraphNode" groups=["graph_nodes"]] -custom_minimum_size = Vector2(400, 100) -offset_right = 400.0 -offset_bottom = 100.0 -theme = SubResource("Theme_llmqa") -title = "NodeComment" -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = false -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = true -script = ExtResource("3_vd25i") -titlebar_color = Color(1, 1, 1, 1) - -[node name="CommentEdit" type="TextEdit" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -wrap_mode = 1 -scroll_fit_content_height = true - -[connection signal="resize_request" from="." to="." method="_on_GraphNode_resize_request"] diff --git a/nodes/condition_node/condition_node.gd b/nodes/condition_node/condition_node.gd deleted file mode 100644 index 94f62594..00000000 --- a/nodes/condition_node/condition_node.gd +++ /dev/null @@ -1,25 +0,0 @@ -@icon("res://ui/assets/icons/Condition.svg") -class_name ConditionNode extends EventNode - - -func _ready(): - super._ready() - node_type = "NodeCondition" - title = node_type - - -func _load_connections(data: Dictionary, key: String = "IfNextID") -> void: - super._load_connections(data, key) - var else_next_id = data.get("ElseNextID") - if else_next_id is String: - var else_next_node = get_graph_edit().get_node_by_id(else_next_id) - if else_next_node: - get_graph_edit().connect_node(name, 1, else_next_node.name, 0) - - -func _to_next(dict: Dictionary, key: String = "IfNextID") -> void: - var next_id_node = get_graph_edit().get_all_connections_from_slot(name, 0) - dict[key] = next_id_node[0].id.value if next_id_node else -1 - - var else_id_node = get_graph_edit().get_all_connections_from_slot(name, 1) - dict["ElseNextID"] = else_id_node[0].id.value if else_id_node else -1 diff --git a/nodes/condition_node/condition_node.gd.uid b/nodes/condition_node/condition_node.gd.uid deleted file mode 100644 index e55a1c79..00000000 --- a/nodes/condition_node/condition_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bt5pl4c11vfxd diff --git a/nodes/condition_node/condition_node.tscn b/nodes/condition_node/condition_node.tscn deleted file mode 100644 index a029f8c2..00000000 --- a/nodes/condition_node/condition_node.tscn +++ /dev/null @@ -1,73 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://dsv566w4evo8b"] - -[ext_resource type="Script" uid="uid://bt5pl4c11vfxd" path="res://nodes/condition_node/condition_node.gd" id="3_vhhwe"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[sub_resource type="LabelSettings" id="LabelSettings_v4ya3"] -font_color = Color(0.572549, 0.572549, 0.572549, 1) - -[node name="ConditionNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 314.0 -offset_bottom = 128.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme = SubResource("Theme_llmqa") -title = "ConditionNode" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -slot/1/left_enabled = false -slot/1/left_type = 0 -slot/1/left_color = Color(1, 1, 1, 1) -slot/1/left_icon = null -slot/1/right_enabled = true -slot/1/right_type = 0 -slot/1/right_color = Color(1, 1, 1, 1) -slot/1/right_icon = null -slot/1/draw_stylebox = false -script = ExtResource("3_vhhwe") -titlebar_color = Color(0.733333, 0.392157, 0.188235, 1) - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="IfLabel" type="Label" parent="HBox"] -layout_mode = 2 -text = "If" - -[node name="VariableLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "variable" - - -[node name="OperatorLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "condition" - - -[node name="ValueLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "value" - - -[node name="ThenLabel" type="Label" parent="HBox"] -layout_mode = 2 -text = "then" - -[node name="ElseHBox" type="HBoxContainer" parent="."] -layout_mode = 2 - -[node name="ElseLabel" type="Label" parent="ElseHBox"] -layout_mode = 2 -text = "Else" diff --git a/nodes/end_path_node/end_path_node.gd b/nodes/end_path_node/end_path_node.gd deleted file mode 100644 index d73cdfd2..00000000 --- a/nodes/end_path_node/end_path_node.gd +++ /dev/null @@ -1,20 +0,0 @@ -@icon("res://ui/assets/icons/exit.svg") -class_name EndPathNode extends MonologueGraphNode - -var next_story := Property.new(FILE, {"filters": ["*.json;Monologue File"]}) - - -func _ready(): - node_type = "NodeEndPath" - super._ready() - next_story.setters["base_path"] = get_parent().file_path - - -func _from_dict(dict: Dictionary): - next_story.value = dict.get("NextStoryName", "") # backwards compatibility - super._from_dict(dict) - - -func _on_close_request(): - queue_free() - get_parent().clear_all_empty_connections() diff --git a/nodes/end_path_node/end_path_node.gd.uid b/nodes/end_path_node/end_path_node.gd.uid deleted file mode 100644 index 142c5d16..00000000 --- a/nodes/end_path_node/end_path_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cnoxaqxh33sue diff --git a/nodes/end_path_node/end_path_node.tscn b/nodes/end_path_node/end_path_node.tscn deleted file mode 100644 index 8d5d4a80..00000000 --- a/nodes/end_path_node/end_path_node.tscn +++ /dev/null @@ -1,30 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://c3t0igg4s2pne"] - -[ext_resource type="Script" uid="uid://cnoxaqxh33sue" path="res://nodes/end_path_node/end_path_node.gd" id="1_vdup8"] -[ext_resource type="Texture2D" uid="uid://dccbv4x7joj8a" path="res://ui/assets/icons/exit.png" id="3_w7usk"] - -[node name="EndPathNode" type="GraphNode"] -offset_right = 240.0 -offset_bottom = 91.0 -scale = Vector2(0.6, 0.6) -mouse_filter = 1 -title = "EndPath" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = false -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_vdup8") -titlebar_color = Color(0.0196078, 0.513726, 0.341176, 1) - -[node name="TextureRect" type="TextureRect" parent="."] -custom_minimum_size = Vector2(48, 48) -layout_mode = 2 -size_flags_vertical = 3 -texture = ExtResource("3_w7usk") -expand_mode = 2 -stretch_mode = 5 diff --git a/nodes/event_node/event_node.gd b/nodes/event_node/event_node.gd deleted file mode 100644 index 13f32b85..00000000 --- a/nodes/event_node/event_node.gd +++ /dev/null @@ -1,48 +0,0 @@ -@icon("res://ui/assets/icons/calendar.svg") -class_name EventNode extends AbstractVariableNode - - -func _ready(): - node_type = "NodeEvent" - super._ready() - - -func get_variable_label() -> Label: - return $HBox/VariableLabel - - -func get_operator_label() -> Label: - return $HBox/OperatorLabel - - -func get_value_label() -> Label: - return $HBox/ValueLabel - - -func get_operator_options(): - return [ - {"id": 0, "text": "=="}, - {"id": 1, "text": ">="}, - {"id": 2, "text": "<="}, - {"id": 3, "text": "!="}, - ] - - -func get_operator_disabler(): - return [1, 2] - - -func _from_dict(dict: Dictionary): - var condition = dict.get("Condition", {}) - var morphing_value = dict.get("Value", "") - if condition: - for v in get_graph_edit().variables: - if v.get("Name") == condition.get("Variable"): - variable.value = condition.get("Variable") - break - operator.value = condition.get("Operator", "==") - morphing_value = condition.get("Value", "") - value.value = morphing_value - - record_morph(morphing_value) - super._from_dict(dict) diff --git a/nodes/event_node/event_node.gd.uid b/nodes/event_node/event_node.gd.uid deleted file mode 100644 index b4e092a8..00000000 --- a/nodes/event_node/event_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dytqpwd0qegbw diff --git a/nodes/event_node/event_node.tscn b/nodes/event_node/event_node.tscn deleted file mode 100644 index 6a1baad5..00000000 --- a/nodes/event_node/event_node.tscn +++ /dev/null @@ -1,57 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://bwfykyaajuu4i"] - -[ext_resource type="Script" uid="uid://dytqpwd0qegbw" path="res://nodes/event_node/event_node.gd" id="3_c6hyr"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[sub_resource type="LabelSettings" id="LabelSettings_v4ya3"] -font_color = Color(0.572549, 0.572549, 0.572549, 1) - -[node name="EventNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 324.0 -offset_bottom = 72.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme = SubResource("Theme_llmqa") -title = "EventNode" -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("3_c6hyr") -titlebar_color = Color(0.0196078, 0.513726, 0.341176, 1) - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="WhenLabel" type="Label" parent="HBox"] -layout_mode = 2 -text = "When" - -[node name="VariableLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "variable" - - -[node name="OperatorLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "condition" - - -[node name="ValueLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "value" - - -[node name="DoLabel" type="Label" parent="HBox"] -layout_mode = 2 -text = "do" \ No newline at end of file diff --git a/nodes/option_node/option_node.gd b/nodes/option_node/option_node.gd deleted file mode 100644 index 0f1aa2c0..00000000 --- a/nodes/option_node/option_node.gd +++ /dev/null @@ -1,72 +0,0 @@ -class_name OptionNode extends MonologueGraphNode - -var option := Localizable.new(TEXT, {"minimum_size": Vector2(200, 100)}) -var enable_by_default := Property.new(CHECKBOX, {}, true) -var one_shot := Property.new(CHECKBOX, {}, false) -var next_id := Property.new(LINE, {}, -1) - -@onready var choice_node = get_parent() -@onready var count_label = %CountLabel -@onready var preview_label = $VBox/PreviewLabel -@onready var ebd_label = %EbDLabel -@onready var one_shot_label = %OneShotLabel - - -func _ready() -> void: - node_type = "NodeOption" - super._ready() - option.connect("change", update_parent) - option.connect("preview", _on_text_preview) - one_shot.connect("change", update_parent) - one_shot.connect("preview", _update) - enable_by_default.connect("change", update_parent) - enable_by_default.connect("preview", _update) - next_id.visible = false - get_titlebar_hbox().get_child(0).hide() - _update() - - -func display() -> void: - get_graph_edit().set_selected(get_parent()) - - -func get_graph_edit() -> MonologueGraphEdit: - return choice_node.get_graph_edit() - - -func reload_preview() -> void: - preview_label.text = option.value - - -func set_count(number: int) -> void: - count_label.text = "Option %d" % number - - -func _update(_value: Variant = null) -> void: - if ebd_label and one_shot_label: - ebd_label.visible = enable_by_default.value - one_shot_label.visible = one_shot.value - - -func update_parent(_old_value = "", _new_value = "") -> void: - var old_option = choice_node.options.value[get_index()] - var new_options = choice_node.options.value.duplicate(true) - new_options[get_index()] = old_option.merged(_to_dict(), true) - choice_node.options.value = new_options - - _update() - - -func _from_dict(dict: Dictionary) -> void: - option.value = dict.get("Sentence", "") - enable_by_default.value = dict.get("Enable", false) - dict.erase("EditorPosition") - super._from_dict(dict) - - -func _on_text_preview(text: Variant) -> void: - preview_label.text = str(text) - - -func _to_next(dict: Dictionary, key: String = "NextID") -> void: - dict[key] = next_id.value diff --git a/nodes/option_node/option_node.gd.uid b/nodes/option_node/option_node.gd.uid deleted file mode 100644 index 5334a03a..00000000 --- a/nodes/option_node/option_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b3sgq1spd1cf8 diff --git a/nodes/option_node/option_node.tscn b/nodes/option_node/option_node.tscn deleted file mode 100644 index 5accde55..00000000 --- a/nodes/option_node/option_node.tscn +++ /dev/null @@ -1,80 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://ccuhx5vr7t50a"] - -[ext_resource type="Script" uid="uid://b3sgq1spd1cf8" path="res://nodes/option_node/option_node.gd" id="1_67u8k"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5mv5j"] -content_margin_left = 10.0 -content_margin_top = 10.0 -content_margin_right = 10.0 -content_margin_bottom = 10.0 -bg_color = Color(0.192157, 0.192157, 0.211765, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.301961, 0.301961, 0.301961, 1) -corner_radius_top_left = 3 -corner_radius_top_right = 3 -corner_radius_bottom_right = 3 -corner_radius_bottom_left = 3 -corner_detail = 12 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_32su8"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_vd2tk"] - -[node name="OptionNode" type="GraphNode"] -custom_minimum_size = Vector2(0, 70) -offset_right = 412.0 -offset_bottom = 94.0 -size_flags_horizontal = 3 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_5mv5j") -theme_override_styles/titlebar = SubResource("StyleBoxEmpty_32su8") -theme_override_styles/slot = SubResource("StyleBoxEmpty_vd2tk") -draggable = false -selectable = false -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = false -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = true -script = ExtResource("1_67u8k") -titlebar_color = Color(0.835294, 0.317647, 0.376471, 1) -show_titlebar = false - -[node name="VBox" type="VBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"VBoxContainer_Small" - -[node name="HBoxContainer" type="HBoxContainer" parent="VBox"] -custom_minimum_size = Vector2(0, 29) -layout_mode = 2 - -[node name="CountLabel" type="Label" parent="VBox/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NoteLabel" -text = "Option 1" - -[node name="EbDLabel" type="Label" parent="VBox/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "Enable by default" - -[node name="OneShotLabel" type="Label" parent="VBox/HBoxContainer"] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "One-shot" - -[node name="PreviewLabel" type="Label" parent="VBox"] -layout_mode = 2 -clip_text = true -text_overrun_behavior = 3 diff --git a/nodes/random_node/output_line.tscn b/nodes/random_node/output_line.tscn deleted file mode 100644 index fa55ab68..00000000 --- a/nodes/random_node/output_line.tscn +++ /dev/null @@ -1,17 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://pa33y1sg05tb"] - -[sub_resource type="GDScript" id="GDScript_ojesf"] -script/source = "extends HBoxContainer - - -func update_label(new_value: String): - $Label.text = new_value -" - -[node name="OutputLine" type="HBoxContainer"] -alignment = 2 -script = SubResource("GDScript_ojesf") - -[node name="Label" type="Label" parent="."] -layout_mode = 2 -text = "100%" diff --git a/nodes/random_node/random_node.gd b/nodes/random_node/random_node.gd deleted file mode 100644 index 7524ca54..00000000 --- a/nodes/random_node/random_node.gd +++ /dev/null @@ -1,150 +0,0 @@ -@icon("res://ui/assets/icons/dice.svg") -class_name RandomNode extends MonologueGraphNode - -@onready var output_line := preload("res://nodes/random_node/output_line.tscn") - -var outputs := Property.new(LIST, {}, []) - -var _output_references: Array = [] - - -func _ready(): - node_type = "NodeRandom" - super._ready() - - outputs.setters["add_callback"] = add_output - outputs.setters["delete_callback"] = modify_on_delete - outputs.setters["get_callback"] = get_outputs - outputs.connect("preview", _refresh) - - if outputs.value.size() <= 0: - load_outputs([]) - _refresh(outputs.value) - - -func _from_dict(dict: Dictionary) -> void: - # check for backwards compatibility v2.x - if dict.has("Target"): - var converter = NodeConverter.new() - dict = converter.convert_dice_roll(dict) - - load_outputs(dict.get("Outputs", [])) - _load_position(dict) - _update() - - -func _load_connections(data: Dictionary, _key: String = "NextID") -> void: - for output in data.get("Outputs"): - link_output(output, output.get("ID")) - - -func _to_next(_dict: Dictionary, _key: String = "NextID") -> void: - pass - - -func add_output(data: Dictionary = {}) -> MonologueRandomOutput: - var output = MonologueRandomOutput.new(self) - output.id.value = _output_references.size() - if data: - output._from_dict(data) - link_output(data, output.id.value) - - _output_references.append(output) - var line_instance := output_line.instantiate() - add_child(line_instance) - line_instance.update_label(str(int(output.weight.value)) + "%") - - # if output was added from scratch, redistribute all equally - if not data: - var share = 100.0 / _output_references.size() - for idx in range(_output_references.size()): - var weight = ceil(share) if idx == 0 else floor(share) - _output_references[idx].weight.value = weight - - var refs = _output_references.slice(0, _output_references.size() - 1) - var dicts = refs.map(func(r): return r._to_dict()) - outputs.propagate(dicts) - - return output - - -func clear_children() -> void: - for child in get_children(): - child.free() - _output_references.clear() - - -func get_outputs() -> Array: - return _output_references - - -func link_output(data: Dictionary, from_port: int): - var next_id = data.get("NextID", -1) - if next_id is String: - var next_node = get_parent().get_node_by_id(next_id) - if next_node: - get_parent().connect_node(name, from_port, next_node.name, 0) - - -func load_outputs(new_output_list: Array): - clear_children() - var ascending = func(a, b): return a.get("ID") < b.get("ID") - new_output_list.sort_custom(ascending) - for output_data in new_output_list: - add_output(output_data) - - if _output_references.is_empty(): - var first = add_output() - var second = add_output() - first.weight.value = 50 - second.weight.value = 50 - new_output_list.append(first._to_dict()) - new_output_list.append(second._to_dict()) - outputs.value = new_output_list - - -func modify_on_delete(data_list: Array): - if data_list: - # reassign IDs on delete - for i in range(data_list.size()): - data_list[i]["ID"] = i - - # rebalance weights if the sum is not equal to 100 - var weights = data_list.map(func(d): return d.get("Weight")) - var sum = weights.reduce(func(total, w): return total + w) - if sum != 100: - var share = 100.0 / data_list.size() - for idx in range(data_list.size()): - var weight = ceil(share) if idx == 0 else floor(share) - data_list[idx]["Weight"] = weight - return data_list - - -## Update the NextID of the output on the given port. -func update_next_id(from_port: int, next_node: MonologueGraphNode): - var output = _output_references[from_port] - var next_value = -1 - if next_node: - next_value = next_node.id.value - output.next_id.value = next_value - outputs.value[from_port] = output._to_dict() - outputs.propagate(outputs.value, false) - - -func _refresh(new_outputs_list: Array): - # disconnect all outbound connections - for connection in get_parent().get_all_outbound_connections(name): - var from_port = connection.get("from_port") - var to_node = connection.get("to_node") - get_parent().disconnect_node(name, from_port, to_node, 0) - clear_children() - - for new_output_data in new_outputs_list: - add_output(new_output_data) - _update() - - -func _update(_new_value: Variant = null) -> void: - for idx in get_child_count(): - set_slot(idx, idx == 0, 0, Color.WHITE, true, 0, Color.WHITE, LEFT_SLOT, RIGHT_SLOT) - super._update() diff --git a/nodes/random_node/random_node.gd.uid b/nodes/random_node/random_node.gd.uid deleted file mode 100644 index bc1d050e..00000000 --- a/nodes/random_node/random_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cx8uppic5c27l diff --git a/nodes/random_node/random_node.tscn b/nodes/random_node/random_node.tscn deleted file mode 100644 index 26f6942e..00000000 --- a/nodes/random_node/random_node.tscn +++ /dev/null @@ -1,10 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://b6mcmodpmsacw"] - -[ext_resource type="Script" uid="uid://cx8uppic5c27l" path="res://nodes/random_node/random_node.gd" id="1_qeufj"] - -[node name="RandomNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 127.0 -offset_bottom = 53.0 -title = "RandomNode" -script = ExtResource("1_qeufj") -titlebar_color = Color(0.560784, 0.462745, 0.0980392, 1) diff --git a/nodes/random_node/random_output.gd b/nodes/random_node/random_output.gd deleted file mode 100644 index 668dc768..00000000 --- a/nodes/random_node/random_output.gd +++ /dev/null @@ -1,61 +0,0 @@ -## Random output data builder. -class_name MonologueRandomOutput extends RefCounted - -var weight := Property.new(MonologueGraphNode.SPINBOX, {"minimum": 0, "maximum": 100}) -var id := Property.new(MonologueGraphNode.SPINBOX, {}, 0) -var next_id := Property.new(MonologueGraphNode.LINE, {}, -1) - -var graph: MonologueGraphEdit -var graph_node: RandomNode - - -func _init(node: RandomNode): - graph_node = node - graph = node.get_parent() - weight.connect("change", change_output_weight) - id.visible = false - - -func change_output_weight(old_value: Variant, new_value: Variant): - var old_list = graph_node.outputs.value.duplicate(true) - var new_list = graph_node.outputs.value.duplicate(true) - - # new value cannot push total weight to exceed 100 - var clamped = clampi(new_value, 1, 100 - new_list.size() + 1) - new_list[id.value]["Weight"] = clamped - - # make up for missing weight by balancing from next - var weight_sum = new_list.reduce(func(total, n): return total + n.get("Weight"), 0) - var balance = 100 - weight_sum - var count = 1 - while count < new_list.size() and balance != 0: - var i = int(id.value + count) % new_list.size() - var new_weight = new_list[i].get("Weight") + balance - if new_weight < 1: - balance -= new_list[i].get("Weight") - 1 - new_weight = 1 - else: - balance = 0 - new_list[i]["Weight"] = new_weight - count += 1 - - graph.undo_redo.create_action("RandomOutput %s => %s" % [old_value, new_value]) - graph.undo_redo.add_do_property(graph_node.outputs, "value", new_list) - graph.undo_redo.add_do_method(graph_node.outputs.propagate.bind(new_list)) - graph.undo_redo.add_undo_property(graph_node.outputs, "value", old_list) - graph.undo_redo.add_undo_method(graph_node.outputs.propagate.bind(old_list)) - graph.undo_redo.commit_action() - - -func get_property_names() -> PackedStringArray: - return ["weight"] - - -func _from_dict(dict: Dictionary) -> void: - #id.value = dict.get("ID") - weight.value = dict.get("Weight") - next_id.value = dict.get("NextID") - - -func _to_dict(): - return {"ID": id.value, "Weight": weight.value, "NextID": next_id.value} diff --git a/nodes/random_node/random_output.gd.uid b/nodes/random_node/random_output.gd.uid deleted file mode 100644 index abc14ac5..00000000 --- a/nodes/random_node/random_output.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://deywhjcajo6if diff --git a/nodes/reroute_node/reroute_node.gd b/nodes/reroute_node/reroute_node.gd deleted file mode 100644 index fd4fc1c5..00000000 --- a/nodes/reroute_node/reroute_node.gd +++ /dev/null @@ -1,20 +0,0 @@ -class_name RerouteNode extends MonologueGraphNode - -@onready var drag_panel: PanelContainer = $Control/CenterContainer/DragPanel - - -func _ready() -> void: - drag_panel.modulate.a = 0 - node_type = "NodeReroute" - super._ready() - title = "" - - -func _on_mouse_entered() -> void: - var tween: Tween = create_tween() - tween.tween_property(drag_panel, "modulate:a", 1, 0.1) - - -func _on_mouse_exited() -> void: - var tween: Tween = create_tween() - tween.tween_property(drag_panel, "modulate:a", 0, 0.1) diff --git a/nodes/reroute_node/reroute_node.gd.uid b/nodes/reroute_node/reroute_node.gd.uid deleted file mode 100644 index af5cff43..00000000 --- a/nodes/reroute_node/reroute_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dar0qh0amas7f diff --git a/nodes/reroute_node/reroute_node.tscn b/nodes/reroute_node/reroute_node.tscn deleted file mode 100644 index 90f18eb6..00000000 --- a/nodes/reroute_node/reroute_node.tscn +++ /dev/null @@ -1,87 +0,0 @@ -[gd_scene load_steps=9 format=3 uid="uid://b4n4q43gfk82"] - -[ext_resource type="Script" uid="uid://dar0qh0amas7f" path="res://nodes/reroute_node/reroute_node.gd" id="1_2xjs7"] -[ext_resource type="Texture2D" uid="uid://blumv8ks5udd0" path="res://ui/assets/icons/move.svg" id="2_qvw7q"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_f11gq"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_0vsom"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eb0dt"] -content_margin_left = 16.0 -content_margin_top = 16.0 -content_margin_right = 16.0 -content_margin_bottom = 16.0 -bg_color = Color(0.952941, 0.352941, 0.572549, 0) - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_pfaoj"] -content_margin_left = 16.0 -content_margin_top = 16.0 -content_margin_right = 16.0 -content_margin_bottom = 16.0 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_m1v7b"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_03oiu"] -bg_color = Color(0.192157, 0.192157, 0.211765, 1) -corner_radius_top_left = 20 -corner_radius_top_right = 20 -corner_radius_bottom_right = 20 -corner_radius_bottom_left = 20 -corner_detail = 20 - -[node name="RerouteNode" type="GraphNode"] -custom_minimum_size = Vector2(60, 0) -anchors_preset = -1 -anchor_right = 0.04 -anchor_bottom = 0.094 -offset_right = -20.0 -offset_bottom = -0.199997 -mouse_filter = 1 -theme_override_constants/port_h_offset = 30 -theme_override_styles/panel = SubResource("StyleBoxEmpty_f11gq") -theme_override_styles/panel_selected = SubResource("StyleBoxEmpty_0vsom") -theme_override_styles/titlebar = SubResource("StyleBoxFlat_eb0dt") -theme_override_styles/titlebar_selected = SubResource("StyleBoxEmpty_pfaoj") -theme_override_styles/slot = SubResource("StyleBoxEmpty_m1v7b") -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_2xjs7") -show_close_button = false -show_titlebar = false - -[node name="Control" type="Control" parent="."] -layout_mode = 2 -mouse_filter = 1 - -[node name="CenterContainer" type="CenterContainer" parent="Control"] -layout_mode = 1 -anchors_preset = 10 -anchor_right = 1.0 -offset_top = -53.0 -offset_bottom = -13.0 -grow_horizontal = 2 - -[node name="DragPanel" type="PanelContainer" parent="Control/CenterContainer"] -custom_minimum_size = Vector2(40, 40) -layout_mode = 2 -mouse_filter = 1 -theme_override_styles/panel = SubResource("StyleBoxFlat_03oiu") - -[node name="CenterContainer" type="CenterContainer" parent="Control/CenterContainer/DragPanel"] -layout_mode = 2 - -[node name="TextureRect" type="TextureRect" parent="Control/CenterContainer/DragPanel/CenterContainer"] -layout_mode = 2 -texture = ExtResource("2_qvw7q") -stretch_mode = 3 - -[connection signal="mouse_entered" from="Control/CenterContainer" to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="Control/CenterContainer" to="." method="_on_mouse_exited"] diff --git a/nodes/root_node/root_node.gd b/nodes/root_node/root_node.gd deleted file mode 100644 index 12ad5d29..00000000 --- a/nodes/root_node/root_node.gd +++ /dev/null @@ -1,80 +0,0 @@ -@icon("res://ui/assets/icons/root.svg") -class_name RootNode extends MonologueGraphNode - -var characters := Property.new(LIST, {}, []) -var variables := Property.new(LIST, {}, []) - -var _character_references = [] -var _variable_references = [] - - -func _ready(): - node_type = "NodeRoot" - super._ready() - - load_character(get_parent().characters) - characters.setters["add_callback"] = add_character - characters.setters["get_callback"] = get_speakers - characters.connect("preview", load_character) - - load_variables(get_parent().variables) - variables.setters["add_callback"] = add_variable - variables.setters["get_callback"] = get_variables - variables.connect("preview", load_variables) - - -func add_character(data: Dictionary = {}) -> MonologueCharacter: - var character = MonologueCharacter.new(self) - if data: - character._from_dict(data) - character.idx.value = _character_references.size() - character.character.setters["character_index"] = character.idx.value - _character_references.append(character) - return character - - -func add_variable(data: Dictionary = {}) -> MonologueVariable: - var variable = MonologueVariable.new(self) - if data: - variable._from_dict(data) - variable.index = _variable_references.size() - _variable_references.append(variable) - return variable - - -func get_speakers(): - return _character_references - - -func get_variables(): - return _variable_references - - -## Perform initial loading of speakers and set indexes correctly. -func load_character(new_character_list: Array): - _character_references.clear() - var ascending = func(a, b): return a.get("EditorIndex") < b.get("EditorIndex") - new_character_list.sort_custom(ascending) - for speaker_data in new_character_list: - add_character(speaker_data) - - if _character_references.is_empty(): - var narrator = add_character() - narrator.character.value["Name"] = "_NARRATOR" - narrator.protected.value = true - new_character_list.append(narrator._to_dict()) - - characters.value = new_character_list - get_graph_edit().characters = new_character_list - - -func load_variables(new_variable_list: Array): - _variable_references.clear() - for variable in new_variable_list: - add_variable(variable) - variables.value = new_variable_list - get_graph_edit().variables = new_variable_list - - -func _to_fields(_dict: Dictionary) -> void: - pass # speakers and variables are stored outside of root node diff --git a/nodes/root_node/root_node.tscn b/nodes/root_node/root_node.tscn deleted file mode 100644 index 58106c43..00000000 --- a/nodes/root_node/root_node.tscn +++ /dev/null @@ -1,31 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://dqsnkyn3r76m4"] - -[ext_resource type="Script" uid="uid://csx2ec4ra5m8k" path="res://nodes/root_node/root_node.gd" id="1_e8g7i"] - -[node name="RootNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 282.0 -offset_bottom = 78.0 -size_flags_vertical = 0 -title = "RootNode" -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_e8g7i") -titlebar_color = Color(0, 0, 0, 1) -show_close_button = false - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -alignment = 1 - -[node name="Label" type="Label" parent="VBoxContainer"] -layout_mode = 2 -theme_type_variation = &"NoteLabel" -text = "Select this node to edit settings" diff --git a/nodes/sentence_node/sentence_node.gd b/nodes/sentence_node/sentence_node.gd deleted file mode 100644 index 81b29dbf..00000000 --- a/nodes/sentence_node/sentence_node.gd +++ /dev/null @@ -1,47 +0,0 @@ -@icon("res://ui/assets/icons/text.svg") -class_name SentenceNode extends MonologueGraphNode - -var speaker := Property.new(DROPDOWN, {"store_index": true}, 0) -var display_name := Property.new(LINE) -var sentence := Localizable.new(TEXT) -var voiceline := Localizable.new(FILE, {"filters": FilePicker.AUDIO}) - -@onready var _preview = $TextLabelPreview - - -func _ready(): - node_type = "NodeSentence" - sentence.connect("preview", _on_text_preview) - voiceline.setters["base_path"] = get_graph_edit().file_path - super._ready() - _update() - - -func reload_preview() -> void: - _preview.text = sentence.value - - -func _from_dict(dict: Dictionary): - # special handling for backwards compatibility v2.x - speaker.value = dict.get("SpeakerID", 0) - display_name.value = dict.get("DisplaySpeakerName", "") - voiceline.value = dict.get("VoicelinePath", "") - super._from_dict(dict) - - -func _on_text_preview(text: Variant): - _preview.text = str(text) - - -func _update(): - super._update() - - var characters: Array = get_graph_edit().characters - speaker.callers["set_items"] = [characters, "Character/Name", "EditorIndex"] - if speaker.value is String: - speaker.value = 0 - reload_preview() - - -func _get_field_groups() -> Array: - return [{"Speaker": ["speaker", "display_name"]}] diff --git a/nodes/sentence_node/sentence_node.tscn b/nodes/sentence_node/sentence_node.tscn deleted file mode 100644 index bb0b5e36..00000000 --- a/nodes/sentence_node/sentence_node.tscn +++ /dev/null @@ -1,32 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://cifg2nritn8t6"] - -[ext_resource type="Script" uid="uid://bm2we18ivulms" path="res://nodes/sentence_node/sentence_node.gd" id="1_at5jy"] - -[node name="SentenceNode" type="GraphNode" groups=["graph_nodes"]] -custom_minimum_size = Vector2(400, 100) -offset_right = 400.0 -offset_bottom = 100.0 -size_flags_vertical = 0 -title = "NodeSentence" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("1_at5jy") -titlebar_color = Color(0.835294, 0.317647, 0.376471, 1) - -[node name="TextLabelPreview" type="RichTextLabel" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 -theme_override_colors/default_color = Color(0.72549, 0.72549, 0.72549, 1) -bbcode_enabled = true -fit_content = true -scroll_following = true - -[connection signal="resize_request" from="." to="." method="_on_GraphNode_resize_request"] diff --git a/nodes/setter_node/setter_node.gd b/nodes/setter_node/setter_node.gd deleted file mode 100644 index eecf1a2a..00000000 --- a/nodes/setter_node/setter_node.gd +++ /dev/null @@ -1,63 +0,0 @@ -class_name SetterNode extends AbstractVariableNode - -var set_type := Property.new(DROPDOWN, {}, "Option") -var option_id := Property.new(LINE) -var enable := Property.new(TOGGLE) - -var _control_groups = {"Option": [option_id, enable], "Variable": [variable, operator, value]} - -@onready var _option_container = $OptionContainer -@onready var _option_id_label = $OptionContainer/OptionIdLabel -@onready var _bool_label = $OptionContainer/BoolLabel -@onready var _variable_container = $VariableContainer - - -func _ready() -> void: - node_type = "NodeSetter" - set_type.callers["set_items"] = [ - [ - {"id": 0, "text": "Option"}, - {"id": 1, "text": "Variable"}, - ] - ] - set_type.connect("preview", _show_group) - set_type.connect("preview", _update) - option_id.connect("preview", _option_id_label.set_text) - enable.connect("preview", func(e): _bool_label.text = str(e)) - - super._ready() - _show_group(set_type.value) - _update(set_type.value) - - -func get_variable_label() -> Label: - return $VariableContainer/VariableLabel - - -func get_operator_label() -> Label: - return $VariableContainer/OperatorLabel - - -func get_value_label() -> Label: - return $VariableContainer/ValueLabel - - -func _from_dict(dict: Dictionary) -> void: - record_morph(dict.get("Value")) - super._from_dict(dict) - _show_group(set_type.value) - - -func _show_group(setter_type: Variant) -> void: - for key in _control_groups.keys(): - for property in _control_groups.get(key): - property.visible = key == setter_type - - -func _update(setter_type: Variant = set_type.value) -> void: - _option_container.visible = setter_type == "Option" - _option_id_label.text = get_default_text(option_id.value, "option id") - _bool_label.text = get_default_text(enable.value, "false") - - _variable_container.visible = setter_type == "Variable" - super._update() diff --git a/nodes/setter_node/setter_node.gd.uid b/nodes/setter_node/setter_node.gd.uid deleted file mode 100644 index 461dc7a4..00000000 --- a/nodes/setter_node/setter_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8qi7r33gmf1i diff --git a/nodes/setter_node/setter_node.tscn b/nodes/setter_node/setter_node.tscn deleted file mode 100644 index da26c1f4..00000000 --- a/nodes/setter_node/setter_node.tscn +++ /dev/null @@ -1,81 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://qd3nbp87ls3r"] - -[ext_resource type="Script" uid="uid://c8qi7r33gmf1i" path="res://nodes/setter_node/setter_node.gd" id="3_3ny3s"] - -[sub_resource type="Theme" id="Theme_llmqa"] - -[node name="SetterNode" type="GraphNode" groups=["graph_nodes"]] -offset_right = 300.0 -offset_bottom = 132.0 -size_flags_horizontal = 0 -size_flags_vertical = 0 -theme = SubResource("Theme_llmqa") -title = "SetterNode" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -slot/1/left_enabled = false -slot/1/left_type = 0 -slot/1/left_color = Color(1, 1, 1, 1) -slot/1/left_icon = null -slot/1/right_enabled = false -slot/1/right_type = 0 -slot/1/right_color = Color(1, 1, 1, 1) -slot/1/right_icon = null -slot/1/draw_stylebox = true -script = ExtResource("3_3ny3s") -titlebar_color = Color(0.733333, 0.392157, 0.188235, 1) - -[node name="OptionContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="DescriptorLabel" type="Label" parent="OptionContainer"] -layout_mode = 2 -text = "Enable option" - -[node name="OptionIdLabel" type="Label" parent="OptionContainer"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "option id" - -[node name="QuestionLabel" type="Label" parent="OptionContainer"] -layout_mode = 2 -text = "?" - -[node name="BoolLabel" type="Label" parent="OptionContainer"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "value" - -[node name="VariableContainer" type="HBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="DescriptorLabel" type="Label" parent="VariableContainer"] -layout_mode = 2 -text = "Set" - -[node name="VariableLabel" type="Label" parent="VariableContainer"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "variable" - -[node name="OperatorLabel" type="Label" parent="VariableContainer"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "operator" - -[node name="ValueLabel" type="Label" parent="VariableContainer"] -layout_mode = 2 -size_flags_horizontal = 4 -theme_type_variation = &"NodeValue" -text = "value" diff --git a/nodes/wait_node/wait_node.gd b/nodes/wait_node/wait_node.gd deleted file mode 100644 index f54034cc..00000000 --- a/nodes/wait_node/wait_node.gd +++ /dev/null @@ -1,20 +0,0 @@ -class_name WaitNode extends MonologueGraphNode - -var time := Property.new(SPINBOX, {"minimum": 0, "maximum": 120}) - -@onready var wait_label := $HBox/WaitLabel - - -func _ready() -> void: - node_type = "NodeWait" - super._ready() - time.connect("preview", _on_wait_preview) - - -func _on_wait_preview(value: Variant): - wait_label.text = str(int(value)) - - -func _update(): - wait_label.text = str(int(time.value)) - super._update() diff --git a/nodes/wait_node/wait_node.gd.uid b/nodes/wait_node/wait_node.gd.uid deleted file mode 100644 index 7ffcd60a..00000000 --- a/nodes/wait_node/wait_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b804pp3yjytjr diff --git a/nodes/wait_node/wait_node.tscn b/nodes/wait_node/wait_node.tscn deleted file mode 100644 index 8ee49b91..00000000 --- a/nodes/wait_node/wait_node.tscn +++ /dev/null @@ -1,44 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://bxu71eq40qoq6"] - -[ext_resource type="Script" uid="uid://b804pp3yjytjr" path="res://nodes/wait_node/wait_node.gd" id="3_n44rg"] - -[sub_resource type="LabelSettings" id="LabelSettings_82fue"] -font_color = Color(0.572549, 0.572549, 0.572549, 1) - -[node name="WaitNode" type="GraphNode" groups=["graph_nodes"]] -custom_minimum_size = Vector2(200, 0) -offset_right = 200.0 -offset_bottom = 82.0 -size_flags_vertical = 0 -title = "NodeWait" -slot/0/left_enabled = true -slot/0/left_type = 0 -slot/0/left_color = Color(1, 1, 1, 1) -slot/0/left_icon = null -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color(1, 1, 1, 1) -slot/0/right_icon = null -slot/0/draw_stylebox = false -script = ExtResource("3_n44rg") -titlebar_color = Color(0.360784, 0.501961, 0.184314, 1) - -[node name="HBox" type="HBoxContainer" parent="."] -layout_mode = 2 -mouse_filter = 2 -theme_type_variation = &"HBoxContainer_Small" - -[node name="Label" type="Label" parent="HBox"] -layout_mode = 2 -text = "Wait" - -[node name="WaitLabel" type="Label" parent="HBox"] -layout_mode = 2 -theme_type_variation = &"NodeValue" -text = "0" - -[node name="Label2" type="Label" parent="HBox"] -layout_mode = 2 -text = "seconds" - -[connection signal="resize_request" from="." to="." method="_on_GraphNode_resize_request"] \ No newline at end of file diff --git a/project.godot b/project.godot index 3cb34d31..0751845e 100644 --- a/project.godot +++ b/project.godot @@ -8,9 +8,9 @@ config_version=5 -[addons] +[animation] -markdown_book/docs_folder="" +compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] @@ -20,33 +20,38 @@ config/tags=PackedStringArray("monologue") run/main_scene="res://scenes/splash/splash.tscn" run/print_header=false run/enable_alt_space_menu=true -config/features=PackedStringArray("4.4") +config/features=PackedStringArray("4.7") boot_splash/bg_color=Color(0.117647, 0.117647, 0.129412, 1) boot_splash/show_image=false -config/icon="res://icon.png" +config/icon="uid://bou7qelcr5dwn" [autoload] -Constants="*res://autoloads/constants.gd" +EventBus="*res://autoloads/event_bus.gd" +Log="*uid://bstbvfwoltk8n" App="*res://autoloads/app.gd" -GlobalVariables="*res://autoloads/global_variables.gd" -GlobalSignal="*res://autoloads/global_signal.gd" -Util="*res://autoloads/util.gd" -SfxLoader="*res://autoloads/sfx_loader.gd" -Path="*res://autoloads/path.gd" -IDGen="*res://autoloads/id_gen.gd" -Cursor="*res://autoloads/cursor.gd" -ImageLoader="*res://autoloads/image_loader.gd" +ConfigManager="*uid://b5ifbbjetj5cm" +ProjectManager="*res://autoloads/project_manager.gd" +FieldBucket="*res://common/fields/field_bucket.gd" +NodeBucket="*res://common/nodes/node_bucket.gd" +CollectionBucket="*uid://dl72pka6rakjc" +ImageLoader="res://autoloads/image_loader.gd" +SfxLoader="res://autoloads/sfx_loader.gd" +Tooltip="*uid://dv715qjt3315j" [debug] +gdscript/warnings/directory_rules={ +"res://addons": 0, +"res://addons/monologue": 1 +} +gdscript/warnings/untyped_declaration=1 gdscript/warnings/integer_division=0 [display] window/size/viewport_width=1400 window/size/viewport_height=800 -window/size/resizable=false window/size/transparent=true window/size/extend_to_title=true window/energy_saving/keep_screen_on=false @@ -55,7 +60,7 @@ window/size/fullscreen=true [editor_plugins] -enabled=PackedStringArray("res://addons/Todo_Manager/plugin.cfg", "res://addons/gdUnit4/plugin.cfg", "res://addons/monologue/plugin.cfg") +enabled=PackedStringArray("res://addons/beautify_code_on_save/plugin.cfg", "res://addons/gdUnit4/plugin.cfg") [file_customization] @@ -75,72 +80,101 @@ ui/inspector/tree_view_mode=1 [gui] -theme/custom="uid://budfhk1hudcfj" -theme/custom_font="uid://xxjublydw27h" +timers/tooltip_delay_sec=5.0 theme/default_font_hinting=2 +theme/default_font_multichannel_signed_distance_field=true theme/lcd_subpixel_layout=0 +theme/custom="uid://c4i457ttn1u4q" + +[importer_defaults] + +font_data_dynamic={ +"Compress": null, +"Fallbacks": null, +"Rendering": null, +"allow_system_fallback": true, +"antialiasing": 1, +"compress": true, +"disable_embedded_bitmaps": true, +"fallbacks": [], +"force_autohinter": false, +"generate_mipmaps": false, +"hinting": 1, +"keep_rounding_remainders": true, +"language_support": {}, +"modulate_color_glyphs": false, +"msdf_pixel_range": 8, +"msdf_size": 48, +"multichannel_signed_distance_field": false, +"opentype_features": {}, +"oversampling": 2.0, +"preload": [{ +"chars": [], +"glyphs": [], +"name": "Nouvelle configuration", +"size": Vector2i(16, 0), +&"variation_embolden": 0.0 +}], +"script_support": {}, +"subpixel_positioning": 4 +} [input] -Save={ +mnl_save={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } -Undo={ +mnl_undo={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":90,"physical_keycode":90,"key_label":90,"unicode":0,"location":0,"echo":false,"script":null) ] } -Redo={ +mnl_redo={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":90,"physical_keycode":90,"key_label":90,"unicode":0,"location":0,"echo":false,"script":null) ] } -Delete={ +mnl_delete={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194312,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } -Select={ +mnl_select={ "deadzone": 0.5, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(377, 21),"global_position":Vector2(386, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) ] } Spacebar={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) -] -} -"Show searchbar"={ -"deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) ] } -Exit={ +mnl_exit={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194335,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } -"Add node"={ +mnl_add_node={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"key_label":0,"unicode":65,"location":0,"echo":false,"script":null) ] } -Ctrl={ +mnl_ctrl={ "deadzone": 0.2, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194326,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } -Alt={ +mnl_alt={ "deadzone": 0.2, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194328,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194328,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_continue={ "deadzone": 0.2, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) ] } ui_mouse_continue={ @@ -148,6 +182,36 @@ ui_mouse_continue={ "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(142, 11),"global_position":Vector2(151, 57),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) ] } +mnl_open={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":79,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +mnl_save_as={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +mnl_new={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":78,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +mnl_regenerate_theme={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":73,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +mnl_graph_snap={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +mnl_graph_show_grid={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":71,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [physics] diff --git a/reports/report_1/css/breadcrumb.css b/reports/report_1/css/breadcrumb.css new file mode 100644 index 00000000..17215ff2 --- /dev/null +++ b/reports/report_1/css/breadcrumb.css @@ -0,0 +1,66 @@ +.breadcrumb { + display: flex; + border-radius: 6px; + overflow: hidden; + height: 45px; + z-index: 1; + background-color: #9d73eb; + margin-top: 0px; + margin-bottom: 10px; + box-shadow: 0 0 3px black; +} + +.breadcrumb a { + position: relative; + display: flex; + -ms-flex-positive: 1; + flex-grow: 1; + text-decoration: none; + margin: auto; + height: 100%; + color: white; +} + +.breadcrumb a:first-child { + padding-left: 5.2px; +} + +.breadcrumb a:last-child { + padding-right: 5.2px; +} + +.breadcrumb a:after { + content: ""; + position: absolute; + display: inline-block; + width: 45px; + height: 45px; + top: 0; + right: -20px; + background-color: #9d73eb; + border-top-right-radius: 5px; + transform: scale(0.707) rotate(45deg); + box-shadow: 2px -2px rgba(0, 0, 0, 0.25); + z-index: 1; +} + +.breadcrumb a:last-child:after { + content: none; +} + +.breadcrumb a.active, +.breadcrumb a:hover { + background: #b899f2; + color: white; + text-decoration: underline; +} + +.breadcrumb a.active:after, +.breadcrumb a:hover:after { + background: #b899f2; +} + +.breadcrumb span { + margin: inherit; + z-index: 2; +} diff --git a/reports/report_1/css/logo.png b/reports/report_1/css/logo.png new file mode 100644 index 00000000..12de79ff Binary files /dev/null and b/reports/report_1/css/logo.png differ diff --git a/reports/report_1/css/logo.png.import b/reports/report_1/css/logo.png.import new file mode 100644 index 00000000..24910f13 --- /dev/null +++ b/reports/report_1/css/logo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddqywce3as3ev" +path="res://.godot/imported/logo.png-827919d13f1ae3f8ce594e95709add8e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://reports/report_1/css/logo.png" +dest_files=["res://.godot/imported/logo.png-827919d13f1ae3f8ce594e95709add8e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/reports/report_1/css/styles.css b/reports/report_1/css/styles.css new file mode 100644 index 00000000..e92d59b7 --- /dev/null +++ b/reports/report_1/css/styles.css @@ -0,0 +1,475 @@ +html, +body { + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + font-family: sans-serif; + background-color: white; + height: 100%; +} + +main { + flex-grow: 1; + overflow: auto; + margin: 0 10em; +} + + +header { + color: white; + padding: 1px; + position: relative; + background-image: linear-gradient(to bottom right, #8058e3, #9d73eb); +} + +.logo { + position: fixed; + top: 20px; + left: 20px; + display: flex; + align-items: center; + z-index: 1000; + filter: grayscale(1); + mix-blend-mode: plus-lighter; +} + +.logo img { + width: 64px; + height: 64px; +} + +.logo span { + font-size: 1.2em; + color: lightslategray; +} + +.report-container { + margin: 0 15em; + text-align: center; + margin-top: 60px; + flex-grow: 0; +} + +h1 { + margin: 0 0 20px 0; + font-size: 2.5em; + font-weight: normal; +} + +.summary { + display: inline-flex; + justify-content: center; + flex-wrap: nowrap; + margin-bottom: 20px; + align-items: baseline; + max-width: 960px; +} + +.summary-item { + flex: 1; + min-width: 80px; +} + +.label { + font-size: 1em; + flex-wrap: nowrap; +} + +.value { + font-size: 0.9em; + display: block; + padding-top: 10px; + color: lightgray; +} + +.success-rate { + padding-left: 40px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.check-icon { + background-color: #34c538; + color: white; + width: 48px; + height: 48px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.4em; +} + +.rate-text { + text-align: center; + flex-wrap: nowrap; +} + +.percentage { + font-size: 1.2em; + font-weight: bold; +} + + +nav { + padding: 20px 0px; + font-family: monospace; +} + +nav ul { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + justify-content: flex-start; + border-bottom: 1px solid lightgray; +} + +nav li { + cursor: pointer; + padding: 5px 20px; + font-size: 1.1em; + color: lightslategray; +} + +nav li.active { + color: darkslategray; + border-bottom: 1px solid darkslategray; + font-weight: bold; +} + +div#content { + height: calc(100vh - 400px); +} + + +table { + width: 100%; + height: 100%; + border-collapse: collapse; + overflow: hidden; +} + +thead th { + position: sticky; + top: 0; + background-color: white; + z-index: 1; + border-bottom: 2px solid #ddd; +} + +tbody { + display: block; + /* Limit the height of the table body */ + max-height: calc(100vh - 400px); + /* Enable scrolling on the table body */ + overflow-y: auto; +} + +thead, +tbody tr { + display: table; + width: 100%; + table-layout: fixed; +} + +tbody td { + overflow: hidden; +} + +/* Ensure scrollbar visibility */ +tbody::-webkit-scrollbar { + height: 4px; + width: 14px; +} + +tbody::-webkit-scrollbar-thumb { + background-color: #aaa6a6; + border-radius: 4px; +} + +tbody::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +th, +td { + font-size: .9em; + padding: 5px 0px; + border-bottom: 1px solid #eee; + color: lightslategrey; + text-align: left; + text-wrap: nowrap; + /* Default max and min width for all columns */ + max-width: 150px; + min-width: 80px; + width: 80px; +} + +th { + font-size: 1em; + font-weight: normal; + padding-top: 20px; + color: gray; + text-wrap: nowrap; +} + +.tab-report { + display: grid; + grid-template-columns: 100%; + margin-bottom: 20px; +} + +.tab-report-grid { + display: grid; + grid-template-columns: 70% 30%; + margin-bottom: 20px; +} + + +/* Specific styling for the first column (Testcase) */ +th:first-child, +td:first-child { + padding-left: 5px; + text-align: left; + /* Max width for the first column */ + min-width: 249px; + width: 250px; + /* Enable scrollbar if content exceeds max-width */ + white-space: nowrap; + overflow: auto; +} + +/* Scrollbar styles for first column */ +td:first-child { + overflow-x: auto; + text-overflow: initial; +} + +/* Scrollbar appearance */ +td:first-child::-webkit-scrollbar { + height: 6px; +} + +td:first-child::-webkit-scrollbar-thumb { + background-color: #888; + border-radius: 10px; +} + +td:first-child::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +/* Max width for Result column */ +th:nth-child(2), +td:nth-child(2) { + max-width: 140px; + min-width: 140px; + width: 140px; +} + +/* Max width for Quick Results column */ +th:nth-child(9), +td:nth-child(9) { + max-width: 140px; + min-width: 140px; + width: 140px; + padding-right: 10px; +} + +/* Background color for alternating groups */ +.group-bg-1 { + background-color: #f1f1f1; +} + +.group-bg-2 { + background-color: #e0e0e0; +} + +.grid-item { + overflow: auto; + padding-left: 20px; + color: lightslategrey; + max-height: calc(100vh - 350px); +} + +div.tab td.report-column, +th.report-column { + display: none; +} + +/* Result status styles */ +.status { + padding: 2px 40px; + border-radius: 6px; + color: black; + width: 40px; + display: flex; + align-content: center; + align-items: center; +} + +.status-bar { + display: flex; + border-radius: 8px; + overflow: hidden; + height: 20px; + flex-wrap: nowrap; + justify-content: space-evenly; +} + +.status-bar-column { + margin: -2px; + color: black; + display: flex; + align-content: center; + align-items: center; + transition: width 0.3s ease; +} + +.status-skipped { + background-color: #888888; +} + +.status-passed { + background-color: #63bb38; +} + +.status-error { + background-color: #fd1100; +} + +.status-failed { + background-color: #ed594f; +} + +.status-flaky { + background-color: #1d9a1f; +} + +.status-warning { + background-color: #fdda3f; +} + +div.tab tr:hover { + background-color: #d9e7fa; + box-shadow: 0 0 5px black; +} + +div.tab tr.selected { + background-color: #d9e7fa; +} + +div.report-column { + margin-top: 10px; + width: 100%; + text-align: left; +} + +.logging-container { + width: 100%; + height: 100%; +} + +div.godot-report-frame { + margin: 10px; + font-family: monospace; + height: 100%; + background-color: #eee; +} + +div.include-footer { + position: fixed; + bottom: 0; + width: 100%; + display: flex; +} + +footer { + position: static; + left: 0; + bottom: 0; + width: 100%; + white-space: nowrap; + color: lightgray; + font-size: 12px; + background-image: linear-gradient(to bottom right, #8058e3, #9d73eb); + display: flex; + justify-content: space-between; + align-items: center; +} + +footer p { + padding-left: 10em; +} + +footer .status-legend { + display: flex; + gap: 15px; + width: 500px; +} + +footer a { + color: lightgray; +} + +footer a:hover { + color: whitesmoke; +} + +footer a:visited { + color: whitesmoke; +} + +.status-legend-item { + display: flex; + align-items: center; + gap: 5px; +} + +.status-box { + width: 15px; + height: 15px; + border-radius: 3px; + display: inline-block; +} + +/* Normal link */ +a { + color: lightslategrey; +} + +/* Link when hovered */ +a:hover { + color: #9d73eb; +} + +/* Visited link */ +a:visited { + color: #8058e3; +} + +/* Active link (while being clicked) */ +a:active { + color: #8058e3; + /* Custom color when link is clicked */ +} + + +@media (max-width: 1024px) { + .summary { + flex-direction: column; + } + + nav ul { + flex-wrap: wrap; + } + + nav li { + margin-right: 10px; + margin-bottom: 5px; + } +} diff --git a/reports/report_1/index.html b/reports/report_1/index.html new file mode 100644 index 00000000..69fb529a --- /dev/null +++ b/reports/report_1/index.html @@ -0,0 +1,164 @@ + + + + + + + GdUnit4 Report + + + + +
+ +
+

Summary Report

+
+
+ Test Suites + 0 +
+
+ Tests + 0 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 0ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+
+
+ +
+ +
+
+ +
+

Generated by GdUnit4 at 2026-07-10 12:38:50

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + + + diff --git a/reports/report_1/path/unit.html b/reports/report_1/path/unit.html new file mode 100644 index 00000000..e2efebae --- /dev/null +++ b/reports/report_1/path/unit.html @@ -0,0 +1,143 @@ + + + + + + + + + GdUnit4 Testsuite + + + + + +
+ +
+

Report by Paths

+
+ res://unit/ +
+
+
+ TestSuites + 1 +
+
+ Tests + 1 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 16ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+ +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestSuitesResultTestsSkippedFlakyFailuresOrphansDurationSuccess rate
test_fieldsPASSED1000016ms +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+

Generated by GdUnit4 at 2026-06-17 00:14:29

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + diff --git a/reports/report_1/results.xml b/reports/report_1/results.xml new file mode 100644 index 00000000..74a7c744 --- /dev/null +++ b/reports/report_1/results.xml @@ -0,0 +1,3 @@ + + + diff --git a/reports/report_1/test_suites/unit.test_dropdown_field.html b/reports/report_1/test_suites/unit.test_dropdown_field.html new file mode 100644 index 00000000..a024f814 --- /dev/null +++ b/reports/report_1/test_suites/unit.test_dropdown_field.html @@ -0,0 +1,218 @@ + + + + + + + + + GdUnit4 Testsuite + + + + + + + + +
+ +
+

Testsuite Report

+
+ res://unit/test_dropdown_field.gd +
+
+
+ Tests + 3 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 35ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestcaseResultSkippedOrphansDurationReport
test_dropdown_field_existsPASSED005ms +
+
+										
+
test_sentence_node_speaker_is_dropdownPASSED008ms +
+
+										
+
test_dropdown_has_source_settingPASSED0013ms +
+
+										
+
+
+
+

Failure Report

+
+
+
+
+ +
+

Generated by GdUnit4 at 2026-06-17 00:12:05

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + + + diff --git a/reports/report_1/test_suites/unit.test_fields.html b/reports/report_1/test_suites/unit.test_fields.html new file mode 100644 index 00000000..0f5e3010 --- /dev/null +++ b/reports/report_1/test_suites/unit.test_fields.html @@ -0,0 +1,190 @@ + + + + + + + + + GdUnit4 Testsuite + + + + + + + + +
+ +
+

Testsuite Report

+
+ res://unit/test_fields.gd +
+
+
+ Tests + 1 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 16ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
TestcaseResultSkippedOrphansDurationReport
test_create_all_fieldsPASSED009ms +
+
+										
+
+
+
+

Failure Report

+
+
+
+
+ +
+

Generated by GdUnit4 at 2026-06-17 00:14:29

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + + + diff --git a/reports/report_1/test_suites/unit.test_node_title_property.html b/reports/report_1/test_suites/unit.test_node_title_property.html new file mode 100644 index 00000000..d3ffc4b8 --- /dev/null +++ b/reports/report_1/test_suites/unit.test_node_title_property.html @@ -0,0 +1,232 @@ + + + + + + + + + GdUnit4 Testsuite + + + + + + + + +
+ +
+

Testsuite Report

+
+ res://unit/test_node_title_property.gd +
+
+
+ Tests + 4 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 48ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestcaseResultSkippedOrphansDurationReport
test_sentence_node_main_propertyPASSED002ms +
+
+										
+
test_text_node_main_propertyPASSED0013ms +
+
+										
+
test_root_node_main_propertyPASSED0014ms +
+
+										
+
test_connection_trackingPASSED0013ms +
+
+										
+
+
+
+

Failure Report

+
+
+
+
+ +
+

Generated by GdUnit4 at 2026-03-03 13:35:01

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + + + diff --git a/reports/report_1/test_suites/unit.test_storyline_properties.html b/reports/report_1/test_suites/unit.test_storyline_properties.html new file mode 100644 index 00000000..961ce2af --- /dev/null +++ b/reports/report_1/test_suites/unit.test_storyline_properties.html @@ -0,0 +1,204 @@ + + + + + + + + + GdUnit4 Testsuite + + + + + + + + +
+ +
+

Testsuite Report

+
+ res://unit/test_storyline_properties.gd +
+
+
+ Tests + 2 +
+
+ Skipped + 0 +
+
+ Flaky + 0 +
+
+ Failures + 0 +
+
+ Orphans + 0 +
+
+ Duration + 179ms +
+
+
+
+ Success Rate + 100% +
+
+
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestcaseResultSkippedOrphansDurationReport
test_can_add_character_to_storylinePASSED00162ms +
+
+										
+
test_can_add_variable_to_storylinePASSED0015ms +
+
+										
+
+
+
+

Failure Report

+
+
+
+
+ +
+

Generated by GdUnit4 at 2026-03-03 13:39:56

+
+ + Skipped + + + Passed + + + Flaky + + + Warning + + + Failed + + + Error + +
+
+ + + + + diff --git a/scenes/editor/editor.gd b/scenes/editor/editor.gd new file mode 100644 index 00000000..3e1be9aa --- /dev/null +++ b/scenes/editor/editor.gd @@ -0,0 +1,105 @@ +class_name MonologueEditor extends Control + +const STORYLINE_EXTENSIONS: Array = ["*.mnlg,*.json;Storyline Document"] + +@export var welcome_window: WelcomeWindow +@export var graph_container: GraphContainer +@export var file_dialog: GlobalFileDialog + +@onready var graph_node_picker: GraphNodePicker = %GraphNodePicker +@onready var inspector_panel_node: InspectorPanel = %Inspector + + +func _ready() -> void: + get_tree().auto_accept_quit = false + + DisplayServer.window_set_drop_files_callback(_on_window_drop_file) + EventBus.add_graph_node.connect(add_node_from_global) + EventBus.select_new_node.connect(_select_new_node) + EventBus.test_trigger.connect(test_project) + + var args: PackedStringArray = OS.get_cmdline_args() + for arg: String in args: + if arg.ends_with(".mnlp") and FileAccess.file_exists(arg): + Log.warn("Attempt to open mlnp file on startup.") + + ProjectManager.load_project(MonologueProject.new()) + + +func _select_new_node() -> void: + graph_node_picker.open_for_node("", -1, null, null, null, true) + + +#func _input(event: InputEvent) -> void: + #if event.is_action_pressed("Save"): + #ProjectManager.current_project.save() + + #if event.is_action_pressed("ui_undo"): + #var focus_owner: Control = get_viewport().gui_get_focus_owner() + #if focus_owner: + #focus_owner.release_focus() + #ProjectManager.current_project.command_manager.undo() +# + #if event.is_action_pressed("ui_redo"): + #ProjectManager.current_project.command_manager.redo() + + +## Function callback for when th e user wants to add a node from global context. +## Used by header menu and graph node selector (picker). +func add_node_from_global(node_type: String, picker: GraphNodePicker = null) -> void: + var storyline_id: String = graph_container.graph.storyline_id + var storyline: StorylineDocument = ProjectManager.current_project.get_storyline(storyline_id) + + if storyline == null: + Log.error("No active storyline available to add node.") + return + + var node: InspectableNode = storyline.create_node(node_type) + if node == null: + Log.error("Unable to create node of type '%s'." % node_type) + return + + var graph_edit: MonologueGraphEdit = graph_container.graph + var target_position: Vector2 = Vector2.ZERO + if picker and picker.graph_release is Vector2: + target_position = picker.graph_release + else: + target_position = graph_edit.scroll_offset / graph_edit.zoom + + var position_property: Property = node.get_property("position") + if position_property: + position_property.set_value(target_position) + + var command: AddNodesCommand = AddNodesCommand.new(storyline.id, [node]) + storyline.history.execute(command) + graph_edit.refresh() + + +func load_project(path: String) -> void: + ProjectManager.load_project_from_path(path) + + +func test_project(_from_node: Variant = null) -> void: + # TODO: Implement run/test functionality + pass + + +func _notification(what: int) -> void: + if what == NOTIFICATION_WM_CLOSE_REQUEST: + get_viewport().gui_release_focus() + + if await ProjectManager.close_current_project(): + get_tree().quit() + + +func _on_window_drop_file(paths: PackedStringArray) -> void: + Log.info("%s file(s) have been dragged into the editor." % paths.size()) + if paths.size() > 1: + Log.warn("Can't open multiple files.") + var path: String = paths[0] + + if not path.ends_with(".%s" % MonologueProject.FILE_FORMAT): + Log.error("Can't open file at '%s'. (wrong file format)" % path) + return + + ProjectManager.load_project_from_path(path) diff --git a/scenes/main/monologue_control.gd.uid b/scenes/editor/editor.gd.uid similarity index 100% rename from scenes/main/monologue_control.gd.uid rename to scenes/editor/editor.gd.uid diff --git a/scenes/editor/editor.tscn b/scenes/editor/editor.tscn new file mode 100644 index 00000000..ea8e7946 --- /dev/null +++ b/scenes/editor/editor.tscn @@ -0,0 +1,227 @@ +[gd_scene format=3 uid="uid://bqjfdabrxujp7"] + +[ext_resource type="Script" uid="uid://q6eg6rid6xqd" path="res://scenes/editor/editor.gd" id="1_ovo1o"] +[ext_resource type="Script" uid="uid://rb4fwghqhreo" path="res://common/ui/menu_buttons/main_view_button.gd" id="2_hg2ka"] +[ext_resource type="Script" uid="uid://0vqgemjkhdc2" path="res://common/ui/menu_buttons/main_file_button.gd" id="2_oljq2"] +[ext_resource type="PackedScene" uid="uid://cb3se7h7akt47" path="res://common/ui/language_switcher/language_switcher.tscn" id="2_p4u40"] +[ext_resource type="Script" uid="uid://cate5mgxkgmlh" path="res://common/ui/menu_buttons/main_edit_button.gd" id="3_oljq2"] +[ext_resource type="Texture2D" uid="uid://b272tbdmvxj20" path="res://ui/assets/icons/play.svg" id="3_syr16"] +[ext_resource type="Script" uid="uid://creja611rnsop" path="res://common/ui/project_panel/project_panel.gd" id="4_syr16"] +[ext_resource type="Script" uid="uid://b8tpwk6h2qmhc" path="res://common/ui/menu_buttons/main_help_button.gd" id="5_j530m"] +[ext_resource type="Script" uid="uid://bw5hfxuolu4p3" path="res://common/ui/search_bar.gd" id="6_hg2ka"] +[ext_resource type="Script" uid="uid://boag5gmomlm3" path="res://common/ui/h_auto_split_container.gd" id="6_syr16"] +[ext_resource type="PackedScene" uid="uid://dqdrf7wtegye6" path="res://common/ui/console/console.tscn" id="9_oljq2"] +[ext_resource type="Script" uid="uid://dvcbv5akqxtp5" path="res://common/ui/tooltip_label.gd" id="9_pq70c"] +[ext_resource type="PackedScene" uid="uid://c8525nhvwt1y0" path="res://common/ui/graph/graph_container.tscn" id="9_qd5yd"] +[ext_resource type="Script" uid="uid://bi4poj4ni2xba" path="res://common/ui/label_memory_usage.gd" id="9_qnluh"] +[ext_resource type="Script" uid="uid://db0p0vwsnoa06" path="res://common/ui/version_label.gd" id="10_i3ywb"] +[ext_resource type="PackedScene" uid="uid://dgvhvxdrd58qp" path="res://common/ui/inspector/inspector_panel.tscn" id="14_craj7"] +[ext_resource type="Script" uid="uid://cd5ku56gijql7" path="res://common/ui/status_bar/status_bar.gd" id="14_ja8tm"] +[ext_resource type="PackedScene" uid="uid://cmpsaafag7cwl" path="res://common/ui/windows/graph_node_picker/graph_node_picker.tscn" id="15_q62vd"] + +[sub_resource type="GDScript" id="GDScript_qnluh"] +script/source = "extends Button + + +func _on_pressed() -> void: + EventBus.test_trigger.emit() +" + +[node name="MonologueEditor" type="Control" unique_id=1841160578 node_paths=PackedStringArray("graph_container")] +clip_contents = true +layout_mode = 3 +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_ovo1o") +graph_container = NodePath("VBox/MarginContainer/HBoxContainer/HSplitContainer/VSplitContainer/Graph") + +[node name="VBox" type="VBoxContainer" parent="." unique_id=212421522] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_type_variation = &"VBoxContainerCompact" + +[node name="Header" type="PanelContainer" parent="VBox" unique_id=906026547] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 +theme_type_variation = &"EditorHeader" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBox/Header" unique_id=4725878] +layout_mode = 2 +theme_type_variation = &"VBoxContainerMedium" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBox/Header/HBoxContainer" unique_id=1514409931] +layout_mode = 2 + +[node name="FileButton" type="Button" parent="VBox/Header/HBoxContainer/HBoxContainer" unique_id=1902954852] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "File" +script = ExtResource("2_oljq2") + +[node name="EditButton" type="Button" parent="VBox/Header/HBoxContainer/HBoxContainer" unique_id=1206611088] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Edit" +script = ExtResource("3_oljq2") + +[node name="ViewButton" type="Button" parent="VBox/Header/HBoxContainer/HBoxContainer" unique_id=608330261] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "View" +script = ExtResource("2_hg2ka") + +[node name="HelpButton" type="Button" parent="VBox/Header/HBoxContainer/HBoxContainer" unique_id=1637718995] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +text = "Help" +script = ExtResource("5_j530m") + +[node name="VSeparator" type="VSeparator" parent="VBox/Header/HBoxContainer" unique_id=372438984] +layout_mode = 2 +size_flags_horizontal = 4 +theme_type_variation = &"SmallVSeparator" + +[node name="LanguageSwitcher" parent="VBox/Header/HBoxContainer" unique_id=348986612 instance=ExtResource("2_p4u40")] +custom_minimum_size = Vector2(0, 0) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Switch language" + +[node name="RunButton" type="Button" parent="VBox/Header/HBoxContainer" unique_id=927762726] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Run project" +theme_type_variation = &"IconButton" +icon = ExtResource("3_syr16") +script = SubResource("GDScript_qnluh") + +[node name="MarginContainer" type="MarginContainer" parent="VBox" unique_id=1332047674] +layout_mode = 2 +size_flags_vertical = 3 +theme_type_variation = &"MediumMarginContainer" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBox/MarginContainer" unique_id=1373149190] +layout_mode = 2 +theme_type_variation = &"HBoxContainerLarge" + +[node name="ProjectPanel" type="PanelContainer" parent="VBox/MarginContainer/HBoxContainer" unique_id=1336539805] +clip_contents = true +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +theme_type_variation = &"EditorPanel" +script = ExtResource("4_syr16") + +[node name="VBox" type="VBoxContainer" parent="VBox/MarginContainer/HBoxContainer/ProjectPanel" unique_id=1441810060] +layout_mode = 2 + +[node name="Header" type="HBoxContainer" parent="VBox/MarginContainer/HBoxContainer/ProjectPanel/VBox" unique_id=1375920705] +layout_mode = 2 + +[node name="SearchBar" type="LineEdit" parent="VBox/MarginContainer/HBoxContainer/ProjectPanel/VBox/Header" unique_id=1007037601] +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("6_hg2ka") + +[node name="ProjectExplorer" type="VBoxContainer" parent="VBox/MarginContainer/HBoxContainer/ProjectPanel/VBox" unique_id=375721155] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HSplitContainer" type="HSplitContainer" parent="VBox/MarginContainer/HBoxContainer" unique_id=109031884] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/minimum_grab_thickness = 18 +split_offsets = PackedInt32Array(902) +drag_area_highlight_in_editor = true +split_offset = 902 +script = ExtResource("6_syr16") + +[node name="VSplitContainer" type="VSplitContainer" parent="VBox/MarginContainer/HBoxContainer/HSplitContainer" unique_id=1564704533] +layout_mode = 2 +split_offsets = PackedInt32Array(510) +split_offset = 510 + +[node name="Graph" parent="VBox/MarginContainer/HBoxContainer/HSplitContainer/VSplitContainer" unique_id=116496835 node_paths=PackedStringArray("inspector_panel") instance=ExtResource("9_qd5yd")] +layout_mode = 2 +inspector_panel = NodePath("../../Inspector") + +[node name="Console" parent="VBox/MarginContainer/HBoxContainer/HSplitContainer/VSplitContainer" unique_id=588975871 instance=ExtResource("9_oljq2")] +layout_mode = 2 + +[node name="Inspector" parent="VBox/MarginContainer/HBoxContainer/HSplitContainer" unique_id=667809004 instance=ExtResource("14_craj7")] +unique_name_in_owner = true +clip_contents = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"EditorPanel" + +[node name="RunButton" parent="VBox/MarginContainer/HBoxContainer/HSplitContainer/Inspector/VBox/Header/SearchBar" parent_id_path=PackedInt32Array(667809004, 711294994) index="2" unique_id=62143223] +tooltip_text = "Run project from this node" +theme_type_variation = &"IconButton" + +[node name="StatusBar" type="PanelContainer" parent="VBox" unique_id=1614990059] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 +theme_type_variation = &"EditorHeader" +script = ExtResource("14_ja8tm") + +[node name="Left" type="HBoxContainer" parent="VBox/StatusBar" unique_id=246129797] +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="TooltipLabel" type="Label" parent="VBox/StatusBar/Left" unique_id=1184545247] +layout_mode = 2 +size_flags_horizontal = 0 +theme_type_variation = &"NoteLabel" +script = ExtResource("9_pq70c") + +[node name="Right" type="HBoxContainer" parent="VBox/StatusBar" unique_id=27361139] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +theme_type_variation = &"EditorHeader" +alignment = 2 + +[node name="MemoryUsageLabel" type="Label" parent="VBox/StatusBar/Right" unique_id=2113438421] +layout_mode = 2 +mouse_filter = 0 +theme_type_variation = &"NoteLabel" +text = "Memory: 40.0 MB" +script = ExtResource("9_qnluh") + +[node name="VSeparator" type="VSeparator" parent="VBox/StatusBar/Right" unique_id=1516301607] +layout_mode = 2 +theme_type_variation = &"SmallVSeparator" + +[node name="VersionLabel" type="Label" parent="VBox/StatusBar/Right" unique_id=2049826380] +layout_mode = 2 +theme_type_variation = &"NoteLabel" +text = "3.0.0" +script = ExtResource("10_i3ywb") + +[node name="GraphNodePicker" parent="." unique_id=1110402275 instance=ExtResource("15_q62vd")] +unique_name_in_owner = true +visible = false +keep_title_visible = false +content_scale_mode = 2 +content_scale_aspect = 1 + +[connection signal="pressed" from="VBox/Header/HBoxContainer/RunButton" to="VBox/Header/HBoxContainer/RunButton" method="_on_pressed"] + +[editable path="VBox/MarginContainer/HBoxContainer/HSplitContainer/Inspector"] diff --git a/scenes/main/main_menu.gd b/scenes/editor/main_menu.gd similarity index 96% rename from scenes/main/main_menu.gd rename to scenes/editor/main_menu.gd index 75070d82..ac466a0e 100644 --- a/scenes/main/main_menu.gd +++ b/scenes/editor/main_menu.gd @@ -1,6 +1,6 @@ extends MenuButton -@onready var search_icon = preload("res://ui/assets/icons/search.svg") +@onready var search_icon: Texture2D = preload("res://ui/assets/icons/search.svg") func _ready() -> void: diff --git a/scenes/main/main_menu.gd.uid b/scenes/editor/main_menu.gd.uid similarity index 100% rename from scenes/main/main_menu.gd.uid rename to scenes/editor/main_menu.gd.uid diff --git a/scenes/main/add_node_button.gd b/scenes/main/add_node_button.gd deleted file mode 100644 index 5fdbffe9..00000000 --- a/scenes/main/add_node_button.gd +++ /dev/null @@ -1,5 +0,0 @@ -extends Button - - -func _on_pressed() -> void: - GlobalSignal.emit("enable_picker_mode", ["", -1, null, null, null, true]) diff --git a/scenes/main/add_node_button.gd.uid b/scenes/main/add_node_button.gd.uid deleted file mode 100644 index 1c52ade2..00000000 --- a/scenes/main/add_node_button.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bmku341x5gaoe diff --git a/scenes/main/app.tscn b/scenes/main/app.tscn index 4263e148..1cdd043c 100644 --- a/scenes/main/app.tscn +++ b/scenes/main/app.tscn @@ -1,24 +1,14 @@ -[gd_scene load_steps=14 format=3 uid="uid://bim51g1aibuw0"] +[gd_scene format=3 uid="uid://bim51g1aibuw0"] -[ext_resource type="Texture2D" uid="uid://dd8v3hpxxk33d" path="res://ui/assets/icons/logo_white.svg" id="1_1u5fg"] -[ext_resource type="PackedScene" uid="uid://bqjfdabrxujp7" path="res://scenes/main/graph.tscn" id="1_kiov6"] -[ext_resource type="Texture2D" uid="uid://hlck6y4i3l5q" path="res://ui/assets/icons/plus.svg" id="3_gcdaj"] -[ext_resource type="Script" uid="uid://m1wxne1g6kju" path="res://scenes/main/main_menu.gd" id="4_f1t0i"] -[ext_resource type="Shader" uid="uid://de1ql8ahad2ts" path="res://common/blur.gdshader" id="5_1u5fg"] -[ext_resource type="PackedScene" uid="uid://bcs6s2yuf374j" path="res://common/layouts/expanded_text_edit/expanded_text_edit_container.tscn" id="6_cqyrh"] -[ext_resource type="Script" uid="uid://d0xgy6gflipxs" path="res://common/layouts/dimmer/dimmer.gd" id="6_nw038"] -[ext_resource type="PackedScene" uid="uid://c7cf7rfwnhf77" path="res://common/layouts/character_edit/character_edit.tscn" id="7_7qdje"] -[ext_resource type="PackedScene" uid="uid://cmpsaafag7cwl" path="res://common/windows/graph_node_picker/graph_node_picker.tscn" id="7_nw038"] -[ext_resource type="PackedScene" uid="uid://d3f7d4bb40iht" path="res://common/windows/preview_window/preview_window.tscn" id="8_7qdje"] -[ext_resource type="PackedScene" uid="uid://bqqcww601rcx5" path="res://common/windows/welcome_window/welcome_window.tscn" id="9_fp7qq"] -[ext_resource type="Script" uid="uid://b2l7hjfyrnr3x" path="res://common/windows/file_dialog/file_dialog.gd" id="10_r77hi"] +[ext_resource type="PackedScene" uid="uid://bqjfdabrxujp7" path="res://scenes/editor/editor.tscn" id="1_kiov6"] +[ext_resource type="Script" uid="uid://ck2cqoxpk1mto" path="res://scenes/main/dimmer.gd" id="2_y0d3j"] +[ext_resource type="PackedScene" uid="uid://bcs6s2yuf374j" path="res://common/ui/expanded_text_edit/expanded_text_edit_container.tscn" id="6_cqyrh"] +[ext_resource type="PackedScene" uid="uid://bkreq3xdr7gxw" path="res://common/ui/windows/prompt_window/prompt_window.tscn" id="7_k15gm"] +[ext_resource type="PackedScene" uid="uid://d3f7d4bb40iht" path="res://common/ui/windows/preview_window/preview_window.tscn" id="8_7qdje"] +[ext_resource type="PackedScene" uid="uid://bqqcww601rcx5" path="res://common/ui/windows/welcome_window/welcome_window.tscn" id="9_fp7qq"] +[ext_resource type="Script" uid="uid://b2l7hjfyrnr3x" path="res://common/ui/windows/file_dialog/file_dialog.gd" id="10_r77hi"] -[sub_resource type="ShaderMaterial" id="ShaderMaterial_7ks4x"] -shader = ExtResource("5_1u5fg") -shader_parameter/lod = 1.0 -shader_parameter/darkness = 0.25 - -[node name="App" type="Control"] +[node name="App" type="Control" unique_id=934936997] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -28,9 +18,8 @@ grow_vertical = 2 size_flags_horizontal = 3 size_flags_vertical = 3 -[node name="Frame" type="PanelContainer" parent="."] +[node name="Frame" type="PanelContainer" parent="." unique_id=1777561432] clip_children = 2 -z_index = 1 clip_contents = true layout_mode = 1 anchors_preset = 15 @@ -40,94 +29,47 @@ grow_horizontal = 2 grow_vertical = 2 theme_type_variation = &"EditorBackground" -[node name="VBoxContainer" type="VBoxContainer" parent="Frame"] -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="CustomTitleBar" type="HBoxContainer" parent="Frame/VBoxContainer"] -custom_minimum_size = Vector2(0, 40) -layout_mode = 2 -theme_override_constants/separation = 0 - -[node name="MainPopupMenu" type="MenuButton" parent="Frame/VBoxContainer/CustomTitleBar"] -custom_minimum_size = Vector2(43, 0) -layout_mode = 2 -icon = ExtResource("1_1u5fg") -icon_alignment = 1 -expand_icon = true -script = ExtResource("4_f1t0i") - -[node name="VSeparator" type="VSeparator" parent="Frame/VBoxContainer/CustomTitleBar"] -layout_mode = 2 - -[node name="HBoxContainer2" type="HBoxContainer" parent="Frame/VBoxContainer/CustomTitleBar"] -layout_mode = 2 -size_flags_horizontal = 3 -theme_override_constants/separation = 0 - -[node name="TabBar" type="TabBar" parent="Frame/VBoxContainer/CustomTitleBar/HBoxContainer2"] +[node name="MonologueEditor" parent="Frame" unique_id=1963406996 node_paths=PackedStringArray("welcome_window", "file_dialog") instance=ExtResource("1_kiov6")] layout_mode = 2 -current_tab = 0 -clip_tabs = false -tab_count = 1 -tab_0/icon = ExtResource("3_gcdaj") +welcome_window = NodePath("../../WelcomeWindow") +file_dialog = NodePath("../../FileDialog") -[node name="HSeparator" type="HSeparator" parent="Frame/VBoxContainer"] -layout_mode = 2 -theme_override_constants/separation = 1 - -[node name="MonologueControl" parent="Frame/VBoxContainer" node_paths=PackedStringArray("welcome_window", "graph_node_picker") instance=ExtResource("1_kiov6")] -layout_mode = 2 -welcome_window = NodePath("../../../WelcomeWindow") -graph_node_picker = NodePath("../../../GraphNodePicker") - -[node name="GraphEditSwitcher" parent="Frame/VBoxContainer/MonologueControl/MainContainer/GraphEditsArea" index="0" node_paths=PackedStringArray("tab_bar")] -tab_bar = NodePath("../../../../CustomTitleBar/HBoxContainer2/TabBar") - -[node name="Dimmer" type="ColorRect" parent="."] +[node name="Dimmer" type="ColorRect" parent="." unique_id=522317776] visible = false -z_index = 2 -material = SubResource("ShaderMaterial_7ks4x") +z_index = 1 layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -script = ExtResource("6_nw038") - -[node name="CharacterEditContainer" parent="." instance=ExtResource("7_7qdje")] -visible = false -z_index = 2 -layout_mode = 1 +color = Color(1, 1, 1, 0.019607844) +script = ExtResource("2_y0d3j") -[node name="ExpandedTextEditContainer" parent="." instance=ExtResource("6_cqyrh")] +[node name="ExpandedTextEditContainer" parent="." unique_id=881659617 instance=ExtResource("6_cqyrh")] visible = false z_index = 2 layout_mode = 1 -[node name="GraphNodePicker" parent="." node_paths=PackedStringArray("switcher") instance=ExtResource("7_nw038")] +[node name="PreviewWindow" parent="." unique_id=1909948793 instance=ExtResource("8_7qdje")] +oversampling_override = 1.0 visible = false -keep_title_visible = false -content_scale_mode = 2 -content_scale_aspect = 1 -switcher = NodePath("../Frame/VBoxContainer/MonologueControl/MainContainer/GraphEditsArea/GraphEditSwitcher") -[node name="PreviewWindow" parent="." instance=ExtResource("8_7qdje")] -visible = false +[node name="WelcomeWindow" parent="." unique_id=979035935 instance=ExtResource("9_fp7qq")] -[node name="WelcomeWindow" parent="." instance=ExtResource("9_fp7qq")] -size = Vector2i(450, 300) +[node name="PromptWindow" parent="." unique_id=2120395406 instance=ExtResource("7_k15gm")] visible = false -[node name="FileDialog" type="FileDialog" parent="."] -auto_translate_mode = 1 -size = Vector2i(419, 201) +[node name="FileDialog" type="FileDialog" parent="." unique_id=1731826877] +oversampling_override = 1.0 +display_mode = 1 access = 2 use_native_dialog = true script = ExtResource("10_r77hi") -[connection signal="visibility_changed" from="CharacterEditContainer" to="CharacterEditContainer" method="_on_visibility_changed"] +[connection signal="dir_selected" from="FileDialog" to="FileDialog" method="_on_dir_selected"] [connection signal="file_selected" from="FileDialog" to="FileDialog" method="_on_file_selected"] +[connection signal="files_selected" from="FileDialog" to="FileDialog" method="_on_files_selected"] -[editable path="Frame/VBoxContainer/MonologueControl"] +[editable path="Frame/MonologueEditor"] +[editable path="Frame/MonologueEditor/VBox/MarginContainer/HBoxContainer/HSplitContainer/Inspector"] diff --git a/scenes/main/dimmer.gd b/scenes/main/dimmer.gd new file mode 100644 index 00000000..42154dee --- /dev/null +++ b/scenes/main/dimmer.gd @@ -0,0 +1,27 @@ +class_name Dimmer extends ColorRect + + +func _ready() -> void: + release_focus() + color = Color.TRANSPARENT + EventBus.show_dimmer.connect(show) + EventBus.hide_dimmer.connect(hide) + #focus_entered.connect(_on_focus_entered) + + +func _process(_delta: float) -> void: + var parent: Control = get_parent() + var index: int = get_index() + + for child: Node in parent.get_children().slice(index+1): + if not child.visible: + continue + + show() + return + hide() + + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + EventBus.window_out.emit() diff --git a/scenes/main/dimmer.gd.uid b/scenes/main/dimmer.gd.uid new file mode 100644 index 00000000..f808733b --- /dev/null +++ b/scenes/main/dimmer.gd.uid @@ -0,0 +1 @@ +uid://ck2cqoxpk1mto diff --git a/scenes/main/graph.tscn b/scenes/main/graph.tscn deleted file mode 100644 index 9f74833e..00000000 --- a/scenes/main/graph.tscn +++ /dev/null @@ -1,229 +0,0 @@ -[gd_scene load_steps=14 format=3 uid="uid://bqjfdabrxujp7"] - -[ext_resource type="Script" uid="uid://q6eg6rid6xqd" path="res://scenes/main/monologue_control.gd" id="1_r00lk"] -[ext_resource type="Texture2D" uid="uid://bfmsxfn26cvfn" path="res://ui/assets/icons/character.svg" id="5_1pnba"] -[ext_resource type="PackedScene" uid="uid://dgvhvxdrd58qp" path="res://common/layouts/side_panel/side_panel.tscn" id="6_3jc6d"] -[ext_resource type="Texture2D" uid="uid://b46sqb5g0spae" path="res://ui/assets/icons/variables.svg" id="6_bfi1l"] -[ext_resource type="Texture2D" uid="uid://dd6wdpndndufl" path="res://ui/assets/icons/sparkles.svg" id="6_l6ili"] -[ext_resource type="PackedScene" uid="uid://cb3se7h7akt47" path="res://common/layouts/language_switcher/language_switcher.tscn" id="6_nqv8k"] -[ext_resource type="Script" uid="uid://bmku341x5gaoe" path="res://scenes/main/add_node_button.gd" id="6_tdvn8"] -[ext_resource type="Script" uid="uid://q0oqx6butjwn" path="res://scenes/main/search_bar_container.gd" id="7_eu0e0"] -[ext_resource type="PackedScene" uid="uid://cvum3eaenloix" path="res://common/layouts/search_bar/search_bar.tscn" id="8_tvorm"] -[ext_resource type="Texture2D" uid="uid://d4fesqfd2v8fd" path="res://ui/assets/icons/settings.svg" id="8_yubrq"] -[ext_resource type="Script" uid="uid://nxistnt1yhxc" path="res://scenes/main/graph_edit_switcher.gd" id="11_k843q"] -[ext_resource type="Texture2D" uid="uid://b272tbdmvxj20" path="res://ui/assets/icons/play.svg" id="15_hn16p"] - -[sub_resource type="GDScript" id="GDScript_lenro"] -script/source = "extends Button - - -func _on_pressed() -> void: - GlobalSignal.emit(\"test_trigger\") -" - -[node name="MonologueControl" type="Control"] -clip_contents = true -layout_mode = 3 -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_r00lk") - -[node name="MainContainer" type="VBoxContainer" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/separation = 15 - -[node name="GraphEditsArea" type="Control" parent="MainContainer"] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 - -[node name="GraphEditSwitcher" type="VBoxContainer" parent="MainContainer/GraphEditsArea" node_paths=PackedStringArray("side_panel")] -unique_name_in_owner = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -theme_override_constants/separation = 0 -script = ExtResource("11_k843q") -side_panel = NodePath("../MarginContainer/HSplitContainer/SidePanel") - -[node name="GraphEditZone" type="Control" parent="MainContainer/GraphEditsArea/GraphEditSwitcher"] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="GraphEdits" type="Control" parent="MainContainer/GraphEditsArea/GraphEditSwitcher/GraphEditZone"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_vertical = 3 - -[node name="MarginContainer" type="MarginContainer" parent="MainContainer/GraphEditsArea"] -layout_mode = 1 -anchors_preset = -1 -anchor_left = 0.5 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 0 -grow_vertical = 2 -mouse_filter = 2 -theme_override_constants/margin_left = 0 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 0 -theme_override_constants/margin_bottom = 0 - -[node name="HSplitContainer" type="HSplitContainer" parent="MainContainer/GraphEditsArea/MarginContainer"] -layout_mode = 2 -mouse_filter = 2 - -[node name="Container" type="Container" parent="MainContainer/GraphEditsArea/MarginContainer/HSplitContainer"] -layout_mode = 2 -size_flags_horizontal = 3 -mouse_filter = 2 - -[node name="SidePanel" parent="MainContainer/GraphEditsArea/MarginContainer/HSplitContainer" instance=ExtResource("6_3jc6d")] -unique_name_in_owner = true -custom_minimum_size = Vector2(450, 0) -layout_mode = 2 -size_flags_horizontal = 3 - -[node name="ToolBarContainer" type="MarginContainer" parent="MainContainer/GraphEditsArea"] -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = -80.0 -grow_horizontal = 2 -grow_vertical = 0 -mouse_filter = 2 -theme_type_variation = &"MarginContainer_Medium" - -[node name="VBoxContainer" type="VBoxContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer"] -layout_mode = 2 -mouse_filter = 2 -alignment = 2 - -[node name="CenterContainer" type="CenterContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer"] -layout_mode = 2 -mouse_filter = 2 - -[node name="ToolBar" type="HBoxContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer"] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Medium" - -[node name="Left" type="PanelContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar"] -layout_mode = 2 -mouse_filter = 1 -theme_type_variation = &"OuterPanel" - -[node name="HBoxContainer" type="HBoxContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left"] -layout_mode = 2 -theme_type_variation = &"HBoxContainer_Medium" -alignment = 1 - -[node name="AddNodeBtn" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer"] -layout_mode = 2 -theme_type_variation = &"Button_Flat" -text = "Add a node..." -script = ExtResource("6_tdvn8") - -[node name="ButtonCharacters" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer"] -visible = false -layout_mode = 2 -theme_type_variation = &"Button_Flat" -icon = ExtResource("5_1pnba") -icon_alignment = 1 - -[node name="ButtonVariables" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer"] -visible = false -layout_mode = 2 -theme_type_variation = &"Button_Flat" -icon = ExtResource("6_bfi1l") -icon_alignment = 1 - -[node name="LanguageSwitcher" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer" instance=ExtResource("6_nqv8k")] -unique_name_in_owner = true -layout_mode = 2 -theme_type_variation = &"Button_Flat" -disabled = true - -[node name="ButtonSettings" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer"] -layout_mode = 2 -theme_type_variation = &"Button_Flat" -icon = ExtResource("8_yubrq") -icon_alignment = 1 - -[node name="ButtonSparkle" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer"] -visible = false -layout_mode = 2 -theme_type_variation = &"Button_Flat" -icon = ExtResource("6_l6ili") - -[node name="Right" type="PanelContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar"] -layout_mode = 2 -mouse_filter = 1 -theme_type_variation = &"OuterPanel" - -[node name="HBoxContainer" type="HBoxContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right"] -layout_mode = 2 -theme_override_constants/separation = 0 -alignment = 1 - -[node name="RunButton" type="Button" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right/HBoxContainer"] -custom_minimum_size = Vector2(34, 34) -layout_mode = 2 -theme_type_variation = &"Button_Flat" -script = SubResource("GDScript_lenro") - -[node name="CenterContainer" type="CenterContainer" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right/HBoxContainer/RunButton"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -rotation = -0.00318988 - -[node name="TextureRect" type="TextureRect" parent="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right/HBoxContainer/RunButton/CenterContainer"] -custom_minimum_size = Vector2(24, 24) -layout_mode = 2 -tooltip_text = "Run the project from the RootNode." -texture = ExtResource("15_hn16p") -expand_mode = 3 -stretch_mode = 5 - -[node name="SearchBarContainer" type="CenterContainer" parent="MainContainer/GraphEditsArea"] -layout_mode = 1 -anchors_preset = -1 -anchor_right = 1.0 -anchor_bottom = 0.5 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -script = ExtResource("7_eu0e0") - -[node name="SearchBar" parent="MainContainer/GraphEditsArea/SearchBarContainer" node_paths=PackedStringArray("graph_edit_switcher") instance=ExtResource("8_tvorm")] -visible = false -layout_mode = 2 -graph_edit_switcher = NodePath("../../GraphEditSwitcher") - -[connection signal="pressed" from="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer/AddNodeBtn" to="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer/AddNodeBtn" method="_on_pressed"] -[connection signal="pressed" from="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer/ButtonSettings" to="." method="_on_button_settings_pressed"] -[connection signal="pressed" from="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Left/HBoxContainer/ButtonSparkle" to="." method="_on_button_sparkle_pressed"] -[connection signal="pressed" from="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right/HBoxContainer/RunButton" to="MainContainer/GraphEditsArea/ToolBarContainer/VBoxContainer/CenterContainer/ToolBar/Right/HBoxContainer/RunButton" method="_on_pressed"] diff --git a/scenes/main/graph_edit_switcher.gd b/scenes/main/graph_edit_switcher.gd deleted file mode 100644 index ac523fc3..00000000 --- a/scenes/main/graph_edit_switcher.gd +++ /dev/null @@ -1,179 +0,0 @@ -## Consists of a TabBar which allows the user to switch between GraphEdits. -## Saving and loading of GraphEdit data is handled by MonologueControl. -class_name GraphEditSwitcher extends VBoxContainer - - -## Reference to the side panel control to connect graph edits to. -@export var side_panel: SidePanel -## Reference to the tab bar for switching graph edits. -@export var tab_bar: TabBar - -var current: MonologueGraphEdit: get = get_current_graph_edit -var graph_edit_scene = preload("res://common/layouts/graph_edit/monologue_graph_edit.tscn") -var is_closing_all_tabs: bool -var pending_new_graph: MonologueGraphEdit -var prompt_scene = preload("res://common/windows/prompt_window/prompt_window.tscn") -var root_scene = Constants.NODE_SCENES.get("Root") -var last_selected_tab: int = 0 -var prevent_switching: bool = false - -@onready var graph_edits: Control = $GraphEditZone/GraphEdits - - -func _ready() -> void: - tab_bar.connect("tab_changed", _on_tab_changed) - tab_bar.connect("tab_close_pressed", _on_tab_close_pressed) - new_graph_edit() - GlobalSignal.add_listener("previous_tab", previous_tab) - GlobalSignal.add_listener("last_tab", last_tab) - GlobalSignal.add_listener("show_current_config", show_current_config) - - -func _input(event: InputEvent) -> void: - # IMPORTANT: order matters, redo must come first, undo second - if event.is_action_pressed("Redo"): - current.trigger_redo() - elif event.is_action_pressed("Undo"): - current.trigger_undo() - elif event.is_action_pressed("Delete") and not side_panel.visible: - current.trigger_delete() - - -## Adds a root node to the current graph edit if given root ID doesn't exist. -func add_root(save: bool = true) -> void: - if not current.get_root_node(): - var root_node = root_scene.instantiate() - current.add_child(root_node) - if save: GlobalSignal.emit("save", [true]) - - -## Adds a new tab with the given JSON filename as the tab title. -func add_tab(filename: String) -> void: - tab_bar.add_tab(Util.truncate_filename(filename)) - tab_bar.move_tab(tab_bar.tab_count - 2, tab_bar.tab_count - 1) - tab_bar.current_tab = tab_bar.tab_count - 2 - - -func connect_side_panel(graph_edit: MonologueGraphEdit) -> void: - graph_edit.connect("node_selected", side_panel.on_graph_node_selected) - graph_edit.connect("node_deselected", side_panel.on_graph_node_deselected) - graph_edit.undo_redo.connect("version_changed", update_save_state) - - -func commit_side_panel(node: MonologueGraphNode) -> void: - side_panel.refocus(node) - - -func get_current_graph_edit() -> MonologueGraphEdit: - return graph_edits.get_child(tab_bar.current_tab) - - -## Check if a graph edit representing the given filepath is opened or not. -func is_file_opened(filepath: String) -> bool: - for node in graph_edits.get_children(): - if node is MonologueGraphEdit and node.file_path == filepath: - return true - return false - - -func new_graph_edit() -> MonologueGraphEdit: - var graph_edit = graph_edit_scene.instantiate() - var root_node = root_scene.instantiate() - - graph_edit.add_child(root_node) - connect_side_panel(graph_edit) - graph_edits.add_child(graph_edit) - - for ge in graph_edits.get_children(): - ge.visible = ge == graph_edit - - return graph_edit - - -func _on_tab_close_pressed(tab: int) -> void: - if prevent_switching: - return - - var ge = graph_edits.get_child(tab) - if ge.is_unsaved(): # prompt user if there are unsaved changes - GlobalSignal.emit("disable_picker_mode") - tab_bar.current_tab = tab - var save_prompt = prompt_scene.instantiate() - save_prompt.connect("confirmed", _close_tab.bind(ge, tab, true)) - save_prompt.connect("cancelled", set.bind("is_closing_all_tabs", false)) - save_prompt.connect("denied", _close_tab.bind(ge, tab)) - add_child(save_prompt) - save_prompt.prompt_save(ge.file_path) - else: - _close_tab(ge, tab) - - -func previous_tab(): - if tab_bar.tab_count > 1: - tab_bar.select_previous_available() - - -func last_tab(): - tab_bar.current_tab = last_selected_tab - tab_bar.tab_changed.emit(last_selected_tab) - - -## Select the RootNode of the current graph edit, which opens the side panel. -func show_current_config() -> void: - var root_node = current.get_root_node() - current.set_selected(root_node) - - -## Update tab title with a suffix based on the current graph_edit's save state. -func update_save_state() -> void: - var index = current.get_index() - var trim = tab_bar.get_tab_title(index).trim_suffix(Constants.UNSAVED_FILE_SUFFIX) - var title = trim + Constants.UNSAVED_FILE_SUFFIX if current.is_unsaved() else trim - tab_bar.set_tab_title(index, title) - - -func _close_tab(graph_edit, tab_index, save_first = false) -> void: - if save_first: - GlobalSignal.emit("save", [true]) - GlobalSignal.emit("close_character_edit") - graph_edit.queue_free() - await graph_edit.tree_exited # buggy if we switch tabs without waiting - tab_bar.remove_tab(tab_index) - - if tab_bar.tab_count == 0: - get_tree().quit() - elif is_closing_all_tabs: - _on_tab_close_pressed(0) - - -func _on_tab_changed(tab: int) -> void: - if prevent_switching: - tab_bar.current_tab = last_selected_tab - return - - if tab < tab_bar.tab_count - 1: - # this allows user to switch out of the new tab (welcome window) - tab_bar.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY - if pending_new_graph and not pending_new_graph.file_path: - pending_new_graph.queue_free() - pending_new_graph = null - GlobalSignal.emit("hide_welcome") - GlobalSignal.emit("enable_language_switcher") - - for ge in graph_edits.get_children(): - if graph_edits.get_child(tab) == ge: - ge.visible = true - GlobalSignal.emit("load_languages", [ge.languages, ge]) - if ge.active_graphnode: - side_panel.on_graph_node_selected(ge.active_graphnode, true) - else: - side_panel.hide() - else: - ge.visible = false - last_selected_tab = tab - else: - tab_bar.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_NEVER - pending_new_graph = new_graph_edit() - GlobalSignal.emit("show_welcome") - GlobalSignal.emit("disable_language_switcher") - side_panel.hide() diff --git a/scenes/main/graph_edit_switcher.gd.uid b/scenes/main/graph_edit_switcher.gd.uid deleted file mode 100644 index 8690d675..00000000 --- a/scenes/main/graph_edit_switcher.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://nxistnt1yhxc diff --git a/scenes/main/graph_edit_switcher.tscn b/scenes/main/graph_edit_switcher.tscn deleted file mode 100644 index 8e1a8cdb..00000000 --- a/scenes/main/graph_edit_switcher.tscn +++ /dev/null @@ -1,37 +0,0 @@ -[gd_scene load_steps=4 format=3 uid="uid://cvjfcgqktlyc2"] - -[ext_resource type="Script" uid="uid://nxistnt1yhxc" path="res://scenes/main/graph_edit_switcher.gd" id="1_fvn0a"] -[ext_resource type="Texture2D" uid="uid://c7vdr4e0mxst6" path="res://ui/assets/icons/close_icon.png" id="2_k726l"] -[ext_resource type="PackedScene" uid="uid://qdgl8co6qy6" path="res://common/layouts/graph_edit/monologue_graph_edit.tscn" id="3_o6be6"] - -[node name="GraphEditSwitcher" type="VBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -script = ExtResource("1_fvn0a") - -[node name="TabBar" type="TabBar" parent="."] -custom_minimum_size = Vector2(0, 30) -layout_mode = 2 -size_flags_horizontal = 0 -mouse_default_cursor_shape = 2 -theme_override_icons/close = ExtResource("2_k726l") -current_tab = 0 -clip_tabs = false -tab_close_display_policy = 1 -tab_count = 1 -tab_0/title = "+" - -[node name="GraphEdits" type="Control" parent="."] -layout_mode = 2 -size_flags_vertical = 3 -mouse_filter = 2 - -[node name="Default" parent="GraphEdits" instance=ExtResource("3_o6be6")] -layout_mode = 1 - -[connection signal="tab_changed" from="TabBar" to="." method="_on_tab_changed"] -[connection signal="tab_close_pressed" from="TabBar" to="." method="on_tab_close_pressed"] diff --git a/scenes/main/monologue_control.gd b/scenes/main/monologue_control.gd deleted file mode 100644 index 27f5a4b2..00000000 --- a/scenes/main/monologue_control.gd +++ /dev/null @@ -1,211 +0,0 @@ -class_name MonologueControl extends Control - -@export var welcome_window: WelcomeWindow -@export var graph_node_picker: GraphNodePicker - -@onready var graph_switcher: GraphEditSwitcher = %GraphEditSwitcher -@onready var side_panel_node: SidePanel = %SidePanel -@onready var run_window := preload("res://scenes/run/run_window.tscn") -@onready var dimmer := $"../../../Dimmer" - - -func _ready(): - get_tree().auto_accept_quit = false # quit handled by _close_tab() - welcome_window.show() - - GlobalSignal.add_listener("add_graph_node", add_node_from_global) - GlobalSignal.add_listener("select_new_node", _select_new_node) - GlobalSignal.add_listener("refresh", refresh) - GlobalSignal.add_listener("load_project", load_project) - GlobalSignal.add_listener("test_trigger", test_project) - GlobalSignal.add_listener("save", save) - - -func _select_new_node() -> void: - graph_node_picker.show() - - -func _input(event): - if event.is_action_pressed("Save"): - save() - - -func _to_dict() -> Dictionary: - var list_nodes: Array[Dictionary] = [] - - # compile all node data of the current graph edit - for node in graph_switcher.current.get_nodes(): - if node.is_queued_for_deletion(): - continue - - # if side panel is still open, release the focus so that some - # text controls trigger the focus_exited() signal to update - if side_panel_node.visible and side_panel_node.selected_node == node: - var refocus = get_viewport().gui_get_focus_owner() - if refocus: - refocus.release_focus() - refocus.grab_focus() - - list_nodes.append(node._to_dict()) - if node.node_type == "NodeChoice": - for child in node.get_children(): - list_nodes.append(child._to_dict()) - - # build data for dialogue characters - var characters = graph_switcher.current.characters - if characters.size() <= 0: - characters.append( - { - "ID": IDGen.generate(5), - "Protected": true, - "Character": {"Name": "_NARRATOR"}, - "EditorIndex": 0 - } - ) - - return { - "EditorVersion": ProjectSettings.get_setting("application/config/version", "unknown"), - "RootNodeID": get_root_dict(list_nodes).get("ID"), - "ListNodes": list_nodes, - "Characters": characters, - "Variables": graph_switcher.current.variables, - "Languages": GlobalVariables.language_switcher.get_languages().keys() - } - - -## Function callback for when the user wants to add a node from global context. -## Used by header menu and graph node selector (picker). -func add_node_from_global(node_type: String, picker: GraphNodePicker = null): - var nodes: Array[MonologueGraphNode] = graph_switcher.current.add_node(node_type, true, picker) - graph_switcher.current.pick_and_center(nodes, picker) - - -func get_root_dict(node_list: Array) -> Dictionary: - for node in node_list: - if node.get("$type") == "NodeRoot": - return node - return {} - - -func load_project(path: String, new_graph: bool = false) -> void: - var file = FileAccess.open(path, FileAccess.READ) - if file and not graph_switcher.is_file_opened(path): - if new_graph: - graph_switcher.new_graph_edit() - graph_switcher.current.file_path = path # set path first before tab creation - - var data = {} - var text = file.get_as_text() - if text: - data = JSON.parse_string(text) - if not data: - data = _to_dict() - save() - - var converter := NodeConverter.new() - graph_switcher.current.languages = data.get("Languages", []) # load language before tab - graph_switcher.add_tab(path.get_file()) - graph_switcher.current.clear() - graph_switcher.current.name = path.get_file().trim_suffix(".json") - graph_switcher.current.characters = converter.convert_characters(data.get("Characters")) - graph_switcher.current.variables = data.get("Variables") - graph_switcher.current.data = data - - var node_list = data.get("ListNodes") - _load_nodes(node_list) - _connect_nodes(node_list) - graph_switcher.add_root() - graph_switcher.current.update_node_positions() - graph_switcher.current.grab_focus() - GlobalSignal.emit("load_successful", [path]) - - -## Reload the current graph edit and side panel values. -func refresh(node: MonologueGraphNode = null, affected_properties: PackedStringArray = []) -> void: - # if there is a given node, refresh only the parts that were specified - if node: - node.reload_preview() - if node is not OptionNode: - node._update.call_deferred() - if side_panel_node.visible: - # actual property value updates are handled by PropertyHistory - for property_name in affected_properties: - var field = side_panel_node.collapsibles.get(property_name) - if is_instance_valid(field): - field.open() - else: - var choice = node.choice_node if node is OptionNode else node - node.get_graph_edit().set_selected(choice) - # otherwise, remake the entire panel and refresh all node previews - else: - for each_node in graph_switcher.current.get_nodes(): - each_node.reload_preview() - #each_node._update.call_deferred() - if side_panel_node.visible: - var current_node = side_panel_node.selected_node - side_panel_node.on_graph_node_selected(current_node, true) - - -func save(): - var data = JSON.stringify(_to_dict(), "\t", false, true) - if data: - var path = graph_switcher.current.file_path - var file = FileAccess.open(path, FileAccess.WRITE) - file.store_string(data) - file.close() - graph_switcher.current.update_version() - graph_switcher.update_save_state() - - -func test_project(from_node: Variant = null): - if graph_switcher.current.file_path: - await save() - var window: RunWindow = run_window.instantiate() - window.file_path = graph_switcher.current.file_path - window.from_node = from_node - window.tree_exited.connect(dimmer.hide) - get_tree().root.add_child(window) - dimmer.show() - - -func _connect_nodes(node_list: Array) -> void: - for node in node_list: - var current_node = graph_switcher.current.get_node_by_id(node.get("ID", "")) - if current_node: - current_node._load_connections(node) - - -func _load_nodes(node_list: Array) -> void: - var converter = NodeConverter.new() - for node in node_list: - var data = converter.convert_node(node) - var node_type = data.get("$type").trim_prefix("Node") - if node_type == "Option": - # option data gets sent to the base_options dictionary - graph_switcher.current.base_options[data.get("ID")] = data - else: - var node_scene = Constants.NODE_SCENES.get(node_type) - if node_scene: - var node_instance = node_scene.instantiate() - node_instance.id.value = data.get("ID") - graph_switcher.current.add_child(node_instance, true) - node_instance._from_dict(data) - - -func _notification(what: int) -> void: - if what == NOTIFICATION_WM_CLOSE_REQUEST: - get_viewport().gui_release_focus() - graph_switcher.is_closing_all_tabs = true - graph_switcher._on_tab_close_pressed(0) - - -func _on_button_sparkle_pressed() -> void: - # TODO: Create an undo/redo action for every nodes. Need to pack undo/redo action into one action. - pass - #graph_switcher.current.set_block_signals(true) - #graph_switcher.current.arrange_nodes() - #graph_switcher.current.set_block_signals.bind(false).call_deferred() - - -func _on_button_settings_pressed() -> void: - GlobalSignal.emit("show_current_config") diff --git a/scenes/main/search_bar_container.gd b/scenes/main/search_bar_container.gd deleted file mode 100644 index 4f1978af..00000000 --- a/scenes/main/search_bar_container.gd +++ /dev/null @@ -1,16 +0,0 @@ -extends CenterContainer - -@onready var searchbar = $SearchBar -@onready var graph_edit_switcher = %GraphEditSwitcher - - -func _input(_event: InputEvent) -> void: - if Input.is_action_just_pressed("Show searchbar"): - searchbar.visible = !searchbar.visible - if searchbar.visible: - searchbar.focus() - graph_edit_switcher.prevent_switching = true - - if Input.is_key_pressed(KEY_ESCAPE): - searchbar.hide() - graph_edit_switcher.prevent_switching = false diff --git a/scenes/main/search_bar_container.gd.uid b/scenes/main/search_bar_container.gd.uid deleted file mode 100644 index fe45408a..00000000 --- a/scenes/main/search_bar_container.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://q0oqx6butjwn diff --git a/scenes/main/title_bar_button.gd b/scenes/main/title_bar_button.gd deleted file mode 100644 index 1897c812..00000000 --- a/scenes/main/title_bar_button.gd +++ /dev/null @@ -1,33 +0,0 @@ -@tool -extends Button - -@export var texture: Texture: - set = _set_button_texture -@export var hover_color: Color = Color("ffffff20") - -@onready var texture_rect: TextureRect = $MarginContainer/TextureRect - - -func _set_button_texture(val: Texture) -> void: - texture = val - reload_texture() - - -func _ready() -> void: - reload_texture() - - var hover_stylebox: StyleBoxFlat = StyleBoxFlat.new() - hover_stylebox.bg_color = hover_color - add_theme_stylebox_override("hover", hover_stylebox) - - connect("pressed", release_focus) - - -func reload_texture() -> void: - if texture is not Texture: - return - - if not is_node_ready(): - await ready - - texture_rect.texture = texture diff --git a/scenes/main/title_bar_button.gd.uid b/scenes/main/title_bar_button.gd.uid deleted file mode 100644 index c087be49..00000000 --- a/scenes/main/title_bar_button.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bbjutqdesleeu diff --git a/scenes/main/title_bar_button.tscn b/scenes/main/title_bar_button.tscn index adc31e06..ddc6ddde 100644 --- a/scenes/main/title_bar_button.tscn +++ b/scenes/main/title_bar_button.tscn @@ -1,23 +1,58 @@ -[gd_scene load_steps=5 format=3 uid="uid://c2k0l1j45eshc"] +[gd_scene format=3 uid="uid://ccm88n1b7trhw"] -[ext_resource type="Script" uid="uid://bbjutqdesleeu" path="res://scenes/main/title_bar_button.gd" id="1_oct8q"] [ext_resource type="Texture2D" uid="uid://kk5em170gn2b" path="res://ui/assets/icons/ui_close.svg" id="2_dijse"] -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ei6n7"] -bg_color = Color(1, 1, 1, 0.12549) - [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tjs5r"] -[node name="TitleBarButton" type="Button"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eg226"] +bg_color = Color(1, 1, 1, 0.1254902) + +[sub_resource type="GDScript" id="GDScript_fud8k"] +script/source = "@tool +extends Button + +@export var texture: Texture: + set = _set_button_texture +@export var hover_color: Color = Color(\"ffffff20\") + +@onready var texture_rect: TextureRect = $MarginContainer/TextureRect + + +func _set_button_texture(val: Texture) -> void: + texture = val + reload_texture() + + +func _ready() -> void: + reload_texture() + + var hover_stylebox: StyleBoxFlat = StyleBoxFlat.new() + hover_stylebox.bg_color = hover_color + add_theme_stylebox_override(\"hover\", hover_stylebox) + + connect(\"pressed\", release_focus) + + +func reload_texture() -> void: + if texture is not Texture: + return + + if not is_node_ready(): + await ready + + texture_rect.texture = texture +" + +[node name="TitleBarButton" type="Button" unique_id=1738916408] custom_minimum_size = Vector2(60, 30) offset_right = 60.0 offset_bottom = 30.0 -theme_override_styles/hover = SubResource("StyleBoxFlat_ei6n7") theme_override_styles/normal = SubResource("StyleBoxEmpty_tjs5r") -script = ExtResource("1_oct8q") +theme_override_styles/hover = SubResource("StyleBoxFlat_eg226") +script = SubResource("GDScript_fud8k") texture = ExtResource("2_dijse") -[node name="MarginContainer" type="MarginContainer" parent="."] +[node name="MarginContainer" type="MarginContainer" parent="." unique_id=1569837501] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -29,7 +64,7 @@ theme_override_constants/margin_top = 7 theme_override_constants/margin_right = 7 theme_override_constants/margin_bottom = 7 -[node name="TextureRect" type="TextureRect" parent="MarginContainer"] +[node name="TextureRect" type="TextureRect" parent="MarginContainer" unique_id=1348367410] custom_minimum_size = Vector2(0, 15) layout_mode = 2 size_flags_vertical = 4 diff --git a/scenes/run/assets/background.png.import b/scenes/run/assets/background.png.import index f19e0e97..841b3fb4 100644 --- a/scenes/run/assets/background.png.import +++ b/scenes/run/assets/background.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/background.png-3276b190ef914a8e7e153690f9d0db compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/scenes/run/common/choice_selector/choice_selector.gd b/scenes/run/common/choice_selector/choice_selector.gd index cf2467f6..fad1c655 100644 --- a/scenes/run/common/choice_selector/choice_selector.gd +++ b/scenes/run/common/choice_selector/choice_selector.gd @@ -20,7 +20,7 @@ func display_option(option: Dictionary, language: String = "English") -> void: func _clear() -> void: hide() - for child in vbox.get_children(): + for child: Node in vbox.get_children(): vbox.remove_child(child) child.queue_free() diff --git a/scenes/run/common/choice_selector/choice_selector.tscn b/scenes/run/common/choice_selector/choice_selector.tscn index 59f83c34..ed28f2cc 100644 --- a/scenes/run/common/choice_selector/choice_selector.tscn +++ b/scenes/run/common/choice_selector/choice_selector.tscn @@ -1,20 +1,20 @@ -[gd_scene load_steps=2 format=3 uid="uid://4br7pp244bp0"] +[gd_scene format=3 uid="uid://4br7pp244bp0"] [ext_resource type="Script" uid="uid://ddrsihbsup3tu" path="res://scenes/run/common/choice_selector/choice_selector.gd" id="1_6fys4"] -[node name="ChoiceSelector" type="PanelContainer"] +[node name="ChoiceSelector" type="PanelContainer" unique_id=2127676326] size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1_6fys4") -[node name="MarginContainer" type="MarginContainer" parent="."] +[node name="MarginContainer" type="MarginContainer" parent="." unique_id=155475952] layout_mode = 2 theme_override_constants/margin_left = 5 theme_override_constants/margin_top = 5 theme_override_constants/margin_right = 5 theme_override_constants/margin_bottom = 5 -[node name="VBox" type="VBoxContainer" parent="MarginContainer"] +[node name="VBox" type="VBoxContainer" parent="MarginContainer" unique_id=1730369124] unique_name_in_owner = true custom_minimum_size = Vector2(300, 0) layout_mode = 2 diff --git a/scenes/run/common/text_box/text_box.gd b/scenes/run/common/text_box/text_box.gd index 12dcf194..5259710b 100644 --- a/scenes/run/common/text_box/text_box.gd +++ b/scenes/run/common/text_box/text_box.gd @@ -1,32 +1,32 @@ extends VBoxContainer -@onready var scroll_container = %ScrollContainer +@onready var scroll_container: ScrollContainer = %ScrollContainer @onready var scroll_bar: ScrollBar = scroll_container.get_v_scroll_bar() -@onready var text_box = preload("res://scenes/run/common/text_box/text_box.tscn") +@onready var text_box: PackedScene = preload("res://scenes/run/common/text_box/text_box.tscn") var current_text_box: RichTextLabel -var text = "" -var speaker_display = "" -var text_speed = 5 +var text: String = "" +var speaker_display: String = "" +var text_speed: int = 5 var complete: bool = false var _display: bool = false -var tick = 0 +var tick: int = 0 -func _ready(): +func _ready() -> void: scroll_bar.connect("changed", handle_scrollbar_changed) reset() update() -func handle_scrollbar_changed(): - scroll_container.scroll_vertical = scroll_bar.max_value +func handle_scrollbar_changed() -> void: + scroll_container.scroll_vertical = round(scroll_bar.max_value) -func _process(delta): +func _process(delta: float) -> void: if complete: pass @@ -41,10 +41,10 @@ func _process(delta): complete = true return - current_text_box.visible_characters += text_speed * delta * 60 + current_text_box.visible_characters += round(text_speed * delta * 60) -func reset(): +func reset() -> void: append_text_box() current_text_box.visible_characters = 0 current_text_box.visible_ratio = 0 @@ -53,21 +53,21 @@ func reset(): tick = 0 -func update(): +func update() -> void: current_text_box.text = text -func display(): +func display() -> void: _display = true -func append_text_box(): +func append_text_box() -> void: if current_text_box: current_text_box.remove_theme_color_override("default_color") current_text_box.add_theme_color_override("default_color", Color("656565")) - var new_text_box = text_box.instantiate() + var new_text_box: Node = text_box.instantiate() add_child(new_text_box) current_text_box = new_text_box - current_text_box.grab_click_focus() \ No newline at end of file + current_text_box.grab_click_focus() diff --git a/scenes/run/main/character_asset.gd b/scenes/run/main/character_asset.gd index 3e72229f..4aa99410 100644 --- a/scenes/run/main/character_asset.gd +++ b/scenes/run/main/character_asset.gd @@ -1,10 +1,10 @@ extends TextureRect -@onready var init_pos = position +@onready var init_pos: Vector2 = position -func undisplay(): - var tween = get_tree().create_tween() +func undisplay() -> void: + var tween: Tween = get_tree().create_tween() tween.set_ease(Tween.EASE_OUT) ( tween @@ -15,8 +15,8 @@ func undisplay(): ) -func display(): - var tween = get_tree().create_tween() +func display() -> void: + var tween: Tween = get_tree().create_tween() tween.set_ease(Tween.EASE_OUT) tween.tween_property(self, "position:x", init_pos.x, .5).set_trans(Tween.TRANS_EXPO).set_ease( Tween.EASE_OUT diff --git a/scenes/run/main/choice_panel.gd b/scenes/run/main/choice_panel.gd index 90099ec8..63c255aa 100644 --- a/scenes/run/main/choice_panel.gd +++ b/scenes/run/main/choice_panel.gd @@ -1,9 +1,9 @@ extends VBoxContainer -var option_button_instance = preload("res://scenes/run/common/option_button.tscn") +var option_button_instance: PackedScene = preload("res://scenes/run/common/option_button.tscn") -func add_button(text, callback: Callable): +func add_button(text: String, callback: Callable) -> void: var new_button: Button = option_button_instance.instantiate() new_button.text = text new_button.tooltip_text = text @@ -13,6 +13,6 @@ func add_button(text, callback: Callable): add_child(new_button) -func clear(): - for child in get_children(): +func clear() -> void: + for child: Node in get_children(): child.queue_free() diff --git a/scenes/run/main/main.gd b/scenes/run/main/main.gd index 9ba9d529..7a36c354 100644 --- a/scenes/run/main/main.gd +++ b/scenes/run/main/main.gd @@ -14,8 +14,8 @@ func _ready() -> void: func _on_quit_btn_pressed() -> void: - var menu_instance = load("res://scenes/run/menu/menu.tscn") - var menu_scene_instance = menu_instance.instantiate() + var menu_instance: Resource = load("res://scenes/run/menu/menu.tscn") + var menu_scene_instance: Node = menu_instance.instantiate() menu_scene_instance.from_node = from_node menu_scene_instance.file_path = file_path get_window().add_scene(menu_scene_instance) diff --git a/scenes/run/main/notification.gd b/scenes/run/main/notification.gd index f446dd81..3a2f5823 100644 --- a/scenes/run/main/notification.gd +++ b/scenes/run/main/notification.gd @@ -1,19 +1,19 @@ extends PanelContainer -const DEFAULT_TIME = 7.5 +const DEFAULT_TIME: float = 7.5 var tween: Tween -@onready var timeleft = $VBoxContainer/TimeLeft -@onready var label = $VBoxContainer/MarginContainer/RichTextLabel +@onready var timeleft: PanelContainer = $VBoxContainer/TimeLeft +@onready var label: RichTextLabel = $VBoxContainer/MarginContainer/RichTextLabel -func _ready(): +func _ready() -> void: hide() -func notify(text, tag, color, time): - var bb_parser = RichTextLabel.new() +func notify(text: String, tag: String, color: Color, time: float) -> void: + var bb_parser: RichTextLabel = RichTextLabel.new() bb_parser.parse_bbcode(text) bb_parser.get_parsed_text() print("[%s] %s" % [tag, bb_parser.get_parsed_text()]) @@ -31,21 +31,21 @@ func notify(text, tag, color, time): show() -func info(text: String, time = DEFAULT_TIME): +func info(text: String, time: float = DEFAULT_TIME) -> void: notify(text, "INFO", Color("579144"), time) -func debug(text: String, time = DEFAULT_TIME): +func debug(text: String, time: float = DEFAULT_TIME) -> void: notify(text, "DEBUG", Color("5e8de6"), time) -func warn(text: String, time = DEFAULT_TIME): +func warn(text: String, time: float = DEFAULT_TIME) -> void: notify(text, "WARN", Color("e5b65e"), time) -func error(text: String, time = DEFAULT_TIME): +func error(text: String, time: float = DEFAULT_TIME) -> void: notify(text, "ERROR", Color("d19c9d"), time) -func critical(text: String, time = DEFAULT_TIME): +func critical(text: String, time: float = DEFAULT_TIME) -> void: notify(text, "CRITICAL", Color("d19c9d"), time) diff --git a/scenes/run/menu/menu.gd b/scenes/run/menu/menu.gd index 756924cb..cec30f5e 100644 --- a/scenes/run/menu/menu.gd +++ b/scenes/run/menu/menu.gd @@ -4,26 +4,27 @@ var file_path: String var from_node: Variant -func _ready(): +func _ready() -> void: %CustomIDLabel.hide() if from_node != null and from_node != "": %CustomIDLabel.text = "(custom start node: " + from_node + ")" %CustomIDLabel.show() -func load_scene(scene): - var main_scene = scene.instantiate() +func load_scene(scene: PackedScene) -> void: + var main_scene: Node = scene.instantiate() main_scene.from_node = from_node if from_node else "" main_scene.file_path = file_path - main_scene.locale = str(GlobalVariables.language_switcher.get_current_language()) + var project := ProjectManager.current_project + main_scene.locale = project.active_language_code if project else "en" get_window().add_scene(main_scene) queue_free() -func _on_leave_button_pressed(): +func _on_leave_button_pressed() -> void: get_window().queue_free() -func _on_run_button_pressed(): - var scene = preload("res://scenes/run/main/main.tscn") +func _on_run_button_pressed() -> void: + var scene: PackedScene = preload("res://scenes/run/main/main.tscn") load_scene(scene) diff --git a/scenes/run/run_window.gd b/scenes/run/run_window.gd index 1c2b42c5..77673367 100644 --- a/scenes/run/run_window.gd +++ b/scenes/run/run_window.gd @@ -13,7 +13,7 @@ func _ready() -> void: force_native = true transient = true - var test_scene = test_instance.instantiate() + var test_scene: Node = test_instance.instantiate() if from_node: test_scene.from_node = from_node test_scene.file_path = file_path diff --git a/scenes/splash/dino/Render0001.png b/scenes/splash/dino/Render0001.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0001.png differ diff --git a/scenes/splash/dino/Render0001.png.import b/scenes/splash/dino/Render0001.png.import new file mode 100644 index 00000000..8f9fb708 --- /dev/null +++ b/scenes/splash/dino/Render0001.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c41kgi20w10ks" +path="res://.godot/imported/Render0001.png-878138baf4c902f226fe7b0c83336c52.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0001.png" +dest_files=["res://.godot/imported/Render0001.png-878138baf4c902f226fe7b0c83336c52.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0002.png b/scenes/splash/dino/Render0002.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0002.png differ diff --git a/scenes/splash/dino/Render0002.png.import b/scenes/splash/dino/Render0002.png.import new file mode 100644 index 00000000..7bc725ff --- /dev/null +++ b/scenes/splash/dino/Render0002.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bb8loih7rtsm7" +path="res://.godot/imported/Render0002.png-9a390ff5f6d680afcd1eb67ae5535da5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0002.png" +dest_files=["res://.godot/imported/Render0002.png-9a390ff5f6d680afcd1eb67ae5535da5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0003.png b/scenes/splash/dino/Render0003.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0003.png differ diff --git a/scenes/splash/dino/Render0003.png.import b/scenes/splash/dino/Render0003.png.import new file mode 100644 index 00000000..c0a470a7 --- /dev/null +++ b/scenes/splash/dino/Render0003.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkcu31j26iae2" +path="res://.godot/imported/Render0003.png-eb552878929270253e56e0c9c04ff6bd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0003.png" +dest_files=["res://.godot/imported/Render0003.png-eb552878929270253e56e0c9c04ff6bd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0004.png b/scenes/splash/dino/Render0004.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0004.png differ diff --git a/scenes/splash/dino/Render0004.png.import b/scenes/splash/dino/Render0004.png.import new file mode 100644 index 00000000..6e280b40 --- /dev/null +++ b/scenes/splash/dino/Render0004.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drdnk23jjdwhh" +path="res://.godot/imported/Render0004.png-de477e7a80e712cad5cdbd2b322ede87.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0004.png" +dest_files=["res://.godot/imported/Render0004.png-de477e7a80e712cad5cdbd2b322ede87.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0005.png b/scenes/splash/dino/Render0005.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0005.png differ diff --git a/scenes/splash/dino/Render0005.png.import b/scenes/splash/dino/Render0005.png.import new file mode 100644 index 00000000..9defebc8 --- /dev/null +++ b/scenes/splash/dino/Render0005.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dt4bk5bfbm3yy" +path="res://.godot/imported/Render0005.png-a82f70bf84972ea535a92f9550b4052e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0005.png" +dest_files=["res://.godot/imported/Render0005.png-a82f70bf84972ea535a92f9550b4052e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0006.png b/scenes/splash/dino/Render0006.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0006.png differ diff --git a/scenes/splash/dino/Render0006.png.import b/scenes/splash/dino/Render0006.png.import new file mode 100644 index 00000000..998204a9 --- /dev/null +++ b/scenes/splash/dino/Render0006.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cbf4cp1cctyy4" +path="res://.godot/imported/Render0006.png-c4f43d7df9a331d291ab55e01054ef6c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0006.png" +dest_files=["res://.godot/imported/Render0006.png-c4f43d7df9a331d291ab55e01054ef6c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0007.png b/scenes/splash/dino/Render0007.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0007.png differ diff --git a/scenes/splash/dino/Render0007.png.import b/scenes/splash/dino/Render0007.png.import new file mode 100644 index 00000000..68935292 --- /dev/null +++ b/scenes/splash/dino/Render0007.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://yw1qkpxfsphn" +path="res://.godot/imported/Render0007.png-ec7643933ec76239816b200808a7dbfd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0007.png" +dest_files=["res://.godot/imported/Render0007.png-ec7643933ec76239816b200808a7dbfd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0008.png b/scenes/splash/dino/Render0008.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0008.png differ diff --git a/scenes/splash/dino/Render0008.png.import b/scenes/splash/dino/Render0008.png.import new file mode 100644 index 00000000..e54912d1 --- /dev/null +++ b/scenes/splash/dino/Render0008.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b14o2y1e6e3hx" +path="res://.godot/imported/Render0008.png-9aa714af1264bee7db30393a7e6f1e63.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0008.png" +dest_files=["res://.godot/imported/Render0008.png-9aa714af1264bee7db30393a7e6f1e63.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0009.png b/scenes/splash/dino/Render0009.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0009.png differ diff --git a/scenes/splash/dino/Render0009.png.import b/scenes/splash/dino/Render0009.png.import new file mode 100644 index 00000000..6771a042 --- /dev/null +++ b/scenes/splash/dino/Render0009.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxwyfggk3n36h" +path="res://.godot/imported/Render0009.png-4a068f759d97606655293ba637b5faaa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0009.png" +dest_files=["res://.godot/imported/Render0009.png-4a068f759d97606655293ba637b5faaa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0010.png b/scenes/splash/dino/Render0010.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0010.png differ diff --git a/scenes/splash/dino/Render0010.png.import b/scenes/splash/dino/Render0010.png.import new file mode 100644 index 00000000..506dd8a2 --- /dev/null +++ b/scenes/splash/dino/Render0010.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnnm4e1auy3jf" +path="res://.godot/imported/Render0010.png-ea4d3b22bd994ed02c59c0b1791de636.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0010.png" +dest_files=["res://.godot/imported/Render0010.png-ea4d3b22bd994ed02c59c0b1791de636.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0011.png b/scenes/splash/dino/Render0011.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0011.png differ diff --git a/scenes/splash/dino/Render0011.png.import b/scenes/splash/dino/Render0011.png.import new file mode 100644 index 00000000..eeb43800 --- /dev/null +++ b/scenes/splash/dino/Render0011.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://capb5oodtr03d" +path="res://.godot/imported/Render0011.png-ec6d8d4260b8748cab3228f805407507.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0011.png" +dest_files=["res://.godot/imported/Render0011.png-ec6d8d4260b8748cab3228f805407507.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0012.png b/scenes/splash/dino/Render0012.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0012.png differ diff --git a/scenes/splash/dino/Render0012.png.import b/scenes/splash/dino/Render0012.png.import new file mode 100644 index 00000000..f81c19b6 --- /dev/null +++ b/scenes/splash/dino/Render0012.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3xd0b7slaext" +path="res://.godot/imported/Render0012.png-39c71147fe0d295ce172f0aac06bbf3a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0012.png" +dest_files=["res://.godot/imported/Render0012.png-39c71147fe0d295ce172f0aac06bbf3a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0013.png b/scenes/splash/dino/Render0013.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0013.png differ diff --git a/scenes/splash/dino/Render0013.png.import b/scenes/splash/dino/Render0013.png.import new file mode 100644 index 00000000..0d7f87b0 --- /dev/null +++ b/scenes/splash/dino/Render0013.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cm2jva4mjfmbd" +path="res://.godot/imported/Render0013.png-f8af2bb72c0a35026575aa271c5b7a65.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0013.png" +dest_files=["res://.godot/imported/Render0013.png-f8af2bb72c0a35026575aa271c5b7a65.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0014.png b/scenes/splash/dino/Render0014.png new file mode 100644 index 00000000..67228ae7 Binary files /dev/null and b/scenes/splash/dino/Render0014.png differ diff --git a/scenes/splash/dino/Render0014.png.import b/scenes/splash/dino/Render0014.png.import new file mode 100644 index 00000000..0c5e6995 --- /dev/null +++ b/scenes/splash/dino/Render0014.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bp1pq3d3nohvw" +path="res://.godot/imported/Render0014.png-d4f915ee72d9ada6e6a9f5a7ac881bed.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0014.png" +dest_files=["res://.godot/imported/Render0014.png-d4f915ee72d9ada6e6a9f5a7ac881bed.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0015.png b/scenes/splash/dino/Render0015.png new file mode 100644 index 00000000..af8bfb48 Binary files /dev/null and b/scenes/splash/dino/Render0015.png differ diff --git a/scenes/splash/dino/Render0015.png.import b/scenes/splash/dino/Render0015.png.import new file mode 100644 index 00000000..1c2998de --- /dev/null +++ b/scenes/splash/dino/Render0015.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ba87aujsth7cl" +path="res://.godot/imported/Render0015.png-412c1f4270e75c735cb355e623ebee20.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0015.png" +dest_files=["res://.godot/imported/Render0015.png-412c1f4270e75c735cb355e623ebee20.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0016.png b/scenes/splash/dino/Render0016.png new file mode 100644 index 00000000..af8bfb48 Binary files /dev/null and b/scenes/splash/dino/Render0016.png differ diff --git a/scenes/splash/dino/Render0016.png.import b/scenes/splash/dino/Render0016.png.import new file mode 100644 index 00000000..8c56130b --- /dev/null +++ b/scenes/splash/dino/Render0016.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0ealo6tadsem" +path="res://.godot/imported/Render0016.png-1ee48f8fefcfcc497eed980f0f02f81a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0016.png" +dest_files=["res://.godot/imported/Render0016.png-1ee48f8fefcfcc497eed980f0f02f81a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0017.png b/scenes/splash/dino/Render0017.png new file mode 100644 index 00000000..67228ae7 Binary files /dev/null and b/scenes/splash/dino/Render0017.png differ diff --git a/scenes/splash/dino/Render0017.png.import b/scenes/splash/dino/Render0017.png.import new file mode 100644 index 00000000..0b2c2593 --- /dev/null +++ b/scenes/splash/dino/Render0017.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnllmukyrs07o" +path="res://.godot/imported/Render0017.png-dcaebcccb16b96a78d00c81388d38b3d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0017.png" +dest_files=["res://.godot/imported/Render0017.png-dcaebcccb16b96a78d00c81388d38b3d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0018.png b/scenes/splash/dino/Render0018.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0018.png differ diff --git a/scenes/splash/dino/Render0018.png.import b/scenes/splash/dino/Render0018.png.import new file mode 100644 index 00000000..81a2f9bb --- /dev/null +++ b/scenes/splash/dino/Render0018.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bysgspu1uklaj" +path="res://.godot/imported/Render0018.png-fb229b03cb11e162cab7ff166d93a47b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0018.png" +dest_files=["res://.godot/imported/Render0018.png-fb229b03cb11e162cab7ff166d93a47b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0019.png b/scenes/splash/dino/Render0019.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0019.png differ diff --git a/scenes/splash/dino/Render0019.png.import b/scenes/splash/dino/Render0019.png.import new file mode 100644 index 00000000..dd1066a1 --- /dev/null +++ b/scenes/splash/dino/Render0019.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2i5w3curjjrc" +path="res://.godot/imported/Render0019.png-6b2688328409d7e70a6574b49b264f13.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0019.png" +dest_files=["res://.godot/imported/Render0019.png-6b2688328409d7e70a6574b49b264f13.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0020.png b/scenes/splash/dino/Render0020.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0020.png differ diff --git a/scenes/splash/dino/Render0020.png.import b/scenes/splash/dino/Render0020.png.import new file mode 100644 index 00000000..d1f26a11 --- /dev/null +++ b/scenes/splash/dino/Render0020.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bge0uh3rri18y" +path="res://.godot/imported/Render0020.png-c97cd5f891b049c25a9ecb489d17351b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0020.png" +dest_files=["res://.godot/imported/Render0020.png-c97cd5f891b049c25a9ecb489d17351b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0021.png b/scenes/splash/dino/Render0021.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0021.png differ diff --git a/scenes/splash/dino/Render0021.png.import b/scenes/splash/dino/Render0021.png.import new file mode 100644 index 00000000..a7d0b9d2 --- /dev/null +++ b/scenes/splash/dino/Render0021.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bjvfg8v7paow2" +path="res://.godot/imported/Render0021.png-5baf946e083df6e522ce7c0bc7dcf025.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0021.png" +dest_files=["res://.godot/imported/Render0021.png-5baf946e083df6e522ce7c0bc7dcf025.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0022.png b/scenes/splash/dino/Render0022.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0022.png differ diff --git a/scenes/splash/dino/Render0022.png.import b/scenes/splash/dino/Render0022.png.import new file mode 100644 index 00000000..ba5e7abe --- /dev/null +++ b/scenes/splash/dino/Render0022.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ewqu3mclfs6c" +path="res://.godot/imported/Render0022.png-60bce78a8e16ad124013197b92958d40.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0022.png" +dest_files=["res://.godot/imported/Render0022.png-60bce78a8e16ad124013197b92958d40.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0023.png b/scenes/splash/dino/Render0023.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0023.png differ diff --git a/scenes/splash/dino/Render0023.png.import b/scenes/splash/dino/Render0023.png.import new file mode 100644 index 00000000..11554ea7 --- /dev/null +++ b/scenes/splash/dino/Render0023.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7nbo4iudfojf" +path="res://.godot/imported/Render0023.png-e877c6caa3fb6bbf2a31024af7a49640.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0023.png" +dest_files=["res://.godot/imported/Render0023.png-e877c6caa3fb6bbf2a31024af7a49640.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0024.png b/scenes/splash/dino/Render0024.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0024.png differ diff --git a/scenes/splash/dino/Render0024.png.import b/scenes/splash/dino/Render0024.png.import new file mode 100644 index 00000000..0a609a78 --- /dev/null +++ b/scenes/splash/dino/Render0024.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c8ctuf25a6uie" +path="res://.godot/imported/Render0024.png-bd68ee02556356ea3dcf8e49c8653906.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0024.png" +dest_files=["res://.godot/imported/Render0024.png-bd68ee02556356ea3dcf8e49c8653906.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0025.png b/scenes/splash/dino/Render0025.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0025.png differ diff --git a/scenes/splash/dino/Render0025.png.import b/scenes/splash/dino/Render0025.png.import new file mode 100644 index 00000000..58d61324 --- /dev/null +++ b/scenes/splash/dino/Render0025.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dv8dbtbhofxo" +path="res://.godot/imported/Render0025.png-5db4809a7c3d3288ef7c0ff11d929119.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0025.png" +dest_files=["res://.godot/imported/Render0025.png-5db4809a7c3d3288ef7c0ff11d929119.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0026.png b/scenes/splash/dino/Render0026.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0026.png differ diff --git a/scenes/splash/dino/Render0026.png.import b/scenes/splash/dino/Render0026.png.import new file mode 100644 index 00000000..1282ed29 --- /dev/null +++ b/scenes/splash/dino/Render0026.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4suruuapjkh2" +path="res://.godot/imported/Render0026.png-77fb4f567a83726d52196efe19d0f93d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0026.png" +dest_files=["res://.godot/imported/Render0026.png-77fb4f567a83726d52196efe19d0f93d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0027.png b/scenes/splash/dino/Render0027.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0027.png differ diff --git a/scenes/splash/dino/Render0027.png.import b/scenes/splash/dino/Render0027.png.import new file mode 100644 index 00000000..351861e9 --- /dev/null +++ b/scenes/splash/dino/Render0027.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dj7gb22q2y3qq" +path="res://.godot/imported/Render0027.png-e1b17fb574c98e9960cf410f7f1557a2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0027.png" +dest_files=["res://.godot/imported/Render0027.png-e1b17fb574c98e9960cf410f7f1557a2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0028.png b/scenes/splash/dino/Render0028.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0028.png differ diff --git a/scenes/splash/dino/Render0028.png.import b/scenes/splash/dino/Render0028.png.import new file mode 100644 index 00000000..b7266556 --- /dev/null +++ b/scenes/splash/dino/Render0028.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dg2a46d32at3t" +path="res://.godot/imported/Render0028.png-8fa811f8d4d8bea16e533422c8439325.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0028.png" +dest_files=["res://.godot/imported/Render0028.png-8fa811f8d4d8bea16e533422c8439325.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0029.png b/scenes/splash/dino/Render0029.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0029.png differ diff --git a/scenes/splash/dino/Render0029.png.import b/scenes/splash/dino/Render0029.png.import new file mode 100644 index 00000000..2565ea43 --- /dev/null +++ b/scenes/splash/dino/Render0029.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://50icxn20btnq" +path="res://.godot/imported/Render0029.png-f007eebed3cdc1d58572b147d39d3996.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0029.png" +dest_files=["res://.godot/imported/Render0029.png-f007eebed3cdc1d58572b147d39d3996.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/dino/Render0030.png b/scenes/splash/dino/Render0030.png new file mode 100644 index 00000000..31c619e3 Binary files /dev/null and b/scenes/splash/dino/Render0030.png differ diff --git a/scenes/splash/dino/Render0030.png.import b/scenes/splash/dino/Render0030.png.import new file mode 100644 index 00000000..7fa05030 --- /dev/null +++ b/scenes/splash/dino/Render0030.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dd00100w3siit" +path="res://.godot/imported/Render0030.png-3640f1f9c65c54619af1a21159e7ceb1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/splash/dino/Render0030.png" +dest_files=["res://.godot/imported/Render0030.png-3640f1f9c65c54619af1a21159e7ceb1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/splash/monologue_eye/frame_1.png.import b/scenes/splash/monologue_eye/frame_1.png.import index eb2b6e33..ed283543 100644 --- a/scenes/splash/monologue_eye/frame_1.png.import +++ b/scenes/splash/monologue_eye/frame_1.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/frame_1.png-147f33cbcbe9df68a844afe932874042. compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/scenes/splash/monologue_eye/frame_2.png.import b/scenes/splash/monologue_eye/frame_2.png.import index e283dfb3..a708ac52 100644 --- a/scenes/splash/monologue_eye/frame_2.png.import +++ b/scenes/splash/monologue_eye/frame_2.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/frame_2.png-42ab9f647cba04dfc332a1585f7c86ea. compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/scenes/splash/monologue_eye/frame_3.png.import b/scenes/splash/monologue_eye/frame_3.png.import index 67614576..41bdab42 100644 --- a/scenes/splash/monologue_eye/frame_3.png.import +++ b/scenes/splash/monologue_eye/frame_3.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/frame_3.png-3a7a807425b0ddd9699f56c885c47046. compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/scenes/splash/splash.gd b/scenes/splash/splash.gd index c1d04aea..f6142c37 100644 --- a/scenes/splash/splash.gd +++ b/scenes/splash/splash.gd @@ -1,34 +1,31 @@ extends Control @export_file var load_scene: String -@export var min_display_time: float = 0.2 -@export var after_blink_time: float = 0.5 -@onready var timer = $tMinDisplayTime -@onready var title = $CenterContainer/TextureRect -@onready var eye = $CenterContainer/TextureRect/AnimatedSprite2D +@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D func _ready() -> void: - timer.start(min_display_time) ResourceLoader.load_threaded_request(load_scene) - title.connect("item_rect_changed", _on_item_rect_changed) + item_rect_changed.connect(_on_item_rect_changed) + _on_item_rect_changed() func _process(_delta: float) -> void: - var status := ResourceLoader.load_threaded_get_status(load_scene) + var status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(load_scene) - if status == ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED and timer.is_stopped(): - var scene := ResourceLoader.load_threaded_get(load_scene) + if status == ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED: + var scene: PackedScene = ResourceLoader.load_threaded_get(load_scene) + _switch_to_scene(scene) - eye.play("blink") - await eye.animation_finished - await get_tree().create_timer(after_blink_time).timeout - get_window().unresizable = false +func _switch_to_scene(scene: PackedScene) -> void: + sprite.play("blink") + await sprite.animation_finished + get_tree().change_scene_to_packed(scene) func _on_item_rect_changed() -> void: - eye.global_position.x = title.position.x + 143 - eye.global_position.y = title.position.y + 40 + var vp: Rect2 = get_viewport_rect() + sprite.global_position = vp.size / 2 diff --git a/scenes/splash/splash.tscn b/scenes/splash/splash.tscn index afc5fc0a..b7d1c987 100644 --- a/scenes/splash/splash.tscn +++ b/scenes/splash/splash.tscn @@ -1,35 +1,36 @@ -[gd_scene load_steps=7 format=3 uid="uid://bakm53e6ecbk8"] +[gd_scene format=3 uid="uid://bakm53e6ecbk8"] [ext_resource type="Script" uid="uid://c0nc2j7o3qbfe" path="res://scenes/splash/splash.gd" id="1_pl0ug"] -[ext_resource type="Texture2D" uid="uid://bddodmw1pm42r" path="res://title_banner@0.5x.png" id="2_rikux"] -[ext_resource type="Texture2D" uid="uid://cbfudkvndq872" path="res://scenes/splash/monologue_eye/frame_1.png" id="3_qdaos"] -[ext_resource type="Texture2D" uid="uid://dp3p88mc7f4k4" path="res://scenes/splash/monologue_eye/frame_3.png" id="5_783yv"] -[ext_resource type="Texture2D" uid="uid://dea6b1sjaitkq" path="res://scenes/splash/monologue_eye/frame_2.png" id="7_ueewa"] +[ext_resource type="Texture2D" uid="uid://cm2jva4mjfmbd" path="res://scenes/splash/dino/Render0013.png" id="14_wk58s"] +[ext_resource type="Texture2D" uid="uid://bp1pq3d3nohvw" path="res://scenes/splash/dino/Render0014.png" id="15_pacsv"] +[ext_resource type="Texture2D" uid="uid://ba87aujsth7cl" path="res://scenes/splash/dino/Render0015.png" id="16_tm5oe"] +[ext_resource type="Texture2D" uid="uid://cnllmukyrs07o" path="res://scenes/splash/dino/Render0017.png" id="18_7g2nk"] +[ext_resource type="Texture2D" uid="uid://bysgspu1uklaj" path="res://scenes/splash/dino/Render0018.png" id="19_hgma0"] [sub_resource type="SpriteFrames" id="SpriteFrames_tj644"] animations = [{ "frames": [{ "duration": 1.0, -"texture": ExtResource("3_qdaos") +"texture": ExtResource("14_wk58s") }, { "duration": 1.0, -"texture": ExtResource("7_ueewa") +"texture": ExtResource("15_pacsv") }, { -"duration": 2.0, -"texture": ExtResource("5_783yv") +"duration": 1.0, +"texture": ExtResource("16_tm5oe") }, { "duration": 1.0, -"texture": ExtResource("7_ueewa") +"texture": ExtResource("18_7g2nk") }, { "duration": 1.0, -"texture": ExtResource("3_qdaos") +"texture": ExtResource("19_hgma0") }], "loop": false, "name": &"blink", "speed": 25.0 }] -[node name="Splash" type="Control"] +[node name="Splash" type="Control" unique_id=1687460055] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -39,16 +40,7 @@ grow_vertical = 2 script = ExtResource("1_pl0ug") load_scene = "res://scenes/main/app.tscn" -[node name="ColorRect" type="ColorRect" parent="."] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0.117647, 0.117647, 0.129412, 1) - -[node name="CenterContainer" type="CenterContainer" parent="."] +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=986209254] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -56,18 +48,9 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="TextureRect" type="TextureRect" parent="CenterContainer"] -custom_minimum_size = Vector2(768, 0) -layout_mode = 2 -texture = ExtResource("2_rikux") -expand_mode = 5 -stretch_mode = 5 - -[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="CenterContainer/TextureRect"] -position = Vector2(142, 40) -scale = Vector2(0.263, 0.26) +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=877560656] +scale = Vector2(0.5, 0.5) sprite_frames = SubResource("SpriteFrames_tj644") animation = &"blink" - -[node name="tMinDisplayTime" type="Timer" parent="."] -one_shot = true +frame = 4 +frame_progress = 1.0 diff --git a/title_banner.png.import b/title_banner.png.import index 6f73a2b5..08291d01 100644 --- a/title_banner.png.import +++ b/title_banner.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/title_banner.png-b6af4c016a12441d70edfc4828a8 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/title_banner@0.5x.png.import b/title_banner@0.5x.png.import index b7bac214..854e49bb 100644 --- a/title_banner@0.5x.png.import +++ b/title_banner@0.5x.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/title_banner@0.5x.png-fcce4185ca69f38f8764502 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/cursors/closed_hand.svg.import b/ui/assets/cursors/closed_hand.svg.import index b12f0b11..8f9a75ab 100644 --- a/ui/assets/cursors/closed_hand.svg.import +++ b/ui/assets/cursors/closed_hand.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bdhmgrqrir12q" -path="res://.godot/imported/closed_hand.svg-2e3453a9ca7ef3b953b6711566c92755.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/closed_hand.svg-2e3453a9ca7ef3b953b6711566c92755.dpitex" [deps] source_file="res://ui/assets/cursors/closed_hand.svg" -dest_files=["res://.godot/imported/closed_hand.svg-2e3453a9ca7ef3b953b6711566c92755.ctex"] +dest_files=["res://.godot/imported/closed_hand.svg-2e3453a9ca7ef3b953b6711566c92755.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/cursors/cursor.svg.import b/ui/assets/cursors/cursor.svg.import index 26afe26e..385cb640 100644 --- a/ui/assets/cursors/cursor.svg.import +++ b/ui/assets/cursors/cursor.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dhlu3mvdkvea6" -path="res://.godot/imported/cursor.svg-cb17606446184733929991836c986519.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cursor.svg-cb17606446184733929991836c986519.dpitex" [deps] source_file="res://ui/assets/cursors/cursor.svg" -dest_files=["res://.godot/imported/cursor.svg-cb17606446184733929991836c986519.ctex"] +dest_files=["res://.godot/imported/cursor.svg-cb17606446184733929991836c986519.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/cursors/hand.svg.import b/ui/assets/cursors/hand.svg.import index 1ddf285f..dda450eb 100644 --- a/ui/assets/cursors/hand.svg.import +++ b/ui/assets/cursors/hand.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cu75pg7vsf7qi" -path="res://.godot/imported/hand.svg-9d7dd8186197dcbfc6e6ad3b81b01648.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/hand.svg-9d7dd8186197dcbfc6e6ad3b81b01648.dpitex" [deps] source_file="res://ui/assets/cursors/hand.svg" -dest_files=["res://.godot/imported/hand.svg-9d7dd8186197dcbfc6e6ad3b81b01648.ctex"] +dest_files=["res://.godot/imported/hand.svg-9d7dd8186197dcbfc6e6ad3b81b01648.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/fonts/CourierNewPS-BoldMT.ttf b/ui/assets/fonts/CourierNewPS-BoldMT.ttf deleted file mode 100644 index dbc46ba1..00000000 Binary files a/ui/assets/fonts/CourierNewPS-BoldMT.ttf and /dev/null differ diff --git a/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import b/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import deleted file mode 100644 index 9dd5ab85..00000000 --- a/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://cotgljf03hjjn" -path="res://.godot/imported/CourierNewPS-BoldMT.ttf-c648ef5d473c3c595d68d88bab164c62.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/CourierNewPS-BoldMT.ttf" -dest_files=["res://.godot/imported/CourierNewPS-BoldMT.ttf-c648ef5d473c3c595d68d88bab164c62.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf b/ui/assets/fonts/CourierNewPS-ItalicMT.ttf deleted file mode 100644 index 933e135c..00000000 Binary files a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf and /dev/null differ diff --git a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import b/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import deleted file mode 100644 index 24fe5396..00000000 --- a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://8qflao6elqsf" -path="res://.godot/imported/CourierNewPS-ItalicMT.ttf-cda42353276740c291a8082292afa8c4.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/CourierNewPS-ItalicMT.ttf" -dest_files=["res://.godot/imported/CourierNewPS-ItalicMT.ttf-cda42353276740c291a8082292afa8c4.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/CourierNewPSMT.ttf b/ui/assets/fonts/CourierNewPSMT.ttf deleted file mode 100644 index 85d82380..00000000 Binary files a/ui/assets/fonts/CourierNewPSMT.ttf and /dev/null differ diff --git a/ui/assets/fonts/CourierNewPSMT.ttf.import b/ui/assets/fonts/CourierNewPSMT.ttf.import deleted file mode 100644 index 2197aec0..00000000 --- a/ui/assets/fonts/CourierNewPSMT.ttf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://dmd74vvvqx340" -path="res://.godot/imported/CourierNewPSMT.ttf-e136e5a332e0e7367e1329529772002f.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/CourierNewPSMT.ttf" -dest_files=["res://.godot/imported/CourierNewPSMT.ttf-e136e5a332e0e7367e1329529772002f.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Bold.otf b/ui/assets/fonts/GeneralSans-Bold.otf deleted file mode 100644 index 697ce036..00000000 Binary files a/ui/assets/fonts/GeneralSans-Bold.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Bold.otf.import b/ui/assets/fonts/GeneralSans-Bold.otf.import deleted file mode 100644 index c5798a0f..00000000 --- a/ui/assets/fonts/GeneralSans-Bold.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://bikme6s8c088d" -path="res://.godot/imported/GeneralSans-Bold.otf-3ee4795681060e1323f8d61449c145a0.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Bold.otf" -dest_files=["res://.godot/imported/GeneralSans-Bold.otf-3ee4795681060e1323f8d61449c145a0.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-BoldItalic.otf b/ui/assets/fonts/GeneralSans-BoldItalic.otf deleted file mode 100644 index c0f1cdd3..00000000 Binary files a/ui/assets/fonts/GeneralSans-BoldItalic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-BoldItalic.otf.import b/ui/assets/fonts/GeneralSans-BoldItalic.otf.import deleted file mode 100644 index d504e870..00000000 --- a/ui/assets/fonts/GeneralSans-BoldItalic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://bu85isty3newe" -path="res://.godot/imported/GeneralSans-BoldItalic.otf-a543c76b3fd50891fcae5bb47f7fb7e1.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-BoldItalic.otf" -dest_files=["res://.godot/imported/GeneralSans-BoldItalic.otf-a543c76b3fd50891fcae5bb47f7fb7e1.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Extralight.otf b/ui/assets/fonts/GeneralSans-Extralight.otf deleted file mode 100644 index 5a8cb072..00000000 Binary files a/ui/assets/fonts/GeneralSans-Extralight.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Extralight.otf.import b/ui/assets/fonts/GeneralSans-Extralight.otf.import deleted file mode 100644 index cb5fe973..00000000 --- a/ui/assets/fonts/GeneralSans-Extralight.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://b5vsr0xw1xnou" -path="res://.godot/imported/GeneralSans-Extralight.otf-4cacac577c43ef23fe1c856ab0a8755b.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Extralight.otf" -dest_files=["res://.godot/imported/GeneralSans-Extralight.otf-4cacac577c43ef23fe1c856ab0a8755b.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-ExtralightItalic.otf b/ui/assets/fonts/GeneralSans-ExtralightItalic.otf deleted file mode 100644 index 28bc23e3..00000000 Binary files a/ui/assets/fonts/GeneralSans-ExtralightItalic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import b/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import deleted file mode 100644 index 4b6fe0a7..00000000 --- a/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://c6s17io1kgar0" -path="res://.godot/imported/GeneralSans-ExtralightItalic.otf-7400827c8fb3b2428b3843aac8907b97.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-ExtralightItalic.otf" -dest_files=["res://.godot/imported/GeneralSans-ExtralightItalic.otf-7400827c8fb3b2428b3843aac8907b97.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Italic.otf b/ui/assets/fonts/GeneralSans-Italic.otf deleted file mode 100644 index a4891286..00000000 Binary files a/ui/assets/fonts/GeneralSans-Italic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Italic.otf.import b/ui/assets/fonts/GeneralSans-Italic.otf.import deleted file mode 100644 index 566271a8..00000000 --- a/ui/assets/fonts/GeneralSans-Italic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://uogpodgt3g3m" -path="res://.godot/imported/GeneralSans-Italic.otf-8d3d5665dc500c87dfd9955ae8c43026.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Italic.otf" -dest_files=["res://.godot/imported/GeneralSans-Italic.otf-8d3d5665dc500c87dfd9955ae8c43026.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Light.otf b/ui/assets/fonts/GeneralSans-Light.otf deleted file mode 100644 index 6d4ca04e..00000000 Binary files a/ui/assets/fonts/GeneralSans-Light.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Light.otf.import b/ui/assets/fonts/GeneralSans-Light.otf.import deleted file mode 100644 index 8db9b3ba..00000000 --- a/ui/assets/fonts/GeneralSans-Light.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://cx1dohmwi4i8o" -path="res://.godot/imported/GeneralSans-Light.otf-ecd20e2cd7b16024f5ba0a79cf3190e2.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Light.otf" -dest_files=["res://.godot/imported/GeneralSans-Light.otf-ecd20e2cd7b16024f5ba0a79cf3190e2.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-LightItalic.otf b/ui/assets/fonts/GeneralSans-LightItalic.otf deleted file mode 100644 index 86371208..00000000 Binary files a/ui/assets/fonts/GeneralSans-LightItalic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-LightItalic.otf.import b/ui/assets/fonts/GeneralSans-LightItalic.otf.import deleted file mode 100644 index a42f79d1..00000000 --- a/ui/assets/fonts/GeneralSans-LightItalic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://b1alo8ggss31b" -path="res://.godot/imported/GeneralSans-LightItalic.otf-885d883262c87ca19f04a1e2115f2eba.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-LightItalic.otf" -dest_files=["res://.godot/imported/GeneralSans-LightItalic.otf-885d883262c87ca19f04a1e2115f2eba.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Medium.otf b/ui/assets/fonts/GeneralSans-Medium.otf deleted file mode 100644 index 42147fac..00000000 Binary files a/ui/assets/fonts/GeneralSans-Medium.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Medium.otf.import b/ui/assets/fonts/GeneralSans-Medium.otf.import deleted file mode 100644 index aba933b4..00000000 --- a/ui/assets/fonts/GeneralSans-Medium.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://xxjublydw27h" -path="res://.godot/imported/GeneralSans-Medium.otf-78d29bbba0271ce9b342121b96f2fd8c.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Medium.otf" -dest_files=["res://.godot/imported/GeneralSans-Medium.otf-78d29bbba0271ce9b342121b96f2fd8c.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-MediumItalic.otf b/ui/assets/fonts/GeneralSans-MediumItalic.otf deleted file mode 100644 index 38dddc34..00000000 Binary files a/ui/assets/fonts/GeneralSans-MediumItalic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-MediumItalic.otf.import b/ui/assets/fonts/GeneralSans-MediumItalic.otf.import deleted file mode 100644 index fa77a3b4..00000000 --- a/ui/assets/fonts/GeneralSans-MediumItalic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://2q3cv24g6aj3" -path="res://.godot/imported/GeneralSans-MediumItalic.otf-7904b55bcae53f801115fd9d8e8f1465.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-MediumItalic.otf" -dest_files=["res://.godot/imported/GeneralSans-MediumItalic.otf-7904b55bcae53f801115fd9d8e8f1465.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Regular.otf b/ui/assets/fonts/GeneralSans-Regular.otf deleted file mode 100644 index 04474391..00000000 Binary files a/ui/assets/fonts/GeneralSans-Regular.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Regular.otf.import b/ui/assets/fonts/GeneralSans-Regular.otf.import deleted file mode 100644 index 37850d24..00000000 --- a/ui/assets/fonts/GeneralSans-Regular.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://b0xdcshqi622v" -path="res://.godot/imported/GeneralSans-Regular.otf-b44d304c66ccd9d04e0f7acf8a30033c.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Regular.otf" -dest_files=["res://.godot/imported/GeneralSans-Regular.otf-b44d304c66ccd9d04e0f7acf8a30033c.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-Semibold.otf b/ui/assets/fonts/GeneralSans-Semibold.otf deleted file mode 100644 index a436638c..00000000 Binary files a/ui/assets/fonts/GeneralSans-Semibold.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-Semibold.otf.import b/ui/assets/fonts/GeneralSans-Semibold.otf.import deleted file mode 100644 index 7de9ed1e..00000000 --- a/ui/assets/fonts/GeneralSans-Semibold.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://ddhkk7pj46bsj" -path="res://.godot/imported/GeneralSans-Semibold.otf-cc72e9b985f04446ae794932fdf46271.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-Semibold.otf" -dest_files=["res://.godot/imported/GeneralSans-Semibold.otf-cc72e9b985f04446ae794932fdf46271.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/GeneralSans-SemiboldItalic.otf b/ui/assets/fonts/GeneralSans-SemiboldItalic.otf deleted file mode 100644 index 8d4cd9b2..00000000 Binary files a/ui/assets/fonts/GeneralSans-SemiboldItalic.otf and /dev/null differ diff --git a/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import b/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import deleted file mode 100644 index 94813f6d..00000000 --- a/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://b1gttavkuk17g" -path="res://.godot/imported/GeneralSans-SemiboldItalic.otf-5fd5fdce58f3cb6d56639e5311bc7d3d.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/GeneralSans-SemiboldItalic.otf" -dest_files=["res://.godot/imported/GeneralSans-SemiboldItalic.otf-5fd5fdce58f3cb6d56639e5311bc7d3d.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/Main.tres b/ui/assets/fonts/Main.tres new file mode 100644 index 00000000..967bf8dc --- /dev/null +++ b/ui/assets/fonts/Main.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontVariation" format=3 uid="uid://dbinvgq5ndfwt"] + +[ext_resource type="FontFile" uid="uid://dsredpdru3btl" path="res://ui/assets/fonts/sources/Inter-Regular.ttf" id="1_20plx"] + +[resource] +fallbacks = Array[Font]([ExtResource("1_20plx")]) +base_font = ExtResource("1_20plx") diff --git a/ui/assets/fonts/Montserrat-400.tres b/ui/assets/fonts/Montserrat-400.tres deleted file mode 100644 index 3a098dfd..00000000 --- a/ui/assets/fonts/Montserrat-400.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="FontVariation" load_steps=2 format=3 uid="uid://bmoiro3u3rsop"] - -[ext_resource type="FontFile" uid="uid://cigt33hv8jj1e" path="res://ui/assets/fonts/Montserrat-Variable.ttf" id="1_t0cnq"] - -[resource] -base_font = ExtResource("1_t0cnq") diff --git a/ui/assets/fonts/Montserrat-Variable.ttf b/ui/assets/fonts/Montserrat-Variable.ttf deleted file mode 100644 index f0b2cb32..00000000 Binary files a/ui/assets/fonts/Montserrat-Variable.ttf and /dev/null differ diff --git a/ui/assets/fonts/Montserrat-Variable.ttf.import b/ui/assets/fonts/Montserrat-Variable.ttf.import deleted file mode 100644 index 7915eb2f..00000000 --- a/ui/assets/fonts/Montserrat-Variable.ttf.import +++ /dev/null @@ -1,35 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://cigt33hv8jj1e" -path="res://.godot/imported/Montserrat-Variable.ttf-e865f9fc0ff57fd1b9724d168bbfb1df.fontdata" - -[deps] - -source_file="res://ui/assets/fonts/Montserrat-Variable.ttf" -dest_files=["res://.godot/imported/Montserrat-Variable.ttf-e865f9fc0ff57fd1b9724d168bbfb1df.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/assets/fonts/Typing.tres b/ui/assets/fonts/Typing.tres new file mode 100644 index 00000000..c58827d7 --- /dev/null +++ b/ui/assets/fonts/Typing.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontVariation" format=3 uid="uid://cq5hsgtxx5td8"] + +[ext_resource type="FontFile" uid="uid://bmmo54f514kqm" path="res://ui/assets/fonts/sources/Arvo-Regular.ttf" id="1_o2g46"] + +[resource] +fallbacks = Array[Font]([ExtResource("1_o2g46")]) +base_font = ExtResource("1_o2g46") diff --git a/ui/assets/fonts/sources/Arvo-Regular.ttf b/ui/assets/fonts/sources/Arvo-Regular.ttf new file mode 100644 index 00000000..73e8aced Binary files /dev/null and b/ui/assets/fonts/sources/Arvo-Regular.ttf differ diff --git a/ui/assets/fonts/sources/Arvo-Regular.ttf.import b/ui/assets/fonts/sources/Arvo-Regular.ttf.import new file mode 100644 index 00000000..afe6b994 --- /dev/null +++ b/ui/assets/fonts/sources/Arvo-Regular.ttf.import @@ -0,0 +1,42 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://bmmo54f514kqm" +path="res://.godot/imported/Arvo-Regular.ttf-68410080134e6458263ca9ab4207efe6.fontdata" + +[deps] + +source_file="res://ui/assets/fonts/sources/Arvo-Regular.ttf" +dest_files=["res://.godot/imported/Arvo-Regular.ttf-68410080134e6458263ca9ab4207efe6.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=2.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "Nouvelle configuration", +"size": Vector2i(16, 0), +&"variation_embolden": 0.0 +}] +language_support={} +script_support={} +opentype_features={} diff --git a/ui/assets/fonts/sources/Inter-Medium.ttf b/ui/assets/fonts/sources/Inter-Medium.ttf new file mode 100644 index 00000000..458cd060 Binary files /dev/null and b/ui/assets/fonts/sources/Inter-Medium.ttf differ diff --git a/ui/assets/fonts/sources/Inter-Medium.ttf.import b/ui/assets/fonts/sources/Inter-Medium.ttf.import new file mode 100644 index 00000000..38872d74 --- /dev/null +++ b/ui/assets/fonts/sources/Inter-Medium.ttf.import @@ -0,0 +1,42 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://cpltccs3sglfg" +path="res://.godot/imported/Inter-Medium.ttf-b1d8c3d6a07ace684220ce02dae35068.fontdata" + +[deps] + +source_file="res://ui/assets/fonts/sources/Inter-Medium.ttf" +dest_files=["res://.godot/imported/Inter-Medium.ttf-b1d8c3d6a07ace684220ce02dae35068.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=2.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "Nouvelle configuration", +"size": Vector2i(16, 0), +&"variation_embolden": 0.0 +}] +language_support={} +script_support={} +opentype_features={} diff --git a/ui/assets/fonts/sources/Inter-Regular.ttf b/ui/assets/fonts/sources/Inter-Regular.ttf new file mode 100644 index 00000000..b7aaca8d Binary files /dev/null and b/ui/assets/fonts/sources/Inter-Regular.ttf differ diff --git a/ui/assets/fonts/sources/Inter-Regular.ttf.import b/ui/assets/fonts/sources/Inter-Regular.ttf.import new file mode 100644 index 00000000..b29086fa --- /dev/null +++ b/ui/assets/fonts/sources/Inter-Regular.ttf.import @@ -0,0 +1,42 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dsredpdru3btl" +path="res://.godot/imported/Inter-Regular.ttf-1a97f22c046f33e6e24744a27c7930dd.fontdata" + +[deps] + +source_file="res://ui/assets/fonts/sources/Inter-Regular.ttf" +dest_files=["res://.godot/imported/Inter-Regular.ttf-1a97f22c046f33e6e24744a27c7930dd.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=2.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [], +"glyphs": [], +"name": "Nouvelle configuration", +"size": Vector2i(16, 0), +&"variation_embolden": 0.0 +}] +language_support={} +script_support={} +opentype_features={} diff --git a/ui/assets/icons/action.svg.import b/ui/assets/icons/action.svg.import index 366afe8a..ed024542 100644 --- a/ui/assets/icons/action.svg.import +++ b/ui/assets/icons/action.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://eihi1vti8evl" -path="res://.godot/imported/action.svg-89cea7d04591ce66b5d87f37eced72b1.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/action.svg-89cea7d04591ce66b5d87f37eced72b1.dpitex" [deps] source_file="res://ui/assets/icons/action.svg" -dest_files=["res://.godot/imported/action.svg-89cea7d04591ce66b5d87f37eced72b1.ctex"] +dest_files=["res://.godot/imported/action.svg-89cea7d04591ce66b5d87f37eced72b1.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/arrow_down.svg b/ui/assets/icons/arrow_down.svg index 7047cb0b..fc7dffbd 100644 --- a/ui/assets/icons/arrow_down.svg +++ b/ui/assets/icons/arrow_down.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/arrow_down.svg.import b/ui/assets/icons/arrow_down.svg.import index 21a0eea3..21470e6d 100644 --- a/ui/assets/icons/arrow_down.svg.import +++ b/ui/assets/icons/arrow_down.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cweh5vxhmobip" -path="res://.godot/imported/arrow_down.svg-ba844a722d541a14d34604aa5d60faed.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/arrow_down.svg-ba844a722d541a14d34604aa5d60faed.dpitex" [deps] source_file="res://ui/assets/icons/arrow_down.svg" -dest_files=["res://.godot/imported/arrow_down.svg-ba844a722d541a14d34604aa5d60faed.ctex"] +dest_files=["res://.godot/imported/arrow_down.svg-ba844a722d541a14d34604aa5d60faed.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/arrow_left.svg b/ui/assets/icons/arrow_left.svg index e2b632e9..d00eb34c 100644 --- a/ui/assets/icons/arrow_left.svg +++ b/ui/assets/icons/arrow_left.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/arrow_left.svg.import b/ui/assets/icons/arrow_left.svg.import index 7d63cc96..dc5ce7a0 100644 --- a/ui/assets/icons/arrow_left.svg.import +++ b/ui/assets/icons/arrow_left.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c0cmik715jqff" -path="res://.godot/imported/arrow_left.svg-9dffcbcd8b7c9383c91750e06cc250f7.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/arrow_left.svg-9dffcbcd8b7c9383c91750e06cc250f7.dpitex" [deps] source_file="res://ui/assets/icons/arrow_left.svg" -dest_files=["res://.godot/imported/arrow_left.svg-9dffcbcd8b7c9383c91750e06cc250f7.ctex"] +dest_files=["res://.godot/imported/arrow_left.svg-9dffcbcd8b7c9383c91750e06cc250f7.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/arrow_right.svg b/ui/assets/icons/arrow_right.svg index 058ed34d..34ebd386 100644 --- a/ui/assets/icons/arrow_right.svg +++ b/ui/assets/icons/arrow_right.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/arrow_right.svg.import b/ui/assets/icons/arrow_right.svg.import index 34d4d3a8..a2d9a94e 100644 --- a/ui/assets/icons/arrow_right.svg.import +++ b/ui/assets/icons/arrow_right.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cb6n6enqfvclw" -path="res://.godot/imported/arrow_right.svg-d5d5883fac403e011270c3f14d3a830d.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/arrow_right.svg-d5d5883fac403e011270c3f14d3a830d.dpitex" [deps] source_file="res://ui/assets/icons/arrow_right.svg" -dest_files=["res://.godot/imported/arrow_right.svg-d5d5883fac403e011270c3f14d3a830d.ctex"] +dest_files=["res://.godot/imported/arrow_right.svg-d5d5883fac403e011270c3f14d3a830d.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/arrow_up.svg b/ui/assets/icons/arrow_up.svg index 405fbfca..351459d8 100644 --- a/ui/assets/icons/arrow_up.svg +++ b/ui/assets/icons/arrow_up.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/arrow_up.svg.import b/ui/assets/icons/arrow_up.svg.import index dce78ce0..5c59badf 100644 --- a/ui/assets/icons/arrow_up.svg.import +++ b/ui/assets/icons/arrow_up.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://du0bedfddmsrg" -path="res://.godot/imported/arrow_up.svg-8c4ac4bf74e35a00d25b71c740cadc39.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/arrow_up.svg-8c4ac4bf74e35a00d25b71c740cadc39.dpitex" [deps] source_file="res://ui/assets/icons/arrow_up.svg" -dest_files=["res://.godot/imported/arrow_up.svg-8c4ac4bf74e35a00d25b71c740cadc39.ctex"] +dest_files=["res://.godot/imported/arrow_up.svg-8c4ac4bf74e35a00d25b71c740cadc39.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/bool_icon.png.import b/ui/assets/icons/bool_icon.png.import index 46f9eda8..4c9bd180 100644 --- a/ui/assets/icons/bool_icon.png.import +++ b/ui/assets/icons/bool_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/bool_icon.png-0bc2af2cd4f36c82452dbe7b6c39e89 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/calendar.svg.import b/ui/assets/icons/calendar.svg.import index 7fcd9f79..c74dc6a5 100644 --- a/ui/assets/icons/calendar.svg.import +++ b/ui/assets/icons/calendar.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bjt74refsmu3w" -path="res://.godot/imported/calendar.svg-4a7185fbaf173d39221d95b4ab58b11b.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/calendar.svg-4a7185fbaf173d39221d95b4ab58b11b.dpitex" [deps] source_file="res://ui/assets/icons/calendar.svg" -dest_files=["res://.godot/imported/calendar.svg-4a7185fbaf173d39221d95b4ab58b11b.ctex"] +dest_files=["res://.godot/imported/calendar.svg-4a7185fbaf173d39221d95b4ab58b11b.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/cell_empty.svg.import b/ui/assets/icons/cell_empty.svg.import index 4c7f7339..c568511c 100644 --- a/ui/assets/icons/cell_empty.svg.import +++ b/ui/assets/icons/cell_empty.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://nnm7d2srfc40" -path="res://.godot/imported/cell_empty.svg-3a31b0fb8349cf6d35aadccfc5d890c7.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cell_empty.svg-3a31b0fb8349cf6d35aadccfc5d890c7.dpitex" [deps] source_file="res://ui/assets/icons/cell_empty.svg" -dest_files=["res://.godot/imported/cell_empty.svg-3a31b0fb8349cf6d35aadccfc5d890c7.ctex"] +dest_files=["res://.godot/imported/cell_empty.svg-3a31b0fb8349cf6d35aadccfc5d890c7.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/cell_left.svg.import b/ui/assets/icons/cell_left.svg.import index f32f0386..6933c8dc 100644 --- a/ui/assets/icons/cell_left.svg.import +++ b/ui/assets/icons/cell_left.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c4nngbw70jdd0" -path="res://.godot/imported/cell_left.svg-a17cbd49cb4662b081893f117bad8b6b.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cell_left.svg-a17cbd49cb4662b081893f117bad8b6b.dpitex" [deps] source_file="res://ui/assets/icons/cell_left.svg" -dest_files=["res://.godot/imported/cell_left.svg-a17cbd49cb4662b081893f117bad8b6b.ctex"] +dest_files=["res://.godot/imported/cell_left.svg-a17cbd49cb4662b081893f117bad8b6b.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/cell_middle.svg.import b/ui/assets/icons/cell_middle.svg.import index 63bbb18f..291be372 100644 --- a/ui/assets/icons/cell_middle.svg.import +++ b/ui/assets/icons/cell_middle.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://e7nc5xoqpgmi" -path="res://.godot/imported/cell_middle.svg-979cd41102c064a6a9901bde47ea0298.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cell_middle.svg-979cd41102c064a6a9901bde47ea0298.dpitex" [deps] source_file="res://ui/assets/icons/cell_middle.svg" -dest_files=["res://.godot/imported/cell_middle.svg-979cd41102c064a6a9901bde47ea0298.ctex"] +dest_files=["res://.godot/imported/cell_middle.svg-979cd41102c064a6a9901bde47ea0298.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/cell_right.svg.import b/ui/assets/icons/cell_right.svg.import index 8e13952c..b96976ff 100644 --- a/ui/assets/icons/cell_right.svg.import +++ b/ui/assets/icons/cell_right.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bcow4e2eus2ik" -path="res://.godot/imported/cell_right.svg-98c96745d13a175ce2c488d00ad91fb0.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cell_right.svg-98c96745d13a175ce2c488d00ad91fb0.dpitex" [deps] source_file="res://ui/assets/icons/cell_right.svg" -dest_files=["res://.godot/imported/cell_right.svg-98c96745d13a175ce2c488d00ad91fb0.ctex"] +dest_files=["res://.godot/imported/cell_right.svg-98c96745d13a175ce2c488d00ad91fb0.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/cell_single.svg.import b/ui/assets/icons/cell_single.svg.import index 8af2441e..1a63961c 100644 --- a/ui/assets/icons/cell_single.svg.import +++ b/ui/assets/icons/cell_single.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bjra4bs2l0m6x" -path="res://.godot/imported/cell_single.svg-0c58d2764fe2b5f4012fcc9b54fc46d1.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cell_single.svg-0c58d2764fe2b5f4012fcc9b54fc46d1.dpitex" [deps] source_file="res://ui/assets/icons/cell_single.svg" -dest_files=["res://.godot/imported/cell_single.svg-0c58d2764fe2b5f4012fcc9b54fc46d1.ctex"] +dest_files=["res://.godot/imported/cell_single.svg-0c58d2764fe2b5f4012fcc9b54fc46d1.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/character.svg.import b/ui/assets/icons/character.svg.import index 3ef8ec84..742739d7 100644 --- a/ui/assets/icons/character.svg.import +++ b/ui/assets/icons/character.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bfmsxfn26cvfn" -path="res://.godot/imported/character.svg-9eaafa5794c4e9dcd6a6e2176ac897bc.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/character.svg-9eaafa5794c4e9dcd6a6e2176ac897bc.dpitex" [deps] source_file="res://ui/assets/icons/character.svg" -dest_files=["res://.godot/imported/character.svg-9eaafa5794c4e9dcd6a6e2176ac897bc.ctex"] +dest_files=["res://.godot/imported/character.svg-9eaafa5794c4e9dcd6a6e2176ac897bc.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/check.svg b/ui/assets/icons/check.svg deleted file mode 100644 index 4f6ca09d..00000000 --- a/ui/assets/icons/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/assets/icons/check.svg.import b/ui/assets/icons/check.svg.import deleted file mode 100644 index d401f7a7..00000000 --- a/ui/assets/icons/check.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://rrcqi68frkrm" -path="res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/check.svg" -dest_files=["res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/checked.svg b/ui/assets/icons/checked.svg new file mode 100644 index 00000000..ef739362 --- /dev/null +++ b/ui/assets/icons/checked.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/checked.svg.import b/ui/assets/icons/checked.svg.import new file mode 100644 index 00000000..ed845da3 --- /dev/null +++ b/ui/assets/icons/checked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://civ1cbpvjfswb" +path="res://.godot/imported/checked.svg-2f7dcb6f509d8c82cffa0bcc78563c75.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/checked.svg" +dest_files=["res://.godot/imported/checked.svg-2f7dcb6f509d8c82cffa0bcc78563c75.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/checked_disabled.svg b/ui/assets/icons/checked_disabled.svg new file mode 100644 index 00000000..2ab8c7a8 --- /dev/null +++ b/ui/assets/icons/checked_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/checked_disabled.svg.import b/ui/assets/icons/checked_disabled.svg.import new file mode 100644 index 00000000..35c38e4b --- /dev/null +++ b/ui/assets/icons/checked_disabled.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cu0dw5e2c6p4d" +path="res://.godot/imported/checked_disabled.svg-e9ca4c2f2df2d78307cb6d75382c0711.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/checked_disabled.svg" +dest_files=["res://.godot/imported/checked_disabled.svg-e9ca4c2f2df2d78307cb6d75382c0711.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/choice.svg.import b/ui/assets/icons/choice.svg.import index 0e8cb413..dc1f6f5c 100644 --- a/ui/assets/icons/choice.svg.import +++ b/ui/assets/icons/choice.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cx0jcmldcyn1n" -path="res://.godot/imported/choice.svg-affa86a6b966b61fb0ef343a93bbeb1b.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/choice.svg-affa86a6b966b61fb0ef343a93bbeb1b.dpitex" [deps] source_file="res://ui/assets/icons/choice.svg" -dest_files=["res://.godot/imported/choice.svg-affa86a6b966b61fb0ef343a93bbeb1b.ctex"] +dest_files=["res://.godot/imported/choice.svg-affa86a6b966b61fb0ef343a93bbeb1b.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/close_icon.png.import b/ui/assets/icons/close_icon.png.import index c2a8dd36..0d518ee4 100644 --- a/ui/assets/icons/close_icon.png.import +++ b/ui/assets/icons/close_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/close_icon.png-63e207f5d16862e2844785b444e12a compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/close_icon_black.png.import b/ui/assets/icons/close_icon_black.png.import index 96372ff7..a9a79926 100644 --- a/ui/assets/icons/close_icon_black.png.import +++ b/ui/assets/icons/close_icon_black.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/close_icon_black.png-2d4e6bbb111eb762f36fc575 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/comment.svg.import b/ui/assets/icons/comment.svg.import index e255d599..b4143b3b 100644 --- a/ui/assets/icons/comment.svg.import +++ b/ui/assets/icons/comment.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dupbtpe7jq82k" -path="res://.godot/imported/comment.svg-5e3bbeaaef2d610777ecffd93b566df6.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/comment.svg-5e3bbeaaef2d610777ecffd93b566df6.dpitex" [deps] source_file="res://ui/assets/icons/comment.svg" -dest_files=["res://.godot/imported/comment.svg-5e3bbeaaef2d610777ecffd93b566df6.ctex"] +dest_files=["res://.godot/imported/comment.svg-5e3bbeaaef2d610777ecffd93b566df6.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/condition.svg.import b/ui/assets/icons/condition.svg.import index 7575f16b..25fd85e4 100644 --- a/ui/assets/icons/condition.svg.import +++ b/ui/assets/icons/condition.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://iqm5av5qduiw" -path="res://.godot/imported/condition.svg-e465084597d0556fe9eb0b96268215eb.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/condition.svg-e465084597d0556fe9eb0b96268215eb.dpitex" [deps] source_file="res://ui/assets/icons/condition.svg" -dest_files=["res://.godot/imported/condition.svg-e465084597d0556fe9eb0b96268215eb.ctex"] +dest_files=["res://.godot/imported/condition.svg-e465084597d0556fe9eb0b96268215eb.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/config_icon.png.import b/ui/assets/icons/config_icon.png.import index 27f78dc4..1df49094 100644 --- a/ui/assets/icons/config_icon.png.import +++ b/ui/assets/icons/config_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/config_icon.png-ea533e1ab946d067ed90bb64ca50a compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/copy.png.import b/ui/assets/icons/copy.png.import index dcbade5a..aeeec590 100644 --- a/ui/assets/icons/copy.png.import +++ b/ui/assets/icons/copy.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/copy.png-581491c44d4bbc2a345e45100bab1871.cte compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/cross.svg b/ui/assets/icons/cross.svg index 8d77b6e0..50d12923 100644 --- a/ui/assets/icons/cross.svg +++ b/ui/assets/icons/cross.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/cross.svg.import b/ui/assets/icons/cross.svg.import index 3ed6436f..ed2ad3dc 100644 --- a/ui/assets/icons/cross.svg.import +++ b/ui/assets/icons/cross.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://b0umav8s8l2qa" -path="res://.godot/imported/cross.svg-b21953d55678fcb60f5e327c0ce12c2c.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/cross.svg-b21953d55678fcb60f5e327c0ce12c2c.dpitex" [deps] source_file="res://ui/assets/icons/cross.svg" -dest_files=["res://.godot/imported/cross.svg-b21953d55678fcb60f5e327c0ce12c2c.ctex"] +dest_files=["res://.godot/imported/cross.svg-b21953d55678fcb60f5e327c0ce12c2c.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/dice.svg.import b/ui/assets/icons/dice.svg.import index 592ce0ab..17f6bd13 100644 --- a/ui/assets/icons/dice.svg.import +++ b/ui/assets/icons/dice.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dbi7c572d4f8y" -path="res://.godot/imported/dice.svg-3fcf558d02707b2ab7683e8b71f673ad.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/dice.svg-3fcf558d02707b2ab7683e8b71f673ad.dpitex" [deps] source_file="res://ui/assets/icons/dice.svg" -dest_files=["res://.godot/imported/dice.svg-3fcf558d02707b2ab7683e8b71f673ad.ctex"] +dest_files=["res://.godot/imported/dice.svg-3fcf558d02707b2ab7683e8b71f673ad.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/dot_icon.png.import b/ui/assets/icons/dot_icon.png.import index da709840..d5349242 100644 --- a/ui/assets/icons/dot_icon.png.import +++ b/ui/assets/icons/dot_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/dot_icon.png-52c3b31fad0194c0c36339bdd3bde68c compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/dots_grid.svg.import b/ui/assets/icons/dots_grid.svg.import index 4935848f..9ca2d74a 100644 --- a/ui/assets/icons/dots_grid.svg.import +++ b/ui/assets/icons/dots_grid.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bv81uyypsna2u" -path="res://.godot/imported/dots_grid.svg-5b2af8497b8455d866dc46f705a3a432.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/dots_grid.svg-5b2af8497b8455d866dc46f705a3a432.dpitex" [deps] source_file="res://ui/assets/icons/dots_grid.svg" -dest_files=["res://.godot/imported/dots_grid.svg-5b2af8497b8455d866dc46f705a3a432.ctex"] +dest_files=["res://.godot/imported/dots_grid.svg-5b2af8497b8455d866dc46f705a3a432.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/drag_handle.svg b/ui/assets/icons/drag_handle.svg new file mode 100644 index 00000000..a59b6830 --- /dev/null +++ b/ui/assets/icons/drag_handle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/assets/icons/drag_handle.svg.import b/ui/assets/icons/drag_handle.svg.import new file mode 100644 index 00000000..cc5157ca --- /dev/null +++ b/ui/assets/icons/drag_handle.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://duxo8j3hmik4v" +path="res://.godot/imported/drag_handle.svg-22727e25d846efe23f62a724fa5b584e.dpitex" + +[deps] + +source_file="res://ui/assets/icons/drag_handle.svg" +dest_files=["res://.godot/imported/drag_handle.svg-22727e25d846efe23f62a724fa5b584e.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/exit.png b/ui/assets/icons/exit.png deleted file mode 100644 index 835668fe..00000000 Binary files a/ui/assets/icons/exit.png and /dev/null differ diff --git a/ui/assets/icons/exit.png.import b/ui/assets/icons/exit.png.import deleted file mode 100644 index 26b5d016..00000000 --- a/ui/assets/icons/exit.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dccbv4x7joj8a" -path="res://.godot/imported/exit.png-89bb20104225ee47e1c62bcf63e94c1a.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/exit.png" -dest_files=["res://.godot/imported/exit.png-89bb20104225ee47e1c62bcf63e94c1a.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/assets/icons/exit.svg b/ui/assets/icons/exit.svg deleted file mode 100644 index a27d514b..00000000 --- a/ui/assets/icons/exit.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/ui/assets/icons/exit.svg.import b/ui/assets/icons/exit.svg.import deleted file mode 100644 index 703dc4c2..00000000 --- a/ui/assets/icons/exit.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b8r5v6gy8jyyn" -path="res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/exit.svg" -dest_files=["res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/expand.svg.import b/ui/assets/icons/expand.svg.import index 61d9e5df..d53c0165 100644 --- a/ui/assets/icons/expand.svg.import +++ b/ui/assets/icons/expand.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bu603ytypk2jb" -path="res://.godot/imported/expand.svg-62a0a6cb2ee0399977756028db93b4c0.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/expand.svg-62a0a6cb2ee0399977756028db93b4c0.dpitex" [deps] source_file="res://ui/assets/icons/expand.svg" -dest_files=["res://.godot/imported/expand.svg-62a0a6cb2ee0399977756028db93b4c0.ctex"] +dest_files=["res://.godot/imported/expand.svg-62a0a6cb2ee0399977756028db93b4c0.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/expanded_arrow.svg b/ui/assets/icons/expanded_arrow.svg new file mode 100644 index 00000000..3195d854 --- /dev/null +++ b/ui/assets/icons/expanded_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/expanded_arrow.svg.import b/ui/assets/icons/expanded_arrow.svg.import new file mode 100644 index 00000000..f950e968 --- /dev/null +++ b/ui/assets/icons/expanded_arrow.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://ccpwwuc3cpepg" +path="res://.godot/imported/expanded_arrow.svg-53508031ccbb7d1da903c6e662ed0b2c.dpitex" + +[deps] + +source_file="res://ui/assets/icons/expanded_arrow.svg" +dest_files=["res://.godot/imported/expanded_arrow.svg-53508031ccbb7d1da903c6e662ed0b2c.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/expanded_arrow_mirrored.svg b/ui/assets/icons/expanded_arrow_mirrored.svg new file mode 100644 index 00000000..42e9bad2 --- /dev/null +++ b/ui/assets/icons/expanded_arrow_mirrored.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/expanded_arrow_mirrored.svg.import b/ui/assets/icons/expanded_arrow_mirrored.svg.import new file mode 100644 index 00000000..8ffe79ad --- /dev/null +++ b/ui/assets/icons/expanded_arrow_mirrored.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://2brg7g4guhyf" +path="res://.godot/imported/expanded_arrow_mirrored.svg-4adf772a810f88634a159a22debd9bc1.dpitex" + +[deps] + +source_file="res://ui/assets/icons/expanded_arrow_mirrored.svg" +dest_files=["res://.godot/imported/expanded_arrow_mirrored.svg-4adf772a810f88634a159a22debd9bc1.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/exposed.svg b/ui/assets/icons/exposed.svg new file mode 100644 index 00000000..0c87ff88 --- /dev/null +++ b/ui/assets/icons/exposed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/icons/exposed.svg.import b/ui/assets/icons/exposed.svg.import new file mode 100644 index 00000000..9757f5f9 --- /dev/null +++ b/ui/assets/icons/exposed.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://d4fp2shj4o5sk" +path="res://.godot/imported/exposed.svg-d424bd564c350cae69407722de283e3e.dpitex" + +[deps] + +source_file="res://ui/assets/icons/exposed.svg" +dest_files=["res://.godot/imported/exposed.svg-d424bd564c350cae69407722de283e3e.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/eye.svg.import b/ui/assets/icons/eye.svg.import index c2bacc97..3262428d 100644 --- a/ui/assets/icons/eye.svg.import +++ b/ui/assets/icons/eye.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dh2il37b3nhyo" -path="res://.godot/imported/eye.svg-5a82bbf625997d04d893b49e9429d918.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/eye.svg-5a82bbf625997d04d893b49e9429d918.dpitex" [deps] source_file="res://ui/assets/icons/eye.svg" -dest_files=["res://.godot/imported/eye.svg-5a82bbf625997d04d893b49e9429d918.ctex"] +dest_files=["res://.godot/imported/eye.svg-5a82bbf625997d04d893b49e9429d918.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/eye_closed.svg.import b/ui/assets/icons/eye_closed.svg.import index e979e831..b7125158 100644 --- a/ui/assets/icons/eye_closed.svg.import +++ b/ui/assets/icons/eye_closed.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bjbs0odxsndlq" -path="res://.godot/imported/eye_closed.svg-1d4509f467a37dac8e22fc42086b82fc.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/eye_closed.svg-1d4509f467a37dac8e22fc42086b82fc.dpitex" [deps] source_file="res://ui/assets/icons/eye_closed.svg" -dest_files=["res://.godot/imported/eye_closed.svg-1d4509f467a37dac8e22fc42086b82fc.ctex"] +dest_files=["res://.godot/imported/eye_closed.svg-1d4509f467a37dac8e22fc42086b82fc.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/file.svg.import b/ui/assets/icons/file.svg.import index 23741512..56536d57 100644 --- a/ui/assets/icons/file.svg.import +++ b/ui/assets/icons/file.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c77fmeslcdgo7" -path="res://.godot/imported/file.svg-dd7077f29278cdb1656a9b1ebb36e6bd.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/file.svg-dd7077f29278cdb1656a9b1ebb36e6bd.dpitex" [deps] source_file="res://ui/assets/icons/file.svg" -dest_files=["res://.godot/imported/file.svg-dd7077f29278cdb1656a9b1ebb36e6bd.ctex"] +dest_files=["res://.godot/imported/file.svg-dd7077f29278cdb1656a9b1ebb36e6bd.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/file_character.svg.import b/ui/assets/icons/file_character.svg.import index 6f672434..afda0d52 100644 --- a/ui/assets/icons/file_character.svg.import +++ b/ui/assets/icons/file_character.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://tnslcne4ae6q" -path="res://.godot/imported/file_character.svg-927860812a469cabc966bd5edd0f8b95.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/file_character.svg-927860812a469cabc966bd5edd0f8b95.dpitex" [deps] source_file="res://ui/assets/icons/file_character.svg" -dest_files=["res://.godot/imported/file_character.svg-927860812a469cabc966bd5edd0f8b95.ctex"] +dest_files=["res://.godot/imported/file_character.svg-927860812a469cabc966bd5edd0f8b95.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/file_plus.svg.import b/ui/assets/icons/file_plus.svg.import index 175447c1..83eb9b31 100644 --- a/ui/assets/icons/file_plus.svg.import +++ b/ui/assets/icons/file_plus.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://faci8gjgsxub" -path="res://.godot/imported/file_plus.svg-e9ac3957ca4145979ac72d07cde5f05a.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/file_plus.svg-e9ac3957ca4145979ac72d07cde5f05a.dpitex" [deps] source_file="res://ui/assets/icons/file_plus.svg" -dest_files=["res://.godot/imported/file_plus.svg-e9ac3957ca4145979ac72d07cde5f05a.ctex"] +dest_files=["res://.godot/imported/file_plus.svg-e9ac3957ca4145979ac72d07cde5f05a.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/filter_funnel.svg.import b/ui/assets/icons/filter_funnel.svg.import index 09f03a6e..768c4e5a 100644 --- a/ui/assets/icons/filter_funnel.svg.import +++ b/ui/assets/icons/filter_funnel.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c6xst2uouq7d2" -path="res://.godot/imported/filter_funnel.svg-7a9398c7c058c3d3c6c389b09e9652ce.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/filter_funnel.svg-7a9398c7c058c3d3c6c389b09e9652ce.dpitex" [deps] source_file="res://ui/assets/icons/filter_funnel.svg" -dest_files=["res://.godot/imported/filter_funnel.svg-7a9398c7c058c3d3c6c389b09e9652ce.ctex"] +dest_files=["res://.godot/imported/filter_funnel.svg-7a9398c7c058c3d3c6c389b09e9652ce.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/folded_arrow.svg b/ui/assets/icons/folded_arrow.svg new file mode 100644 index 00000000..ac1293a0 --- /dev/null +++ b/ui/assets/icons/folded_arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/folded_arrow.svg.import b/ui/assets/icons/folded_arrow.svg.import new file mode 100644 index 00000000..d50cf3c2 --- /dev/null +++ b/ui/assets/icons/folded_arrow.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://c5cnrdqhe57bk" +path="res://.godot/imported/folded_arrow.svg-43579267d591745750cea0e23acf0b61.dpitex" + +[deps] + +source_file="res://ui/assets/icons/folded_arrow.svg" +dest_files=["res://.godot/imported/folded_arrow.svg-43579267d591745750cea0e23acf0b61.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/folded_arrow_mirrored.svg b/ui/assets/icons/folded_arrow_mirrored.svg new file mode 100644 index 00000000..db76bb9d --- /dev/null +++ b/ui/assets/icons/folded_arrow_mirrored.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/folded_arrow_mirrored.svg.import b/ui/assets/icons/folded_arrow_mirrored.svg.import new file mode 100644 index 00000000..7a98b524 --- /dev/null +++ b/ui/assets/icons/folded_arrow_mirrored.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://b1alqqob1xpnn" +path="res://.godot/imported/folded_arrow_mirrored.svg-2d8262a811b3351dd960a96936ef2dda.dpitex" + +[deps] + +source_file="res://ui/assets/icons/folded_arrow_mirrored.svg" +dest_files=["res://.godot/imported/folded_arrow_mirrored.svg-2d8262a811b3351dd960a96936ef2dda.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/folder.svg.import b/ui/assets/icons/folder.svg.import index ea53cb16..5a2bfd13 100644 --- a/ui/assets/icons/folder.svg.import +++ b/ui/assets/icons/folder.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://87majwdgoajd" -path="res://.godot/imported/folder.svg-18ec41b2c3c3a12bc6a3e991fda7eb40.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/folder.svg-18ec41b2c3c3a12bc6a3e991fda7eb40.dpitex" [deps] source_file="res://ui/assets/icons/folder.svg" -dest_files=["res://.godot/imported/folder.svg-18ec41b2c3c3a12bc6a3e991fda7eb40.ctex"] +dest_files=["res://.godot/imported/folder.svg-18ec41b2c3c3a12bc6a3e991fda7eb40.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/folder_download.svg.import b/ui/assets/icons/folder_download.svg.import index bc38bfae..8a9db0df 100644 --- a/ui/assets/icons/folder_download.svg.import +++ b/ui/assets/icons/folder_download.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dmx3uqn8v7q4e" -path="res://.godot/imported/folder_download.svg-9cf05bfddbd173374b7055956c4e8343.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/folder_download.svg-9cf05bfddbd173374b7055956c4e8343.dpitex" [deps] source_file="res://ui/assets/icons/folder_download.svg" -dest_files=["res://.godot/imported/folder_download.svg-9cf05bfddbd173374b7055956c4e8343.ctex"] +dest_files=["res://.godot/imported/folder_download.svg-9cf05bfddbd173374b7055956c4e8343.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/folder_icon.png.import b/ui/assets/icons/folder_icon.png.import index 5f750299..aedfc776 100644 --- a/ui/assets/icons/folder_icon.png.import +++ b/ui/assets/icons/folder_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/folder_icon.png-07715a9796114aae484d3567d0abe compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/folder_search.svg.import b/ui/assets/icons/folder_search.svg.import index c6ab36a9..a5f968af 100644 --- a/ui/assets/icons/folder_search.svg.import +++ b/ui/assets/icons/folder_search.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bbw07aw88fo0p" -path="res://.godot/imported/folder_search.svg-4362508f18fefe7404e910c84c8304e2.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/folder_search.svg-4362508f18fefe7404e910c84c8304e2.dpitex" [deps] source_file="res://ui/assets/icons/folder_search.svg" -dest_files=["res://.godot/imported/folder_search.svg-4362508f18fefe7404e910c84c8304e2.ctex"] +dest_files=["res://.godot/imported/folder_search.svg-4362508f18fefe7404e910c84c8304e2.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/globe.svg b/ui/assets/icons/globe.svg new file mode 100644 index 00000000..638e1222 --- /dev/null +++ b/ui/assets/icons/globe.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/globe.svg.import b/ui/assets/icons/globe.svg.import new file mode 100644 index 00000000..70a97e10 --- /dev/null +++ b/ui/assets/icons/globe.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://e3jit73s1eld" +path="res://.godot/imported/globe.svg-b83789c009862cf95dc68ebc6906d10f.dpitex" + +[deps] + +source_file="res://ui/assets/icons/globe.svg" +dest_files=["res://.godot/imported/globe.svg-b83789c009862cf95dc68ebc6906d10f.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/grid.svg b/ui/assets/icons/grid.svg index b21bebc7..0b89798f 100644 --- a/ui/assets/icons/grid.svg +++ b/ui/assets/icons/grid.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/ui/assets/icons/grid.svg.import b/ui/assets/icons/grid.svg.import index 5c8fffcf..b0d8bd88 100644 --- a/ui/assets/icons/grid.svg.import +++ b/ui/assets/icons/grid.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cvel6ifgyclof" -path="res://.godot/imported/grid.svg-6a34e8c39e1213823a63f4b397a10223.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/grid.svg-6a34e8c39e1213823a63f4b397a10223.dpitex" [deps] source_file="res://ui/assets/icons/grid.svg" -dest_files=["res://.godot/imported/grid.svg-6a34e8c39e1213823a63f4b397a10223.ctex"] +dest_files=["res://.godot/imported/grid.svg-6a34e8c39e1213823a63f4b397a10223.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/help_icon.png.import b/ui/assets/icons/help_icon.png.import index 74bf7410..b6711cc8 100644 --- a/ui/assets/icons/help_icon.png.import +++ b/ui/assets/icons/help_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/help_icon.png-9b4e9b76a02c80aaa31a337cd102241 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/image_down.svg.import b/ui/assets/icons/image_down.svg.import index 17f4d982..4bb6d997 100644 --- a/ui/assets/icons/image_down.svg.import +++ b/ui/assets/icons/image_down.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bey5byxlx7jji" -path="res://.godot/imported/image_down.svg-acc4a344e8c65c7feb6c94aaef10f508.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/image_down.svg-acc4a344e8c65c7feb6c94aaef10f508.dpitex" [deps] source_file="res://ui/assets/icons/image_down.svg" -dest_files=["res://.godot/imported/image_down.svg-acc4a344e8c65c7feb6c94aaef10f508.ctex"] +dest_files=["res://.godot/imported/image_down.svg-acc4a344e8c65c7feb6c94aaef10f508.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/improt_cell.svg.import b/ui/assets/icons/improt_cell.svg.import index 20253c8d..9d2fa43b 100644 --- a/ui/assets/icons/improt_cell.svg.import +++ b/ui/assets/icons/improt_cell.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c07ekrem76mk3" -path="res://.godot/imported/improt_cell.svg-69131e44375e64e7e1b2fda89997b214.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/improt_cell.svg-69131e44375e64e7e1b2fda89997b214.dpitex" [deps] source_file="res://ui/assets/icons/improt_cell.svg" -dest_files=["res://.godot/imported/improt_cell.svg-69131e44375e64e7e1b2fda89997b214.ctex"] +dest_files=["res://.godot/imported/improt_cell.svg-69131e44375e64e7e1b2fda89997b214.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=2.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/int_icon.png.import b/ui/assets/icons/int_icon.png.import index 25826a26..ed16037f 100644 --- a/ui/assets/icons/int_icon.png.import +++ b/ui/assets/icons/int_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/int_icon.png-ed7c36cd8a0633daff3e0cf227bd235d compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/language.svg b/ui/assets/icons/language.svg new file mode 100644 index 00000000..65539120 --- /dev/null +++ b/ui/assets/icons/language.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/language.svg.import b/ui/assets/icons/language.svg.import new file mode 100644 index 00000000..4360c0ba --- /dev/null +++ b/ui/assets/icons/language.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b8g668axs0g2" +path="res://.godot/imported/language.svg-b86b1a6133449c029ab0c700408fd96a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/language.svg" +dest_files=["res://.godot/imported/language.svg-b86b1a6133449c029ab0c700408fd96a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/layout_right.svg.import b/ui/assets/icons/layout_right.svg.import index d43c0029..62a21b4f 100644 --- a/ui/assets/icons/layout_right.svg.import +++ b/ui/assets/icons/layout_right.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cfpgu1noth4uf" -path="res://.godot/imported/layout_right.svg-31c3b25a7924e01b1337c6ab228028e5.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/layout_right.svg-31c3b25a7924e01b1337c6ab228028e5.dpitex" [deps] source_file="res://ui/assets/icons/layout_right.svg" -dest_files=["res://.godot/imported/layout_right.svg-31c3b25a7924e01b1337c6ab228028e5.ctex"] +dest_files=["res://.godot/imported/layout_right.svg-31c3b25a7924e01b1337c6ab228028e5.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/link.svg.import b/ui/assets/icons/link.svg.import index c255a162..7502cb7b 100644 --- a/ui/assets/icons/link.svg.import +++ b/ui/assets/icons/link.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://q4df0swsn207" -path="res://.godot/imported/link.svg-da884fb1938f339f47467a3fa13a6c67.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/link.svg-da884fb1938f339f47467a3fa13a6c67.dpitex" [deps] source_file="res://ui/assets/icons/link.svg" -dest_files=["res://.godot/imported/link.svg-da884fb1938f339f47467a3fa13a6c67.ctex"] +dest_files=["res://.godot/imported/link.svg-da884fb1938f339f47467a3fa13a6c67.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/logo_white.svg b/ui/assets/icons/logo_white.svg deleted file mode 100644 index fe886d6e..00000000 --- a/ui/assets/icons/logo_white.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/ui/assets/icons/logo_white.svg.import b/ui/assets/icons/logo_white.svg.import deleted file mode 100644 index 5f615f9d..00000000 --- a/ui/assets/icons/logo_white.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dd8v3hpxxk33d" -path="res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/logo_white.svg" -dest_files=["res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/magnet.svg b/ui/assets/icons/magnet.svg new file mode 100644 index 00000000..a24f45ea --- /dev/null +++ b/ui/assets/icons/magnet.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/assets/icons/magnet.svg.import b/ui/assets/icons/magnet.svg.import new file mode 100644 index 00000000..4f2b0a8a --- /dev/null +++ b/ui/assets/icons/magnet.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://bl4t6n5wyuelg" +path="res://.godot/imported/magnet.svg-4e0e0a83cb1037fe796877bbf9213e60.dpitex" + +[deps] + +source_file="res://ui/assets/icons/magnet.svg" +dest_files=["res://.godot/imported/magnet.svg-4e0e0a83cb1037fe796877bbf9213e60.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/media_loop.svg.import b/ui/assets/icons/media_loop.svg.import index 05510620..21df280b 100644 --- a/ui/assets/icons/media_loop.svg.import +++ b/ui/assets/icons/media_loop.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://b5pe3oxlthl2i" -path="res://.godot/imported/media_loop.svg-bfcb30a7b7efe13bb9befc850fea02ad.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_loop.svg-bfcb30a7b7efe13bb9befc850fea02ad.dpitex" [deps] source_file="res://ui/assets/icons/media_loop.svg" -dest_files=["res://.godot/imported/media_loop.svg-bfcb30a7b7efe13bb9befc850fea02ad.ctex"] +dest_files=["res://.godot/imported/media_loop.svg-bfcb30a7b7efe13bb9befc850fea02ad.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/media_play.svg.import b/ui/assets/icons/media_play.svg.import index ad200ffd..17a7254f 100644 --- a/ui/assets/icons/media_play.svg.import +++ b/ui/assets/icons/media_play.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bficqn5yhfmah" -path="res://.godot/imported/media_play.svg-5cf4a5c1d08beef6e9ebbc5baefb537a.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_play.svg-5cf4a5c1d08beef6e9ebbc5baefb537a.dpitex" [deps] source_file="res://ui/assets/icons/media_play.svg" -dest_files=["res://.godot/imported/media_play.svg-5cf4a5c1d08beef6e9ebbc5baefb537a.ctex"] +dest_files=["res://.godot/imported/media_play.svg-5cf4a5c1d08beef6e9ebbc5baefb537a.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/media_play_backward.svg.import b/ui/assets/icons/media_play_backward.svg.import index e8d39a96..d916115f 100644 --- a/ui/assets/icons/media_play_backward.svg.import +++ b/ui/assets/icons/media_play_backward.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://08g8utdd2kai" -path="res://.godot/imported/media_play_backward.svg-6bfdbfa5122824cfde055f679f4a4078.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_play_backward.svg-6bfdbfa5122824cfde055f679f4a4078.dpitex" [deps] source_file="res://ui/assets/icons/media_play_backward.svg" -dest_files=["res://.godot/imported/media_play_backward.svg-6bfdbfa5122824cfde055f679f4a4078.ctex"] +dest_files=["res://.godot/imported/media_play_backward.svg-6bfdbfa5122824cfde055f679f4a4078.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/media_skip_backward.svg.import b/ui/assets/icons/media_skip_backward.svg.import index 9646ce33..13f76177 100644 --- a/ui/assets/icons/media_skip_backward.svg.import +++ b/ui/assets/icons/media_skip_backward.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bowdhs0emx5i2" -path="res://.godot/imported/media_skip_backward.svg-99b74c1f4f45bf5e508ef1508a91cedb.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_skip_backward.svg-99b74c1f4f45bf5e508ef1508a91cedb.dpitex" [deps] source_file="res://ui/assets/icons/media_skip_backward.svg" -dest_files=["res://.godot/imported/media_skip_backward.svg-99b74c1f4f45bf5e508ef1508a91cedb.ctex"] +dest_files=["res://.godot/imported/media_skip_backward.svg-99b74c1f4f45bf5e508ef1508a91cedb.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/media_skip_forward.svg.import b/ui/assets/icons/media_skip_forward.svg.import index 9d78cf43..9f5c6588 100644 --- a/ui/assets/icons/media_skip_forward.svg.import +++ b/ui/assets/icons/media_skip_forward.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://8cxo7ofybuix" -path="res://.godot/imported/media_skip_forward.svg-8acd7a6efa3466e2a74163538b55041d.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_skip_forward.svg-8acd7a6efa3466e2a74163538b55041d.dpitex" [deps] source_file="res://ui/assets/icons/media_skip_forward.svg" -dest_files=["res://.godot/imported/media_skip_forward.svg-8acd7a6efa3466e2a74163538b55041d.ctex"] +dest_files=["res://.godot/imported/media_skip_forward.svg-8acd7a6efa3466e2a74163538b55041d.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/media_stop.svg.import b/ui/assets/icons/media_stop.svg.import index b370ed48..b2b42ea6 100644 --- a/ui/assets/icons/media_stop.svg.import +++ b/ui/assets/icons/media_stop.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://ctm64x2sdw2y2" -path="res://.godot/imported/media_stop.svg-a1a80d37ff29a5f89fd182a2ee5be62c.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/media_stop.svg-a1a80d37ff29a5f89fd182a2ee5be62c.dpitex" [deps] source_file="res://ui/assets/icons/media_stop.svg" -dest_files=["res://.godot/imported/media_stop.svg-a1a80d37ff29a5f89fd182a2ee5be62c.ctex"] +dest_files=["res://.godot/imported/media_stop.svg-a1a80d37ff29a5f89fd182a2ee5be62c.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/menu.svg b/ui/assets/icons/menu.svg new file mode 100644 index 00000000..f8f00f8d --- /dev/null +++ b/ui/assets/icons/menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/menu.svg.import b/ui/assets/icons/menu.svg.import new file mode 100644 index 00000000..01db2494 --- /dev/null +++ b/ui/assets/icons/menu.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://ctciioed1ksts" +path="res://.godot/imported/menu.svg-f4b40f9bebb8dde7f2749ef939803317.dpitex" + +[deps] + +source_file="res://ui/assets/icons/menu.svg" +dest_files=["res://.godot/imported/menu.svg-f4b40f9bebb8dde7f2749ef939803317.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/menu_checked.svg b/ui/assets/icons/menu_checked.svg new file mode 100644 index 00000000..3254422a --- /dev/null +++ b/ui/assets/icons/menu_checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/icons/menu_checked.svg.import b/ui/assets/icons/menu_checked.svg.import new file mode 100644 index 00000000..f7cbcca6 --- /dev/null +++ b/ui/assets/icons/menu_checked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djl0xsdt87ifa" +path="res://.godot/imported/menu_checked.svg-4a9f84069b1d42e5e958e56ae990e0f4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/menu_checked.svg" +dest_files=["res://.godot/imported/menu_checked.svg-4a9f84069b1d42e5e958e56ae990e0f4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/menu_unchecked.svg b/ui/assets/icons/menu_unchecked.svg new file mode 100644 index 00000000..9724e320 --- /dev/null +++ b/ui/assets/icons/menu_unchecked.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/menu_unchecked.svg.import b/ui/assets/icons/menu_unchecked.svg.import new file mode 100644 index 00000000..8ac782ee --- /dev/null +++ b/ui/assets/icons/menu_unchecked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b838jlax6ppl7" +path="res://.godot/imported/menu_unchecked.svg-c117f997aed72f81ee6e085ef85453c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/menu_unchecked.svg" +dest_files=["res://.godot/imported/menu_unchecked.svg-c117f997aed72f81ee6e085ef85453c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/minimize_ui.svg b/ui/assets/icons/minimize_ui.svg new file mode 100644 index 00000000..2feb0712 --- /dev/null +++ b/ui/assets/icons/minimize_ui.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/icons/minimize_ui.svg.import b/ui/assets/icons/minimize_ui.svg.import new file mode 100644 index 00000000..0b01e60e --- /dev/null +++ b/ui/assets/icons/minimize_ui.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://jeypaf8vd3sf" +path="res://.godot/imported/minimize_ui.svg-a7f7091d30d61e3aa7a9e1b1719fbf22.dpitex" + +[deps] + +source_file="res://ui/assets/icons/minimize_ui.svg" +dest_files=["res://.godot/imported/minimize_ui.svg-a7f7091d30d61e3aa7a9e1b1719fbf22.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/minus_min.svg.import b/ui/assets/icons/minus_min.svg.import index 3b67b8d8..ebf8ee8f 100644 --- a/ui/assets/icons/minus_min.svg.import +++ b/ui/assets/icons/minus_min.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://2avo8aox8ls2" -path="res://.godot/imported/minus_min.svg-5ae019e38e0730ebccb59b2b00c6ed61.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/minus_min.svg-5ae019e38e0730ebccb59b2b00c6ed61.dpitex" [deps] source_file="res://ui/assets/icons/minus_min.svg" -dest_files=["res://.godot/imported/minus_min.svg-5ae019e38e0730ebccb59b2b00c6ed61.ctex"] +dest_files=["res://.godot/imported/minus_min.svg-5ae019e38e0730ebccb59b2b00c6ed61.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/move.svg.import b/ui/assets/icons/move.svg.import index 095f9b5b..3602875e 100644 --- a/ui/assets/icons/move.svg.import +++ b/ui/assets/icons/move.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://blumv8ks5udd0" -path="res://.godot/imported/move.svg-02773225dd69d6b087d1f5350234f226.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/move.svg-02773225dd69d6b087d1f5350234f226.dpitex" [deps] source_file="res://ui/assets/icons/move.svg" -dest_files=["res://.godot/imported/move.svg-02773225dd69d6b087d1f5350234f226.ctex"] +dest_files=["res://.godot/imported/move.svg-02773225dd69d6b087d1f5350234f226.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/new_file_icon.png.import b/ui/assets/icons/new_file_icon.png.import index 037525e5..7e63b104 100644 --- a/ui/assets/icons/new_file_icon.png.import +++ b/ui/assets/icons/new_file_icon.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/new_file_icon.png-03f076b44da6a67b9b336d7fd15 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/node.svg.import b/ui/assets/icons/node.svg.import index 348be620..0c463ec4 100644 --- a/ui/assets/icons/node.svg.import +++ b/ui/assets/icons/node.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dricxnc8d6oam" -path="res://.godot/imported/node.svg-f625aceed4ae1a6d24afe0a60b536868.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/node.svg-f625aceed4ae1a6d24afe0a60b536868.dpitex" [deps] source_file="res://ui/assets/icons/node.svg" -dest_files=["res://.godot/imported/node.svg-f625aceed4ae1a6d24afe0a60b536868.ctex"] +dest_files=["res://.godot/imported/node.svg-f625aceed4ae1a6d24afe0a60b536868.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/object.svg b/ui/assets/icons/object.svg new file mode 100644 index 00000000..a709768e --- /dev/null +++ b/ui/assets/icons/object.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/object.svg.import b/ui/assets/icons/object.svg.import new file mode 100644 index 00000000..9e181c75 --- /dev/null +++ b/ui/assets/icons/object.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://cxyieo7jnlvkm" +path="res://.godot/imported/object.svg-e3a0713805ea8ed4ba90e3101bf0d51d.dpitex" + +[deps] + +source_file="res://ui/assets/icons/object.svg" +dest_files=["res://.godot/imported/object.svg-e3a0713805ea8ed4ba90e3101bf0d51d.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/offset_cross.svg.import b/ui/assets/icons/offset_cross.svg.import index f5b42535..4949e458 100644 --- a/ui/assets/icons/offset_cross.svg.import +++ b/ui/assets/icons/offset_cross.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c7ee44ql1gv6b" -path="res://.godot/imported/offset_cross.svg-fa6ba8866adb942e2f9497d32f98f219.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/offset_cross.svg-fa6ba8866adb942e2f9497d32f98f219.dpitex" [deps] source_file="res://ui/assets/icons/offset_cross.svg" -dest_files=["res://.godot/imported/offset_cross.svg-fa6ba8866adb942e2f9497d32f98f219.ctex"] +dest_files=["res://.godot/imported/offset_cross.svg-fa6ba8866adb942e2f9497d32f98f219.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=4.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/path.svg b/ui/assets/icons/path.svg deleted file mode 100644 index 674b2a76..00000000 --- a/ui/assets/icons/path.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/ui/assets/icons/path.svg.import b/ui/assets/icons/path.svg.import deleted file mode 100644 index c6b643ae..00000000 --- a/ui/assets/icons/path.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bmpfvosa78tot" -path="res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/path.svg" -dest_files=["res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/pen.svg b/ui/assets/icons/pen.svg index 31ae3a2c..931922ae 100644 --- a/ui/assets/icons/pen.svg +++ b/ui/assets/icons/pen.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/ui/assets/icons/pen.svg.import b/ui/assets/icons/pen.svg.import index f3b96fcd..444c30e4 100644 --- a/ui/assets/icons/pen.svg.import +++ b/ui/assets/icons/pen.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dixramdai4iq4" -path="res://.godot/imported/pen.svg-8b9edd9c40f973206eb1e8ec00453c78.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/pen.svg-8b9edd9c40f973206eb1e8ec00453c78.dpitex" [deps] source_file="res://ui/assets/icons/pen.svg" -dest_files=["res://.godot/imported/pen.svg-8b9edd9c40f973206eb1e8ec00453c78.ctex"] +dest_files=["res://.godot/imported/pen.svg-8b9edd9c40f973206eb1e8ec00453c78.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/picture.svg b/ui/assets/icons/picture.svg deleted file mode 100644 index b2d5c6d4..00000000 --- a/ui/assets/icons/picture.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ui/assets/icons/picture.svg.import b/ui/assets/icons/picture.svg.import deleted file mode 100644 index 04f02951..00000000 --- a/ui/assets/icons/picture.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://csskef1726pia" -path="res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/picture.svg" -dest_files=["res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/play.svg b/ui/assets/icons/play.svg index 5db5535d..76b3a96c 100644 --- a/ui/assets/icons/play.svg +++ b/ui/assets/icons/play.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/play.svg.import b/ui/assets/icons/play.svg.import index b6d19dfd..1c3350a5 100644 --- a/ui/assets/icons/play.svg.import +++ b/ui/assets/icons/play.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://b272tbdmvxj20" -path="res://.godot/imported/play.svg-6e11631c470fc7234c44a516961b928d.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/play.svg-6e11631c470fc7234c44a516961b928d.dpitex" [deps] source_file="res://ui/assets/icons/play.svg" -dest_files=["res://.godot/imported/play.svg-6e11631c470fc7234c44a516961b928d.ctex"] +dest_files=["res://.godot/imported/play.svg-6e11631c470fc7234c44a516961b928d.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/plus.svg.import b/ui/assets/icons/plus.svg.import index 1ba9f56c..39f08b38 100644 --- a/ui/assets/icons/plus.svg.import +++ b/ui/assets/icons/plus.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://hlck6y4i3l5q" -path="res://.godot/imported/plus.svg-2aec05a24ba3fce411ff8c07d0216505.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/plus.svg-2aec05a24ba3fce411ff8c07d0216505.dpitex" [deps] source_file="res://ui/assets/icons/plus.svg" -dest_files=["res://.godot/imported/plus.svg-2aec05a24ba3fce411ff8c07d0216505.ctex"] +dest_files=["res://.godot/imported/plus.svg-2aec05a24ba3fce411ff8c07d0216505.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/plus_min.svg.import b/ui/assets/icons/plus_min.svg.import index fc95b74d..23428412 100644 --- a/ui/assets/icons/plus_min.svg.import +++ b/ui/assets/icons/plus_min.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://xiahr3jughjg" -path="res://.godot/imported/plus_min.svg-d59fdc210a7c0d10d974d1630fcd3160.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/plus_min.svg-d59fdc210a7c0d10d974d1630fcd3160.dpitex" [deps] source_file="res://ui/assets/icons/plus_min.svg" -dest_files=["res://.godot/imported/plus_min.svg-d59fdc210a7c0d10d974d1630fcd3160.ctex"] +dest_files=["res://.godot/imported/plus_min.svg-d59fdc210a7c0d10d974d1630fcd3160.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/recording.svg.import b/ui/assets/icons/recording.svg.import index ca19ad9b..4fff1783 100644 --- a/ui/assets/icons/recording.svg.import +++ b/ui/assets/icons/recording.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cogl1yv0fxcm0" -path="res://.godot/imported/recording.svg-fd6b02caaf2c0c82bbcc7b1672a860aa.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/recording.svg-fd6b02caaf2c0c82bbcc7b1672a860aa.dpitex" [deps] source_file="res://ui/assets/icons/recording.svg" -dest_files=["res://.godot/imported/recording.svg-fd6b02caaf2c0c82bbcc7b1672a860aa.ctex"] +dest_files=["res://.godot/imported/recording.svg-fd6b02caaf2c0c82bbcc7b1672a860aa.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/root.svg.import b/ui/assets/icons/root.svg.import index c20bf61c..b10f810a 100644 --- a/ui/assets/icons/root.svg.import +++ b/ui/assets/icons/root.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://djqan6ktvu3gg" -path="res://.godot/imported/root.svg-a7e7915a457085f02dd5b3dd6e4c5932.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/root.svg-a7e7915a457085f02dd5b3dd6e4c5932.dpitex" [deps] source_file="res://ui/assets/icons/root.svg" -dest_files=["res://.godot/imported/root.svg-a7e7915a457085f02dd5b3dd6e4c5932.ctex"] +dest_files=["res://.godot/imported/root.svg-a7e7915a457085f02dd5b3dd6e4c5932.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/search.svg.import b/ui/assets/icons/search.svg.import index 1234316a..bd2e7804 100644 --- a/ui/assets/icons/search.svg.import +++ b/ui/assets/icons/search.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://7cgdpf2fr6p6" -path="res://.godot/imported/search.svg-8cadf8079107f4438b3983b0b7aa4949.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/search.svg-8cadf8079107f4438b3983b0b7aa4949.dpitex" [deps] source_file="res://ui/assets/icons/search.svg" -dest_files=["res://.godot/imported/search.svg-8cadf8079107f4438b3983b0b7aa4949.ctex"] +dest_files=["res://.godot/imported/search.svg-8cadf8079107f4438b3983b0b7aa4949.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/settings.svg.import b/ui/assets/icons/settings.svg.import index 468d96ff..808b95bf 100644 --- a/ui/assets/icons/settings.svg.import +++ b/ui/assets/icons/settings.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://d4fesqfd2v8fd" -path="res://.godot/imported/settings.svg-c46ee68c0fee76b79327321b0ec3cca9.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/settings.svg-c46ee68c0fee76b79327321b0ec3cca9.dpitex" [deps] source_file="res://ui/assets/icons/settings.svg" -dest_files=["res://.godot/imported/settings.svg-c46ee68c0fee76b79327321b0ec3cca9.ctex"] +dest_files=["res://.godot/imported/settings.svg-c46ee68c0fee76b79327321b0ec3cca9.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/side_panel_mode.png.import b/ui/assets/icons/side_panel_mode.png.import index 794b727f..c20b912f 100644 --- a/ui/assets/icons/side_panel_mode.png.import +++ b/ui/assets/icons/side_panel_mode.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/side_panel_mode.png-8d1679ccbd6f7603bba8ee1ec compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/slot.svg b/ui/assets/icons/slot.svg index 57129077..2f1e8276 100644 --- a/ui/assets/icons/slot.svg +++ b/ui/assets/icons/slot.svg @@ -1,3 +1,3 @@ - - + + diff --git a/ui/assets/icons/slot.svg.import b/ui/assets/icons/slot.svg.import index 831510dc..871a3248 100644 --- a/ui/assets/icons/slot.svg.import +++ b/ui/assets/icons/slot.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dsb6opwewlipc" -path="res://.godot/imported/slot.svg-7512eb906f1c8e0921720e836517d8dd.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/slot.svg-7512eb906f1c8e0921720e836517d8dd.dpitex" [deps] source_file="res://ui/assets/icons/slot.svg" -dest_files=["res://.godot/imported/slot.svg-7512eb906f1c8e0921720e836517d8dd.ctex"] +dest_files=["res://.godot/imported/slot.svg-7512eb906f1c8e0921720e836517d8dd.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/slot_in.svg b/ui/assets/icons/slot_in.svg new file mode 100644 index 00000000..8651ed7a --- /dev/null +++ b/ui/assets/icons/slot_in.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/slot_in.svg.import b/ui/assets/icons/slot_in.svg.import new file mode 100644 index 00000000..a1a9d4e5 --- /dev/null +++ b/ui/assets/icons/slot_in.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://dg8g8bw67365f" +path="res://.godot/imported/slot_in.svg-9728f97a87edc6ee8d977b05f41d534e.dpitex" + +[deps] + +source_file="res://ui/assets/icons/slot_in.svg" +dest_files=["res://.godot/imported/slot_in.svg-9728f97a87edc6ee8d977b05f41d534e.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/slot_left.svg b/ui/assets/icons/slot_left.svg deleted file mode 100644 index 453a240f..00000000 --- a/ui/assets/icons/slot_left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/assets/icons/slot_left.svg.import b/ui/assets/icons/slot_left.svg.import deleted file mode 100644 index 04ad2a9d..00000000 --- a/ui/assets/icons/slot_left.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://px17kflnvnd7" -path="res://.godot/imported/slot_left.svg-95dad6ec06003ae7e5766bd98efb6690.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/slot_left.svg" -dest_files=["res://.godot/imported/slot_left.svg-95dad6ec06003ae7e5766bd98efb6690.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/slot_node.svg b/ui/assets/icons/slot_node.svg new file mode 100644 index 00000000..c624e5c1 --- /dev/null +++ b/ui/assets/icons/slot_node.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/slot_node.svg.import b/ui/assets/icons/slot_node.svg.import new file mode 100644 index 00000000..64c16f12 --- /dev/null +++ b/ui/assets/icons/slot_node.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://dqfhmm5xqls6u" +path="res://.godot/imported/slot_node.svg-990bb35e7c371fe2109f9161bd311cce.dpitex" + +[deps] + +source_file="res://ui/assets/icons/slot_node.svg" +dest_files=["res://.godot/imported/slot_node.svg-990bb35e7c371fe2109f9161bd311cce.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/slot_out.svg b/ui/assets/icons/slot_out.svg new file mode 100644 index 00000000..b45f77d6 --- /dev/null +++ b/ui/assets/icons/slot_out.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/slot_out.svg.import b/ui/assets/icons/slot_out.svg.import new file mode 100644 index 00000000..9dd61665 --- /dev/null +++ b/ui/assets/icons/slot_out.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://bbudj20nubmr3" +path="res://.godot/imported/slot_out.svg-2492886c5426afdc81c5df6d7c2bc795.dpitex" + +[deps] + +source_file="res://ui/assets/icons/slot_out.svg" +dest_files=["res://.godot/imported/slot_out.svg-2492886c5426afdc81c5df6d7c2bc795.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/slot_right.svg b/ui/assets/icons/slot_right.svg deleted file mode 100644 index 9c952598..00000000 --- a/ui/assets/icons/slot_right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/assets/icons/slot_right.svg.import b/ui/assets/icons/slot_right.svg.import deleted file mode 100644 index a0a0d7c1..00000000 --- a/ui/assets/icons/slot_right.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bexlgpgwakbrr" -path="res://.godot/imported/slot_right.svg-8add5ca45ef21dcad026b04cdcc1f76c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/slot_right.svg" -dest_files=["res://.godot/imported/slot_right.svg-8add5ca45ef21dcad026b04cdcc1f76c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/sparkles.svg.import b/ui/assets/icons/sparkles.svg.import index fdd23382..939cb8dc 100644 --- a/ui/assets/icons/sparkles.svg.import +++ b/ui/assets/icons/sparkles.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dd6wdpndndufl" -path="res://.godot/imported/sparkles.svg-38158dff79dbfe4cd6546951a63d7513.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/sparkles.svg-38158dff79dbfe4cd6546951a63d7513.dpitex" [deps] source_file="res://ui/assets/icons/sparkles.svg" -dest_files=["res://.godot/imported/sparkles.svg-38158dff79dbfe4cd6546951a63d7513.ctex"] +dest_files=["res://.godot/imported/sparkles.svg-38158dff79dbfe4cd6546951a63d7513.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/star.svg.import b/ui/assets/icons/star.svg.import index 3648b2ff..b57db149 100644 --- a/ui/assets/icons/star.svg.import +++ b/ui/assets/icons/star.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://bogfuvhttgn1v" -path="res://.godot/imported/star.svg-9d050bc3c1ee183ab53abdc76a3f0ea4.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/star.svg-9d050bc3c1ee183ab53abdc76a3f0ea4.dpitex" [deps] source_file="res://ui/assets/icons/star.svg" -dest_files=["res://.godot/imported/star.svg-9d050bc3c1ee183ab53abdc76a3f0ea4.ctex"] +dest_files=["res://.godot/imported/star.svg-9d050bc3c1ee183ab53abdc76a3f0ea4.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/star_full.svg.import b/ui/assets/icons/star_full.svg.import index da5bcf61..f88d4da3 100644 --- a/ui/assets/icons/star_full.svg.import +++ b/ui/assets/icons/star_full.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://degqh4fiyy5q7" -path="res://.godot/imported/star_full.svg-600206e11e3287e6d34f7f695b4fae83.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/star_full.svg-600206e11e3287e6d34f7f695b4fae83.dpitex" [deps] source_file="res://ui/assets/icons/star_full.svg" -dest_files=["res://.godot/imported/star_full.svg-600206e11e3287e6d34f7f695b4fae83.ctex"] +dest_files=["res://.godot/imported/star_full.svg-600206e11e3287e6d34f7f695b4fae83.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/story_file.svg.import b/ui/assets/icons/story_file.svg.import index ef0b676e..d869bb3b 100644 --- a/ui/assets/icons/story_file.svg.import +++ b/ui/assets/icons/story_file.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dh5pqexvqelgr" -path="res://.godot/imported/story_file.svg-358a15444b72001dd607e5f16b13094e.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/story_file.svg-358a15444b72001dd607e5f16b13094e.dpitex" [deps] source_file="res://ui/assets/icons/story_file.svg" -dest_files=["res://.godot/imported/story_file.svg-358a15444b72001dd607e5f16b13094e.ctex"] +dest_files=["res://.godot/imported/story_file.svg-358a15444b72001dd607e5f16b13094e.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/str_icon.png b/ui/assets/icons/str_icon.png deleted file mode 100644 index 9845f981..00000000 Binary files a/ui/assets/icons/str_icon.png and /dev/null differ diff --git a/ui/assets/icons/str_icon.png.import b/ui/assets/icons/str_icon.png.import deleted file mode 100644 index 01f41368..00000000 --- a/ui/assets/icons/str_icon.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bjbupmf3if01e" -path="res://.godot/imported/str_icon.png-8ca382e7db66e9cfe98d3ed38a6840f4.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/str_icon.png" -dest_files=["res://.godot/imported/str_icon.png-8ca382e7db66e9cfe98d3ed38a6840f4.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/assets/icons/text.svg.import b/ui/assets/icons/text.svg.import index a449543f..016957db 100644 --- a/ui/assets/icons/text.svg.import +++ b/ui/assets/icons/text.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://b28iomp56v1c8" -path="res://.godot/imported/text.svg-f12730dd150189529254abdf2a0e4477.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/text.svg-f12730dd150189529254abdf2a0e4477.dpitex" [deps] source_file="res://ui/assets/icons/text.svg" -dest_files=["res://.godot/imported/text.svg-f12730dd150189529254abdf2a0e4477.ctex"] +dest_files=["res://.godot/imported/text.svg-f12730dd150189529254abdf2a0e4477.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/textbox_mode.png.import b/ui/assets/icons/textbox_mode.png.import index 7c7bc090..8162b339 100644 --- a/ui/assets/icons/textbox_mode.png.import +++ b/ui/assets/icons/textbox_mode.png.import @@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/textbox_mode.png-2c66a415327262c19ab1b5baa0c6 compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 @@ -25,6 +27,10 @@ mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false diff --git a/ui/assets/icons/time.svg b/ui/assets/icons/time.svg deleted file mode 100644 index 333fa961..00000000 --- a/ui/assets/icons/time.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ui/assets/icons/time.svg.import b/ui/assets/icons/time.svg.import deleted file mode 100644 index 87633a58..00000000 --- a/ui/assets/icons/time.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://choeykqmdmkm6" -path="res://.godot/imported/time.svg-f3195264abb11aaf5366306fe281ffce.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/time.svg" -dest_files=["res://.godot/imported/time.svg-f3195264abb11aaf5366306fe281ffce.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/toggle.svg.import b/ui/assets/icons/toggle.svg.import index fcbbc239..3a628e1b 100644 --- a/ui/assets/icons/toggle.svg.import +++ b/ui/assets/icons/toggle.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dltidudnc0ig4" -path="res://.godot/imported/toggle.svg-8beead1392fe293bddccb24536de320e.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/toggle.svg-8beead1392fe293bddccb24536de320e.dpitex" [deps] source_file="res://ui/assets/icons/toggle.svg" -dest_files=["res://.godot/imported/toggle.svg-8beead1392fe293bddccb24536de320e.ctex"] +dest_files=["res://.godot/imported/toggle.svg-8beead1392fe293bddccb24536de320e.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/toggle_off.svg.import b/ui/assets/icons/toggle_off.svg.import index 2cb8732c..6107ee29 100644 --- a/ui/assets/icons/toggle_off.svg.import +++ b/ui/assets/icons/toggle_off.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://dsjl0jdgaayu8" -path="res://.godot/imported/toggle_off.svg-b4decedaf4ff04bb807c4a46db2c5ee9.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/toggle_off.svg-b4decedaf4ff04bb807c4a46db2c5ee9.dpitex" [deps] source_file="res://ui/assets/icons/toggle_off.svg" -dest_files=["res://.godot/imported/toggle_off.svg-b4decedaf4ff04bb807c4a46db2c5ee9.ctex"] +dest_files=["res://.godot/imported/toggle_off.svg-b4decedaf4ff04bb807c4a46db2c5ee9.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/toggle_on.svg.import b/ui/assets/icons/toggle_on.svg.import index f1edf801..3a8753a0 100644 --- a/ui/assets/icons/toggle_on.svg.import +++ b/ui/assets/icons/toggle_on.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cenp7to5aeciv" -path="res://.godot/imported/toggle_on.svg-e6030d5275cc3e6903bc8c9eb1d5fcd6.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/toggle_on.svg-e6030d5275cc3e6903bc8c9eb1d5fcd6.dpitex" [deps] source_file="res://ui/assets/icons/toggle_on.svg" -dest_files=["res://.godot/imported/toggle_on.svg-e6030d5275cc3e6903bc8c9eb1d5fcd6.ctex"] +dest_files=["res://.godot/imported/toggle_on.svg-e6030d5275cc3e6903bc8c9eb1d5fcd6.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/trash.svg.import b/ui/assets/icons/trash.svg.import index 21489aca..2a963423 100644 --- a/ui/assets/icons/trash.svg.import +++ b/ui/assets/icons/trash.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://hmjhxdsk3pwj" -path="res://.godot/imported/trash.svg-8e3a1edf4d1ed72bc18f8ac99c6641da.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/trash.svg-8e3a1edf4d1ed72bc18f8ac99c6641da.dpitex" [deps] source_file="res://ui/assets/icons/trash.svg" -dest_files=["res://.godot/imported/trash.svg-8e3a1edf4d1ed72bc18f8ac99c6641da.ctex"] +dest_files=["res://.godot/imported/trash.svg-8e3a1edf4d1ed72bc18f8ac99c6641da.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/trash_min.svg.import b/ui/assets/icons/trash_min.svg.import index fb9c6c88..812fb8a6 100644 --- a/ui/assets/icons/trash_min.svg.import +++ b/ui/assets/icons/trash_min.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://fcpb4fd7uma3" -path="res://.godot/imported/trash_min.svg-e96161714aef429f1b62e394c1db86d1.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/trash_min.svg-e96161714aef429f1b62e394c1db86d1.dpitex" [deps] source_file="res://ui/assets/icons/trash_min.svg" -dest_files=["res://.godot/imported/trash_min.svg-e96161714aef429f1b62e394c1db86d1.ctex"] +dest_files=["res://.godot/imported/trash_min.svg-e96161714aef429f1b62e394c1db86d1.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/ui_close.svg.import b/ui/assets/icons/ui_close.svg.import index 14c5e0aa..48cf1a16 100644 --- a/ui/assets/icons/ui_close.svg.import +++ b/ui/assets/icons/ui_close.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://kk5em170gn2b" -path="res://.godot/imported/ui_close.svg-076d4f5a3ec70c68b7317eb59bc98596.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/ui_close.svg-076d4f5a3ec70c68b7317eb59bc98596.dpitex" [deps] source_file="res://ui/assets/icons/ui_close.svg" -dest_files=["res://.godot/imported/ui_close.svg-076d4f5a3ec70c68b7317eb59bc98596.ctex"] +dest_files=["res://.godot/imported/ui_close.svg-076d4f5a3ec70c68b7317eb59bc98596.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/ui_maximize.svg.import b/ui/assets/icons/ui_maximize.svg.import index d5057c57..848a0650 100644 --- a/ui/assets/icons/ui_maximize.svg.import +++ b/ui/assets/icons/ui_maximize.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://c4ib88gq1pbjr" -path="res://.godot/imported/ui_maximize.svg-6fdac7ebdd4643a4a2e6a7e1da7b5ae2.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/ui_maximize.svg-6fdac7ebdd4643a4a2e6a7e1da7b5ae2.dpitex" [deps] source_file="res://ui/assets/icons/ui_maximize.svg" -dest_files=["res://.godot/imported/ui_maximize.svg-6fdac7ebdd4643a4a2e6a7e1da7b5ae2.ctex"] +dest_files=["res://.godot/imported/ui_maximize.svg-6fdac7ebdd4643a4a2e6a7e1da7b5ae2.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/ui_minimize.svg.import b/ui/assets/icons/ui_minimize.svg.import index 4400deb9..f071ef83 100644 --- a/ui/assets/icons/ui_minimize.svg.import +++ b/ui/assets/icons/ui_minimize.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://wij3oqtk1gqe" -path="res://.godot/imported/ui_minimize.svg-69f299b5766bece959ce093aa08cbcdd.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/ui_minimize.svg-69f299b5766bece959ce093aa08cbcdd.dpitex" [deps] source_file="res://ui/assets/icons/ui_minimize.svg" -dest_files=["res://.godot/imported/ui_minimize.svg-69f299b5766bece959ce093aa08cbcdd.ctex"] +dest_files=["res://.godot/imported/ui_minimize.svg-69f299b5766bece959ce093aa08cbcdd.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/ui_reduce.svg.import b/ui/assets/icons/ui_reduce.svg.import index c0498b70..c50cf3b5 100644 --- a/ui/assets/icons/ui_reduce.svg.import +++ b/ui/assets/icons/ui_reduce.svg.import @@ -1,37 +1,20 @@ [remap] -importer="texture" -type="CompressedTexture2D" +importer="svg" +type="DPITexture" uid="uid://cxek38x4sufp5" -path="res://.godot/imported/ui_reduce.svg-f16069b8b6cbc2a2d84de1fc43cd5d8b.ctex" -metadata={ -"vram_texture": false -} +path="res://.godot/imported/ui_reduce.svg-f16069b8b6cbc2a2d84de1fc43cd5d8b.dpitex" [deps] source_file="res://ui/assets/icons/ui_reduce.svg" -dest_files=["res://.godot/imported/ui_reduce.svg-f16069b8b6cbc2a2d84de1fc43cd5d8b.ctex"] +dest_files=["res://.godot/imported/ui_reduce.svg-f16069b8b6cbc2a2d84de1fc43cd5d8b.dpitex"] [params] -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/icons/unchecked.svg b/ui/assets/icons/unchecked.svg new file mode 100644 index 00000000..e9964206 --- /dev/null +++ b/ui/assets/icons/unchecked.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/unchecked.svg.import b/ui/assets/icons/unchecked.svg.import new file mode 100644 index 00000000..408daa97 --- /dev/null +++ b/ui/assets/icons/unchecked.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dd8gpem5hniol" +path="res://.godot/imported/unchecked.svg-96beaabfb4b9414ac062734891bfa4e2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/unchecked.svg" +dest_files=["res://.godot/imported/unchecked.svg-96beaabfb4b9414ac062734891bfa4e2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/unchecked_disabled.svg b/ui/assets/icons/unchecked_disabled.svg new file mode 100644 index 00000000..86a56906 --- /dev/null +++ b/ui/assets/icons/unchecked_disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/icons/unchecked_disabled.svg.import b/ui/assets/icons/unchecked_disabled.svg.import new file mode 100644 index 00000000..33f81346 --- /dev/null +++ b/ui/assets/icons/unchecked_disabled.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://di77t2rvoooiw" +path="res://.godot/imported/unchecked_disabled.svg-85823cf2ba5237c1aebfb900a684f4e9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/unchecked_disabled.svg" +dest_files=["res://.godot/imported/unchecked_disabled.svg-85823cf2ba5237c1aebfb900a684f4e9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/unexposable.svg b/ui/assets/icons/unexposable.svg new file mode 100644 index 00000000..d447a82e --- /dev/null +++ b/ui/assets/icons/unexposable.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/unexposable.svg.import b/ui/assets/icons/unexposable.svg.import new file mode 100644 index 00000000..1b858246 --- /dev/null +++ b/ui/assets/icons/unexposable.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://qs0nswphmi80" +path="res://.godot/imported/unexposable.svg-071e11abf0bd720eed1d7d5f04176d8a.dpitex" + +[deps] + +source_file="res://ui/assets/icons/unexposable.svg" +dest_files=["res://.godot/imported/unexposable.svg-071e11abf0bd720eed1d7d5f04176d8a.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/unexposed.svg b/ui/assets/icons/unexposed.svg new file mode 100644 index 00000000..66928df5 --- /dev/null +++ b/ui/assets/icons/unexposed.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/icons/unexposed.svg.import b/ui/assets/icons/unexposed.svg.import new file mode 100644 index 00000000..4be42c98 --- /dev/null +++ b/ui/assets/icons/unexposed.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://x3csrllomotc" +path="res://.godot/imported/unexposed.svg-38ee52dd1817a5ef1bf2a75430518018.dpitex" + +[deps] + +source_file="res://ui/assets/icons/unexposed.svg" +dest_files=["res://.godot/imported/unexposed.svg-38ee52dd1817a5ef1bf2a75430518018.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/icons/upload.svg b/ui/assets/icons/upload.svg deleted file mode 100644 index 45387714..00000000 --- a/ui/assets/icons/upload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/assets/icons/upload.svg.import b/ui/assets/icons/upload.svg.import deleted file mode 100644 index e9814a8c..00000000 --- a/ui/assets/icons/upload.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bpgyletubbyq" -path="res://.godot/imported/upload.svg-adffb124d89942e718999881cec1ee18.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/upload.svg" -dest_files=["res://.godot/imported/upload.svg-adffb124d89942e718999881cec1ee18.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/variables.svg b/ui/assets/icons/variables.svg deleted file mode 100644 index 61f4d46e..00000000 --- a/ui/assets/icons/variables.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/assets/icons/variables.svg.import b/ui/assets/icons/variables.svg.import deleted file mode 100644 index ed56105a..00000000 --- a/ui/assets/icons/variables.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b46sqb5g0spae" -path="res://.godot/imported/variables.svg-067e9dca7014970f6e37bbf8c84ae70c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/assets/icons/variables.svg" -dest_files=["res://.godot/imported/variables.svg-067e9dca7014970f6e37bbf8c84ae70c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/vertical_dots.svg b/ui/assets/icons/vertical_dots.svg new file mode 100644 index 00000000..ee9efbc0 --- /dev/null +++ b/ui/assets/icons/vertical_dots.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/assets/icons/vertical_dots.svg.import b/ui/assets/icons/vertical_dots.svg.import new file mode 100644 index 00000000..42b01459 --- /dev/null +++ b/ui/assets/icons/vertical_dots.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3adrbx54m4c3" +path="res://.godot/imported/vertical_dots.svg-8c243fa55c65bb827f052611a2649e72.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/vertical_dots.svg" +dest_files=["res://.godot/imported/vertical_dots.svg-8c243fa55c65bb827f052611a2649e72.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/ui/assets/icons/vertical_dots_unfocus.svg b/ui/assets/icons/vertical_dots_unfocus.svg new file mode 100644 index 00000000..1f597540 --- /dev/null +++ b/ui/assets/icons/vertical_dots_unfocus.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ui/assets/icons/vertical_dots_unfocus.svg.import b/ui/assets/icons/vertical_dots_unfocus.svg.import new file mode 100644 index 00000000..8a8931f7 --- /dev/null +++ b/ui/assets/icons/vertical_dots_unfocus.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://beolc2wkdg6ey" +path="res://.godot/imported/vertical_dots_unfocus.svg-4e7d2bd64ded9042e49bd4e28323dd3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/icons/vertical_dots_unfocus.svg" +dest_files=["res://.godot/imported/vertical_dots_unfocus.svg-4e7d2bd64ded9042e49bd4e28323dd3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/common/assets/logo/logo_256x256.png b/ui/assets/logo/logo_256x256.png similarity index 100% rename from common/assets/logo/logo_256x256.png rename to ui/assets/logo/logo_256x256.png diff --git a/ui/assets/logo/logo_256x256.png.import b/ui/assets/logo/logo_256x256.png.import new file mode 100644 index 00000000..71cf2228 --- /dev/null +++ b/ui/assets/logo/logo_256x256.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dec1yarlm1myv" +path="res://.godot/imported/logo_256x256.png-d30f47f26f5474556d288c440b176d35.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/logo/logo_256x256.png" +dest_files=["res://.godot/imported/logo_256x256.png-d30f47f26f5474556d288c440b176d35.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_256x256.svg b/ui/assets/logo/logo_256x256.svg similarity index 100% rename from common/assets/logo/logo_256x256.svg rename to ui/assets/logo/logo_256x256.svg diff --git a/ui/assets/logo/logo_256x256.svg.import b/ui/assets/logo/logo_256x256.svg.import new file mode 100644 index 00000000..d795cb60 --- /dev/null +++ b/ui/assets/logo/logo_256x256.svg.import @@ -0,0 +1,20 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://exl7lwdbgxr2" +path="res://.godot/imported/logo_256x256.svg-fbfed44eccfa7ade455594d7ebb2375a.dpitex" + +[deps] + +source_file="res://ui/assets/logo/logo_256x256.svg" +dest_files=["res://.godot/imported/logo_256x256.svg-fbfed44eccfa7ade455594d7ebb2375a.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/common/assets/logo/logo_32x32.png b/ui/assets/logo/logo_32x32.png similarity index 100% rename from common/assets/logo/logo_32x32.png rename to ui/assets/logo/logo_32x32.png diff --git a/ui/assets/logo/logo_32x32.png.import b/ui/assets/logo/logo_32x32.png.import new file mode 100644 index 00000000..6c96079e --- /dev/null +++ b/ui/assets/logo/logo_32x32.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://t1y8o0t6yppw" +path="res://.godot/imported/logo_32x32.png-a7421dcde5d402c9ee02af74d07092a9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://ui/assets/logo/logo_32x32.png" +dest_files=["res://.godot/imported/logo_32x32.png-a7421dcde5d402c9ee02af74d07092a9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/common/assets/logo/logo_32x32.svg b/ui/assets/logo/logo_32x32.svg similarity index 100% rename from common/assets/logo/logo_32x32.svg rename to ui/assets/logo/logo_32x32.svg diff --git a/ui/assets/logo/logo_32x32.svg.import b/ui/assets/logo/logo_32x32.svg.import new file mode 100644 index 00000000..50862509 --- /dev/null +++ b/ui/assets/logo/logo_32x32.svg.import @@ -0,0 +1,20 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://b35w6abcbrbvw" +path="res://.godot/imported/logo_32x32.svg-fc49c7b401f9ad22097b61ac7d68c258.dpitex" + +[deps] + +source_file="res://ui/assets/logo/logo_32x32.svg" +dest_files=["res://.godot/imported/logo_32x32.svg-fc49c7b401f9ad22097b61ac7d68c258.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +fix_alpha_border=false +premult_alpha=false +compress=true diff --git a/ui/assets/theme/checked.svg b/ui/assets/theme/checked.svg new file mode 100644 index 00000000..6e8fa6fa --- /dev/null +++ b/ui/assets/theme/checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/theme/checked.svg.import b/ui/assets/theme/checked.svg.import new file mode 100644 index 00000000..360e9980 --- /dev/null +++ b/ui/assets/theme/checked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://rrcqi68frkrm" +path="res://.godot/imported/checked.svg-748c365703e0f6eba0858441255f4b8a.dpitex" + +[deps] + +source_file="res://ui/assets/theme/checked.svg" +dest_files=["res://.godot/imported/checked.svg-748c365703e0f6eba0858441255f4b8a.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/checked_disabled.svg b/ui/assets/theme/checked_disabled.svg new file mode 100644 index 00000000..51baf16e --- /dev/null +++ b/ui/assets/theme/checked_disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/theme/checked_disabled.svg.import b/ui/assets/theme/checked_disabled.svg.import new file mode 100644 index 00000000..be7dd7bf --- /dev/null +++ b/ui/assets/theme/checked_disabled.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://61ciebp2wqc2" +path="res://.godot/imported/checked_disabled.svg-eaef839b43b0db73d8eafa98329a4de9.dpitex" + +[deps] + +source_file="res://ui/assets/theme/checked_disabled.svg" +dest_files=["res://.godot/imported/checked_disabled.svg-eaef839b43b0db73d8eafa98329a4de9.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/dash.svg b/ui/assets/theme/dash.svg similarity index 100% rename from ui/theme_default/assets/dash.svg rename to ui/assets/theme/dash.svg diff --git a/ui/assets/theme/dash.svg.import b/ui/assets/theme/dash.svg.import new file mode 100644 index 00000000..18394fbd --- /dev/null +++ b/ui/assets/theme/dash.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://b1xjwwcy2gms8" +path="res://.godot/imported/dash.svg-cbbae3abb31da2be701a86901273956b.dpitex" + +[deps] + +source_file="res://ui/assets/theme/dash.svg" +dest_files=["res://.godot/imported/dash.svg-cbbae3abb31da2be701a86901273956b.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/grabber.svg b/ui/assets/theme/grabber.svg similarity index 100% rename from ui/theme_default/assets/grabber.svg rename to ui/assets/theme/grabber.svg diff --git a/ui/assets/theme/grabber.svg.import b/ui/assets/theme/grabber.svg.import new file mode 100644 index 00000000..8f5aaa3f --- /dev/null +++ b/ui/assets/theme/grabber.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://bprc86xjlnexd" +path="res://.godot/imported/grabber.svg-46dc7a8734c5e577d33df621ed77c3dd.dpitex" + +[deps] + +source_file="res://ui/assets/theme/grabber.svg" +dest_files=["res://.godot/imported/grabber.svg-46dc7a8734c5e577d33df621ed77c3dd.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/menu_checked.svg b/ui/assets/theme/menu_checked.svg new file mode 100644 index 00000000..b4d8ec20 --- /dev/null +++ b/ui/assets/theme/menu_checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/theme/menu_checked.svg.import b/ui/assets/theme/menu_checked.svg.import new file mode 100644 index 00000000..8ecb7853 --- /dev/null +++ b/ui/assets/theme/menu_checked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://cawl30yeertei" +path="res://.godot/imported/menu_checked.svg-4495524bdc766c7f4b8479298df2d809.dpitex" + +[deps] + +source_file="res://ui/assets/theme/menu_checked.svg" +dest_files=["res://.godot/imported/menu_checked.svg-4495524bdc766c7f4b8479298df2d809.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/menu_radio_checked.svg b/ui/assets/theme/menu_radio_checked.svg new file mode 100644 index 00000000..21909da2 --- /dev/null +++ b/ui/assets/theme/menu_radio_checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/theme/menu_radio_checked.svg.import b/ui/assets/theme/menu_radio_checked.svg.import new file mode 100644 index 00000000..a3aef267 --- /dev/null +++ b/ui/assets/theme/menu_radio_checked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://cqw42d04pau3m" +path="res://.godot/imported/menu_radio_checked.svg-a432b04ee286c3dd2ee97b7ea9bc2afc.dpitex" + +[deps] + +source_file="res://ui/assets/theme/menu_radio_checked.svg" +dest_files=["res://.godot/imported/menu_radio_checked.svg-a432b04ee286c3dd2ee97b7ea9bc2afc.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/menu_radio_unchecked.svg b/ui/assets/theme/menu_radio_unchecked.svg new file mode 100644 index 00000000..e930d227 --- /dev/null +++ b/ui/assets/theme/menu_radio_unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/assets/theme/menu_radio_unchecked.svg.import b/ui/assets/theme/menu_radio_unchecked.svg.import new file mode 100644 index 00000000..7068e024 --- /dev/null +++ b/ui/assets/theme/menu_radio_unchecked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://d16q2pqmnhubw" +path="res://.godot/imported/menu_radio_unchecked.svg-31a9eedfd1d239bf285b2cd2d984e75e.dpitex" + +[deps] + +source_file="res://ui/assets/theme/menu_radio_unchecked.svg" +dest_files=["res://.godot/imported/menu_radio_unchecked.svg-31a9eedfd1d239bf285b2cd2d984e75e.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/menu_unchecked.svg b/ui/assets/theme/menu_unchecked.svg new file mode 100644 index 00000000..9724e320 --- /dev/null +++ b/ui/assets/theme/menu_unchecked.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/theme/menu_unchecked.svg.import b/ui/assets/theme/menu_unchecked.svg.import new file mode 100644 index 00000000..3ca46719 --- /dev/null +++ b/ui/assets/theme/menu_unchecked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://wyxmevae70ox" +path="res://.godot/imported/menu_unchecked.svg-ff3605f0f5b6818bcd19a3bc2f63d434.dpitex" + +[deps] + +source_file="res://ui/assets/theme/menu_unchecked.svg" +dest_files=["res://.godot/imported/menu_unchecked.svg-ff3605f0f5b6818bcd19a3bc2f63d434.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/radio_checked.svg b/ui/assets/theme/radio_checked.svg similarity index 100% rename from ui/theme_default/assets/radio_checked.svg rename to ui/assets/theme/radio_checked.svg diff --git a/ui/assets/theme/radio_checked.svg.import b/ui/assets/theme/radio_checked.svg.import new file mode 100644 index 00000000..a94a3a20 --- /dev/null +++ b/ui/assets/theme/radio_checked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://ukfdhtpsv36d" +path="res://.godot/imported/radio_checked.svg-c6aaa3dcfa5c68a68bb6d68247aa2143.dpitex" + +[deps] + +source_file="res://ui/assets/theme/radio_checked.svg" +dest_files=["res://.godot/imported/radio_checked.svg-c6aaa3dcfa5c68a68bb6d68247aa2143.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/radio_checked_disabled.svg b/ui/assets/theme/radio_checked_disabled.svg similarity index 100% rename from ui/theme_default/assets/radio_checked_disabled.svg rename to ui/assets/theme/radio_checked_disabled.svg diff --git a/ui/assets/theme/radio_checked_disabled.svg.import b/ui/assets/theme/radio_checked_disabled.svg.import new file mode 100644 index 00000000..8d9e297e --- /dev/null +++ b/ui/assets/theme/radio_checked_disabled.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://cev1bkvk42y1b" +path="res://.godot/imported/radio_checked_disabled.svg-04340a5c2aa70f737887beea16e0b0e6.dpitex" + +[deps] + +source_file="res://ui/assets/theme/radio_checked_disabled.svg" +dest_files=["res://.godot/imported/radio_checked_disabled.svg-04340a5c2aa70f737887beea16e0b0e6.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/radio_unchecked.svg b/ui/assets/theme/radio_unchecked.svg similarity index 100% rename from ui/theme_default/assets/radio_unchecked.svg rename to ui/assets/theme/radio_unchecked.svg diff --git a/ui/assets/theme/radio_unchecked.svg.import b/ui/assets/theme/radio_unchecked.svg.import new file mode 100644 index 00000000..b70b10b3 --- /dev/null +++ b/ui/assets/theme/radio_unchecked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://dkn12tgqxnutf" +path="res://.godot/imported/radio_unchecked.svg-0b5a82a37e6928b503d27069973e27f4.dpitex" + +[deps] + +source_file="res://ui/assets/theme/radio_unchecked.svg" +dest_files=["res://.godot/imported/radio_unchecked.svg-0b5a82a37e6928b503d27069973e27f4.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme_default/assets/radio_unchecked_disabled.svg b/ui/assets/theme/radio_unchecked_disabled.svg similarity index 100% rename from ui/theme_default/assets/radio_unchecked_disabled.svg rename to ui/assets/theme/radio_unchecked_disabled.svg diff --git a/ui/assets/theme/radio_unchecked_disabled.svg.import b/ui/assets/theme/radio_unchecked_disabled.svg.import new file mode 100644 index 00000000..1e1cd090 --- /dev/null +++ b/ui/assets/theme/radio_unchecked_disabled.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://br60sxpbxl5hm" +path="res://.godot/imported/radio_unchecked_disabled.svg-9ad816ab5c691573436b420866b4b049.dpitex" + +[deps] + +source_file="res://ui/assets/theme/radio_unchecked_disabled.svg" +dest_files=["res://.godot/imported/radio_unchecked_disabled.svg-9ad816ab5c691573436b420866b4b049.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/unchecked.svg b/ui/assets/theme/unchecked.svg new file mode 100644 index 00000000..c923f553 --- /dev/null +++ b/ui/assets/theme/unchecked.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/theme/unchecked.svg.import b/ui/assets/theme/unchecked.svg.import new file mode 100644 index 00000000..fa3c70a4 --- /dev/null +++ b/ui/assets/theme/unchecked.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://7mjdr6hic806" +path="res://.godot/imported/unchecked.svg-5f3720a6d69b033ffed93eb61c09e546.dpitex" + +[deps] + +source_file="res://ui/assets/theme/unchecked.svg" +dest_files=["res://.godot/imported/unchecked.svg-5f3720a6d69b033ffed93eb61c09e546.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/assets/theme/unchecked_disabled.svg b/ui/assets/theme/unchecked_disabled.svg new file mode 100644 index 00000000..d3345a7c --- /dev/null +++ b/ui/assets/theme/unchecked_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/assets/theme/unchecked_disabled.svg.import b/ui/assets/theme/unchecked_disabled.svg.import new file mode 100644 index 00000000..d813fa9e --- /dev/null +++ b/ui/assets/theme/unchecked_disabled.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://cxgw12cfdkdq" +path="res://.godot/imported/unchecked_disabled.svg-71679c3c58d05b1a960ec3a2b4961928.dpitex" + +[deps] + +source_file="res://ui/assets/theme/unchecked_disabled.svg" +dest_files=["res://.godot/imported/unchecked_disabled.svg-71679c3c58d05b1a960ec3a2b4961928.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/ui/theme.tres b/ui/theme.tres new file mode 100644 index 00000000..dd844e4d --- /dev/null +++ b/ui/theme.tres @@ -0,0 +1,3 @@ +[gd_resource type="Theme" format=3 uid="uid://c4i457ttn1u4q"] + +[resource] diff --git a/ui/theme_default/assets/checked.svg b/ui/theme_default/assets/checked.svg deleted file mode 100644 index 05fac28f..00000000 --- a/ui/theme_default/assets/checked.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ui/theme_default/assets/checked.svg.import b/ui/theme_default/assets/checked.svg.import deleted file mode 100644 index 40f49153..00000000 --- a/ui/theme_default/assets/checked.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cfk4fuhncchxg" -path="res://.godot/imported/checked.svg-a2e3aa6723c978ceb1086430232b6ef8.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/checked.svg" -dest_files=["res://.godot/imported/checked.svg-a2e3aa6723c978ceb1086430232b6ef8.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/checked_disabled.svg b/ui/theme_default/assets/checked_disabled.svg deleted file mode 100644 index f60466dc..00000000 --- a/ui/theme_default/assets/checked_disabled.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ui/theme_default/assets/checked_disabled.svg.import b/ui/theme_default/assets/checked_disabled.svg.import deleted file mode 100644 index 298e8040..00000000 --- a/ui/theme_default/assets/checked_disabled.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bs272e68nsf7r" -path="res://.godot/imported/checked_disabled.svg-f39b51af6b974695633280859d9076d9.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/checked_disabled.svg" -dest_files=["res://.godot/imported/checked_disabled.svg-f39b51af6b974695633280859d9076d9.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/dash.svg.import b/ui/theme_default/assets/dash.svg.import deleted file mode 100644 index 9fcdcdb6..00000000 --- a/ui/theme_default/assets/dash.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b1xjwwcy2gms8" -path="res://.godot/imported/dash.svg-507e14f61e6cc6045017967ea4a29ae8.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/dash.svg" -dest_files=["res://.godot/imported/dash.svg-507e14f61e6cc6045017967ea4a29ae8.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/grabber.svg.import b/ui/theme_default/assets/grabber.svg.import deleted file mode 100644 index d8e8b144..00000000 --- a/ui/theme_default/assets/grabber.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bprc86xjlnexd" -path="res://.godot/imported/grabber.svg-abeade4349148784d1485f8ef9a7bfd2.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/grabber.svg" -dest_files=["res://.godot/imported/grabber.svg-abeade4349148784d1485f8ef9a7bfd2.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/radio_checked.svg.import b/ui/theme_default/assets/radio_checked.svg.import deleted file mode 100644 index 5e2753f4..00000000 --- a/ui/theme_default/assets/radio_checked.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://ukfdhtpsv36d" -path="res://.godot/imported/radio_checked.svg-f599e463893a63e77a1dd4af36a0358e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/radio_checked.svg" -dest_files=["res://.godot/imported/radio_checked.svg-f599e463893a63e77a1dd4af36a0358e.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/radio_checked_disabled.svg.import b/ui/theme_default/assets/radio_checked_disabled.svg.import deleted file mode 100644 index a9d373d9..00000000 --- a/ui/theme_default/assets/radio_checked_disabled.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cev1bkvk42y1b" -path="res://.godot/imported/radio_checked_disabled.svg-49bdf8cfebc4f192a76777ccbdc67936.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/radio_checked_disabled.svg" -dest_files=["res://.godot/imported/radio_checked_disabled.svg-49bdf8cfebc4f192a76777ccbdc67936.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/radio_unchecked.svg.import b/ui/theme_default/assets/radio_unchecked.svg.import deleted file mode 100644 index b6cf5ff8..00000000 --- a/ui/theme_default/assets/radio_unchecked.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dkn12tgqxnutf" -path="res://.godot/imported/radio_unchecked.svg-8b33c4e8be7415b64a2339859729a66b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/radio_unchecked.svg" -dest_files=["res://.godot/imported/radio_unchecked.svg-8b33c4e8be7415b64a2339859729a66b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/radio_unchecked_disabled.svg.import b/ui/theme_default/assets/radio_unchecked_disabled.svg.import deleted file mode 100644 index db4c8a78..00000000 --- a/ui/theme_default/assets/radio_unchecked_disabled.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://br60sxpbxl5hm" -path="res://.godot/imported/radio_unchecked_disabled.svg-02b04de59a43ed2df9d95e7fbce4c130.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/radio_unchecked_disabled.svg" -dest_files=["res://.godot/imported/radio_unchecked_disabled.svg-02b04de59a43ed2df9d95e7fbce4c130.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/unchecked.svg b/ui/theme_default/assets/unchecked.svg deleted file mode 100644 index d9a0053c..00000000 --- a/ui/theme_default/assets/unchecked.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/theme_default/assets/unchecked.svg.import b/ui/theme_default/assets/unchecked.svg.import deleted file mode 100644 index a28ccf82..00000000 --- a/ui/theme_default/assets/unchecked.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dugiq0wty3mb0" -path="res://.godot/imported/unchecked.svg-4985d678b5bc215ed850e5a684f10cfe.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/unchecked.svg" -dest_files=["res://.godot/imported/unchecked.svg-4985d678b5bc215ed850e5a684f10cfe.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/assets/unchecked_disabled.svg b/ui/theme_default/assets/unchecked_disabled.svg deleted file mode 100644 index 50c41ef2..00000000 --- a/ui/theme_default/assets/unchecked_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ui/theme_default/assets/unchecked_disabled.svg.import b/ui/theme_default/assets/unchecked_disabled.svg.import deleted file mode 100644 index 1506bb38..00000000 --- a/ui/theme_default/assets/unchecked_disabled.svg.import +++ /dev/null @@ -1,37 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dgv4wsdayk8l5" -path="res://.godot/imported/unchecked_disabled.svg-5584a5db223afa90b5f1116dbaf91a4f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/theme_default/assets/unchecked_disabled.svg" -dest_files=["res://.godot/imported/unchecked_disabled.svg-5584a5db223afa90b5f1116dbaf91a4f.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/ui/theme_default/main.tres b/ui/theme_default/main.tres deleted file mode 100644 index 774210c9..00000000 --- a/ui/theme_default/main.tres +++ /dev/null @@ -1,1155 +0,0 @@ -[gd_resource type="Theme" script_class="MonologueTheme" load_steps=92 format=3 uid="uid://budfhk1hudcfj"] - -[ext_resource type="Texture2D" uid="uid://cfk4fuhncchxg" path="res://ui/theme_default/assets/checked.svg" id="1_ks7w5"] -[ext_resource type="Texture2D" uid="uid://dsb6opwewlipc" path="res://ui/assets/icons/slot.svg" id="1_yjacf"] -[ext_resource type="Texture2D" uid="uid://cenp7to5aeciv" path="res://ui/assets/icons/toggle_on.svg" id="2_btlbu"] -[ext_resource type="Texture2D" uid="uid://bs272e68nsf7r" path="res://ui/theme_default/assets/checked_disabled.svg" id="2_uqgmq"] -[ext_resource type="Texture2D" uid="uid://dsjl0jdgaayu8" path="res://ui/assets/icons/toggle_off.svg" id="3_5qsde"] -[ext_resource type="FontFile" uid="uid://dmd74vvvqx340" path="res://ui/assets/fonts/CourierNewPSMT.ttf" id="4_flt0i"] -[ext_resource type="Texture2D" uid="uid://bprc86xjlnexd" path="res://ui/theme_default/assets/grabber.svg" id="4_gye7x"] -[ext_resource type="Script" uid="uid://c2nmcbo0pbk5u" path="res://ui/theme_default/theme_default.gd" id="5_gye7x"] -[ext_resource type="Texture2D" uid="uid://b1xjwwcy2gms8" path="res://ui/theme_default/assets/dash.svg" id="6_6ojxv"] -[ext_resource type="Texture2D" uid="uid://dugiq0wty3mb0" path="res://ui/theme_default/assets/unchecked.svg" id="7_rb0lk"] -[ext_resource type="Texture2D" uid="uid://ukfdhtpsv36d" path="res://ui/theme_default/assets/radio_checked.svg" id="7_tydhh"] -[ext_resource type="Texture2D" uid="uid://dgv4wsdayk8l5" path="res://ui/theme_default/assets/unchecked_disabled.svg" id="8_30fyb"] -[ext_resource type="Texture2D" uid="uid://cev1bkvk42y1b" path="res://ui/theme_default/assets/radio_checked_disabled.svg" id="8_ttl1t"] -[ext_resource type="Texture2D" uid="uid://dkn12tgqxnutf" path="res://ui/theme_default/assets/radio_unchecked.svg" id="9_2ase2"] -[ext_resource type="Texture2D" uid="uid://br60sxpbxl5hm" path="res://ui/theme_default/assets/radio_unchecked_disabled.svg" id="10_ks7w5"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ks7w5"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uqgmq"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rb0lk"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.2) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_30fyb"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.075) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hxw5v"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jwwfe"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.819608, 0.313726, 0.313726, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_isxcr"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.768627, 0.180392, 0.25098, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pnw2l"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.768627, 0.180392, 0.25098, 0.2) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qr2ui"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.768627, 0.180392, 0.25098, 0.25) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nxpu0"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.768627, 0.180392, 0.25098, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8t50g"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_00o8r"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a5c6f"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vsni6"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2fovc"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.075) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_25yn7"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_vvgve"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_left = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7fun3"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -expand_margin_left = 7.0 -expand_margin_top = 8.0 -expand_margin_right = 8.0 -expand_margin_bottom = 8.0 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wlc2a"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.25) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0ta60"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(1, 1, 1, 0) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.15) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1uy7d"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(1, 1, 1, 0) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.15) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6hq17"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8qegs"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_left = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 -shadow_color = Color(0, 0, 0, 0.15) -shadow_size = 30 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qd482"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_left = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.3) -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 -shadow_color = Color(0, 0, 0, 0.15) -shadow_size = 30 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_85xuj"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qccfg"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -shadow_color = Color(0, 0, 0, 0.15) -shadow_size = 30 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a8gsr"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.3) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -shadow_color = Color(0, 0, 0, 0.15) -shadow_size = 30 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8epah"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_j2kio"] -texture = ExtResource("6_6ojxv") -texture_margin_top = 1.0 -axis_stretch_horizontal = 2 -axis_stretch_vertical = 2 -modulate_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q56mj"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.324235, 0.324549, 0.349333, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hvbmk"] -content_margin_left = 2.0 -content_margin_top = 2.0 -content_margin_right = 2.0 -content_margin_bottom = 2.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.324235, 0.324549, 0.349333, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lvjcx"] -content_margin_left = 2.0 -content_margin_top = 2.0 -content_margin_right = 2.0 -content_margin_bottom = 2.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_color = Color(0.324235, 0.324549, 0.349333, 1) - -[sub_resource type="StyleBoxLine" id="StyleBoxLine_qi7n2"] -color = Color(0.890196, 0.894118, 0.921569, 0.2) -grow_begin = 0.0 -grow_end = 0.0 - -[sub_resource type="StyleBoxLine" id="StyleBoxLine_kp0xv"] -color = Color(0.890196, 0.894118, 0.921569, 0.2) -grow_begin = 8.0 -grow_end = 8.0 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_sdjsm"] -content_margin_top = 5.0 -bg_color = Color(0.819608, 0.313726, 0.313726, 1) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eudc8"] -content_margin_top = 5.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.15) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xm1xm"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qhjh4"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_txkog"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_axeq1"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -draw_center = false -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qd60x"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_poo5t"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_blikl"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v58ty"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yj5pq"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y6go5"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kik2p"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.2) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_huxsq"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.25) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6yqil"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j1x70"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.256471, 0.257255, 0.272157, 1) -corner_radius_top_left = 14 -corner_radius_top_right = 14 -corner_radius_bottom_right = 14 -corner_radius_bottom_left = 14 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5eo3a"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l5wh6"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c4a6a"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.256471, 0.257255, 0.272157, 1) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxLine" id="StyleBoxLine_xlkkc"] -color = Color(0.890196, 0.894118, 0.921569, 1) -grow_begin = 0.0 -grow_end = 0.0 -vertical = true - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qdp3q"] -content_margin_left = 4.0 -content_margin_top = 4.0 -content_margin_right = 4.0 -content_margin_bottom = 4.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_s45bh"] -content_margin_left = 4.0 -content_margin_top = 4.0 -content_margin_right = 4.0 -content_margin_bottom = 4.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_raufu"] -content_margin_left = 4.0 -content_margin_top = 4.0 -content_margin_right = 4.0 -content_margin_bottom = 4.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e7orh"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.15) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eggpx"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tm0m6"] -content_margin_left = 0.0 -content_margin_top = 0.0 -content_margin_right = 0.0 -content_margin_bottom = 0.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.075) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_c1m33"] - -[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3f21d"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_j02or"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_78rkj"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_61iib"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ydnyu"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.819608, 0.313726, 0.313726, 1) -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_onp0e"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_width_right = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_olm6c"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -draw_center = false -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k4pvp"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.403922, 0.384314, 0.470588, 0.15) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bdyw7"] -content_margin_left = 8.0 -content_margin_top = 4.0 -content_margin_right = 8.0 -content_margin_bottom = 4.0 -bg_color = Color(0.662745, 0.658824, 0.752941, 0.05) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5aedr"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_right = 1 -border_color = Color(0, 0, 0, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ibb5s"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_width_bottom = 1 -border_color = Color(0, 0, 0, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_341sr"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -draw_center = false -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xrd2a"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.0980392, 0.0980392, 0.109804, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) -corner_radius_top_left = 6 -corner_radius_top_right = 6 -corner_radius_bottom_right = 6 -corner_radius_bottom_left = 6 - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_muvro"] -content_margin_left = 8.0 -content_margin_top = 8.0 -content_margin_right = 8.0 -content_margin_bottom = 8.0 -bg_color = Color(0.182745, 0.182157, 0.206275, 1) -border_color = Color(0.890196, 0.894118, 0.921569, 0.2) - -[sub_resource type="StyleBoxLine" id="StyleBoxLine_tafav"] -color = Color(0.890196, 0.894118, 0.921569, 0.2) -grow_begin = 0.0 -grow_end = 0.0 -vertical = true - -[resource] -Button/colors/font_color = Color(0.890196, 0.894118, 0.921569, 0.8) -Button/colors/font_disabled_color = Color(0.890196, 0.894118, 0.921569, 0.3) -Button/colors/font_focus_color = Color(0.890196, 0.894118, 0.921569, 1) -Button/colors/font_hover_color = Color(0.890196, 0.894118, 0.921569, 1) -Button/colors/font_hover_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -Button/colors/font_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -Button/colors/icon_disabled_color = Color(0.890196, 0.894118, 0.921569, 0.3) -Button/colors/icon_normal_color = Color(0.890196, 0.894118, 0.921569, 0.8) -Button/constants/outline_size = 0 -Button/styles/disabled = SubResource("StyleBoxFlat_ks7w5") -Button/styles/disabled_mirrored = SubResource("StyleBoxFlat_ks7w5") -Button/styles/focus = SubResource("StyleBoxFlat_uqgmq") -Button/styles/hover = SubResource("StyleBoxFlat_rb0lk") -Button/styles/hover_mirrored = SubResource("StyleBoxFlat_rb0lk") -Button/styles/hover_pressed = SubResource("StyleBoxFlat_30fyb") -Button/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_30fyb") -Button/styles/normal = SubResource("StyleBoxFlat_hxw5v") -Button/styles/normal_mirrored = SubResource("StyleBoxFlat_hxw5v") -Button/styles/pressed = SubResource("StyleBoxFlat_30fyb") -Button/styles/pressed_mirrored = SubResource("StyleBoxFlat_30fyb") -ButtonAccent/base_type = &"Button" -ButtonAccent/styles/disabled = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/disabled_mirrored = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/focus = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/hover = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/hover_mirrored = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/hover_pressed = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/normal = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/normal_mirrored = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/pressed = SubResource("StyleBoxFlat_jwwfe") -ButtonAccent/styles/pressed_mirrored = SubResource("StyleBoxFlat_jwwfe") -ButtonWarning/base_type = &"Button" -ButtonWarning/constants/outline_size = 0 -ButtonWarning/styles/disabled = SubResource("StyleBoxFlat_isxcr") -ButtonWarning/styles/disabled_mirrored = SubResource("StyleBoxFlat_isxcr") -ButtonWarning/styles/focus = SubResource("StyleBoxFlat_uqgmq") -ButtonWarning/styles/hover = SubResource("StyleBoxFlat_pnw2l") -ButtonWarning/styles/hover_mirrored = SubResource("StyleBoxFlat_pnw2l") -ButtonWarning/styles/hover_pressed = SubResource("StyleBoxFlat_qr2ui") -ButtonWarning/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_qr2ui") -ButtonWarning/styles/normal = SubResource("StyleBoxFlat_nxpu0") -ButtonWarning/styles/normal_mirrored = SubResource("StyleBoxFlat_nxpu0") -ButtonWarning/styles/pressed = SubResource("StyleBoxFlat_qr2ui") -ButtonWarning/styles/pressed_mirrored = SubResource("StyleBoxFlat_qr2ui") -CheckBox/colors/font_hover_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -CheckBox/colors/font_pressed_color = Color(0.890196, 0.894118, 0.921569, 0.7) -CheckBox/constants/h_separation = 8 -CheckBox/icons/checked = ExtResource("1_ks7w5") -CheckBox/icons/checked_disabled = ExtResource("2_uqgmq") -CheckBox/icons/radio_checked = ExtResource("7_tydhh") -CheckBox/icons/radio_checked_disabled = ExtResource("8_ttl1t") -CheckBox/icons/radio_unchecked = ExtResource("9_2ase2") -CheckBox/icons/radio_unchecked_disabled = ExtResource("10_ks7w5") -CheckBox/icons/unchecked = ExtResource("7_rb0lk") -CheckBox/icons/unchecked_disabled = ExtResource("8_30fyb") -CheckBox/styles/disabled = SubResource("StyleBoxFlat_8t50g") -CheckBox/styles/disabled_mirrored = SubResource("StyleBoxFlat_8t50g") -CheckBox/styles/focus = SubResource("StyleBoxFlat_00o8r") -CheckBox/styles/hover = SubResource("StyleBoxFlat_00o8r") -CheckBox/styles/hover_mirrored = SubResource("StyleBoxFlat_00o8r") -CheckBox/styles/hover_pressed = SubResource("StyleBoxFlat_a5c6f") -CheckBox/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_a5c6f") -CheckBox/styles/normal = SubResource("StyleBoxFlat_vsni6") -CheckBox/styles/normal_mirrored = SubResource("StyleBoxFlat_vsni6") -CheckBox/styles/pressed = SubResource("StyleBoxFlat_a5c6f") -CheckBox/styles/pressed_mirrored = SubResource("StyleBoxFlat_a5c6f") -CheckButton/colors/font_focus_color = Color(0.890196, 0.894118, 0.921569, 0.7) -CheckButton/colors/font_hover_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -CheckButton/colors/font_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -CheckButton/icons/checked = ExtResource("2_btlbu") -CheckButton/icons/unchecked = ExtResource("3_5qsde") -CheckButton/styles/disabled = SubResource("StyleBoxFlat_8t50g") -CheckButton/styles/disabled_mirrored = SubResource("StyleBoxFlat_8t50g") -CheckButton/styles/focus = SubResource("StyleBoxFlat_00o8r") -CheckButton/styles/hover = SubResource("StyleBoxFlat_00o8r") -CheckButton/styles/hover_mirrored = SubResource("StyleBoxFlat_00o8r") -CheckButton/styles/hover_pressed = SubResource("StyleBoxFlat_a5c6f") -CheckButton/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_a5c6f") -CheckButton/styles/normal = SubResource("StyleBoxFlat_vsni6") -CheckButton/styles/normal_mirrored = SubResource("StyleBoxFlat_vsni6") -CheckButton/styles/pressed = SubResource("StyleBoxFlat_a5c6f") -CheckButton/styles/pressed_mirrored = SubResource("StyleBoxFlat_a5c6f") -CollapsibleFieldPanel/base_type = &"PanelContainer" -CollapsibleFieldPanel/styles/panel = SubResource("StyleBoxFlat_2fovc") -EditorBackground/base_type = &"PanelContainer" -EditorBackground/styles/panel = SubResource("StyleBoxFlat_25yn7") -EditorSidePanel/base_type = &"PanelContainer" -EditorSidePanel/styles/panel = SubResource("StyleBoxFlat_vvgve") -EditorSidePanelTopBox/base_type = &"PanelContainer" -EditorSidePanelTopBox/styles/panel = SubResource("StyleBoxFlat_7fun3") -FieldContainer/base_type = &"VBoxContainer" -FieldContainer/constants/separation = 4 -FlatButton/colors/font_color = Color(0.890196, 0.894118, 0.921569, 0.8) -FlatButton/colors/font_disabled_color = Color(0.890196, 0.894118, 0.921569, 0.3) -FlatButton/colors/font_focus_color = Color(0.890196, 0.894118, 0.921569, 1) -FlatButton/colors/font_hover_color = Color(0.890196, 0.894118, 0.921569, 1) -FlatButton/colors/font_hover_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -FlatButton/colors/font_pressed_color = Color(0.890196, 0.894118, 0.921569, 1) -FlatButton/colors/icon_disabled_color = Color(0.890196, 0.894118, 0.921569, 0.3) -FlatButton/colors/icon_normal_color = Color(0.890196, 0.894118, 0.921569, 0.8) -FlatButton/constants/outline_size = 0 -FlatButton/styles/disabled = SubResource("StyleBoxFlat_ks7w5") -FlatButton/styles/disabled_mirrored = SubResource("StyleBoxFlat_ks7w5") -FlatButton/styles/hover = SubResource("StyleBoxFlat_wlc2a") -FlatButton/styles/hover_mirrored = SubResource("StyleBoxFlat_wlc2a") -FlatButton/styles/hover_pressed = SubResource("StyleBoxFlat_0ta60") -FlatButton/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_0ta60") -FlatButton/styles/normal = SubResource("StyleBoxFlat_1uy7d") -FlatButton/styles/normal_mirrored = SubResource("StyleBoxFlat_1uy7d") -FlatButton/styles/pressed = SubResource("StyleBoxFlat_0ta60") -FlatButton/styles/pressed_mirrored = SubResource("StyleBoxFlat_0ta60") -GraphEdit/colors/grid_major = Color(0.890196, 0.894118, 0.921569, 0.15) -GraphEdit/colors/grid_minor = Color(0.890196, 0.894118, 0.921569, 0.15) -GraphEdit/styles/panel = SubResource("StyleBoxFlat_6hq17") -GraphNode/constants/separation = 8 -GraphNode/icons/port = ExtResource("1_yjacf") -GraphNode/styles/panel = SubResource("StyleBoxFlat_8qegs") -GraphNode/styles/panel_selected = SubResource("StyleBoxFlat_qd482") -GraphNode/styles/slot = SubResource("StyleBoxEmpty_85xuj") -GraphNode/styles/titlebar = SubResource("StyleBoxFlat_qccfg") -GraphNode/styles/titlebar_selected = SubResource("StyleBoxFlat_a8gsr") -GraphNodePicker/base_type = &"PanelContainer" -GraphNodePicker/styles/panel = SubResource("StyleBoxFlat_8epah") -HBoxContainer/constants/separation = 8 -HDottedSeparator/base_type = &"HSeparator" -HDottedSeparator/constants/separation = 1 -HDottedSeparator/styles/separator = SubResource("StyleBoxTexture_j2kio") -HScrollBar/styles/grabber = SubResource("StyleBoxFlat_q56mj") -HScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_q56mj") -HScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_q56mj") -HScrollBar/styles/scroll = SubResource("StyleBoxFlat_hvbmk") -HScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_lvjcx") -HSeparator/constants/separation = 1 -HSeparator/styles/separator = SubResource("StyleBoxLine_qi7n2") -HSeparatorGrow/base_type = &"HSeparator" -HSeparatorGrow/constants/separation = 1 -HSeparatorGrow/styles/separator = SubResource("StyleBoxLine_kp0xv") -HSlider/icons/grabber = ExtResource("4_gye7x") -HSlider/icons/grabber_disabled = ExtResource("4_gye7x") -HSlider/icons/grabber_highlight = ExtResource("4_gye7x") -HSlider/styles/grabber_area = SubResource("StyleBoxFlat_sdjsm") -HSlider/styles/grabber_area_highlight = SubResource("StyleBoxFlat_sdjsm") -HSlider/styles/slider = SubResource("StyleBoxFlat_eudc8") -ItemContainer/base_type = &"PanelContainer" -ItemContainer/styles/panel = SubResource("StyleBoxFlat_xm1xm") -ItemContainerFlat/base_type = &"PanelContainer" -ItemContainerFlat/styles/panel = SubResource("StyleBoxFlat_qhjh4") -Label/colors/font_color = Color(0.890196, 0.894118, 0.921569, 1) -LineEdit/styles/disabled = SubResource("StyleBoxFlat_txkog") -LineEdit/styles/focus = SubResource("StyleBoxFlat_axeq1") -LineEdit/styles/normal = SubResource("StyleBoxFlat_qd60x") -LineEditPortraitOption/base_type = &"LineEdit" -LineEditPortraitOption/colors/font_color = Color(0.890196, 0.894118, 0.921569, 1) -LineEditPortraitOption/colors/font_uneditable_color = Color(0.890196, 0.894118, 0.921569, 1) -LineEditPortraitOption/styles/disabled = SubResource("StyleBoxFlat_poo5t") -LineEditPortraitOption/styles/focus = SubResource("StyleBoxFlat_blikl") -LineEditPortraitOption/styles/normal = SubResource("StyleBoxFlat_v58ty") -MarginContainer/constants/margin_bottom = 8 -MarginContainer/constants/margin_left = 8 -MarginContainer/constants/margin_right = 8 -MarginContainer/constants/margin_top = 8 -NodeValue/base_type = &"Label" -NodeValue/colors/font_color = Color(0.890196, 0.894118, 0.921569, 1) -NodeValue/styles/normal = SubResource("StyleBoxFlat_yj5pq") -NoteLabel/base_type = &"Label" -NoteLabel/colors/font_color = Color(0.890196, 0.894118, 0.921569, 0.6) -OptionButton/constants/arrow_margin = 8 -OptionButton/constants/h_separation = 8 -OptionButton/styles/disabled = SubResource("StyleBoxFlat_y6go5") -OptionButton/styles/disabled_mirrored = SubResource("StyleBoxFlat_y6go5") -OptionButton/styles/focus = SubResource("StyleBoxFlat_uqgmq") -OptionButton/styles/hover = SubResource("StyleBoxFlat_kik2p") -OptionButton/styles/hover_mirrored = SubResource("StyleBoxFlat_kik2p") -OptionButton/styles/hover_pressed = SubResource("StyleBoxFlat_huxsq") -OptionButton/styles/hover_pressed_mirrored = SubResource("StyleBoxFlat_huxsq") -OptionButton/styles/normal = SubResource("StyleBoxFlat_6yqil") -OptionButton/styles/normal_mirrored = SubResource("StyleBoxFlat_6yqil") -OptionButton/styles/pressed = SubResource("StyleBoxFlat_huxsq") -OptionButton/styles/pressed_mirrored = SubResource("StyleBoxFlat_huxsq") -OuterPanel/base_type = &"PanelContainer" -OuterPanel/styles/panel = SubResource("StyleBoxFlat_j1x70") -Panel/styles/panel = SubResource("StyleBoxFlat_5eo3a") -PanelContainer/styles/panel = SubResource("StyleBoxFlat_5eo3a") -PopupMenu/constants/h_separation = 8 -PopupMenu/constants/icon_max_width = 14 -PopupMenu/constants/item_end_padding = 8 -PopupMenu/constants/item_start_padding = 8 -PopupMenu/constants/v_separation = 4 -PopupMenu/font_sizes/font_size = 16 -PopupMenu/icons/checked = ExtResource("1_ks7w5") -PopupMenu/icons/checked_disabled = ExtResource("2_uqgmq") -PopupMenu/icons/radio_checked = ExtResource("7_tydhh") -PopupMenu/icons/radio_checked_disabled = ExtResource("8_ttl1t") -PopupMenu/icons/radio_unchecked = ExtResource("9_2ase2") -PopupMenu/icons/radio_unchecked_disabled = ExtResource("10_ks7w5") -PopupMenu/icons/unchecked = ExtResource("7_rb0lk") -PopupMenu/icons/unchecked_disabled = ExtResource("8_30fyb") -PopupMenu/styles/hover = SubResource("StyleBoxFlat_l5wh6") -PopupMenu/styles/panel = SubResource("StyleBoxFlat_c4a6a") -PopupMenu/styles/separator = SubResource("StyleBoxLine_xlkkc") -SpinBoxButtonLeft/base_type = &"Button" -SpinBoxButtonLeft/styles/disabled = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonLeft/styles/focus = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonLeft/styles/hover = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonLeft/styles/normal = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonLeft/styles/pressed = SubResource("StyleBoxFlat_s45bh") -SpinBoxButtonRight/base_type = &"Button" -SpinBoxButtonRight/styles/disabled = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonRight/styles/focus = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonRight/styles/hover = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonRight/styles/normal = SubResource("StyleBoxFlat_qdp3q") -SpinBoxButtonRight/styles/pressed = SubResource("StyleBoxFlat_raufu") -SpinBoxLineEdit/base_type = &"LineEdit" -SpinBoxLineEdit/styles/focus = SubResource("StyleBoxFlat_e7orh") -SpinBoxLineEdit/styles/normal = SubResource("StyleBoxFlat_eggpx") -SpinBoxLineEdit/styles/read_only = SubResource("StyleBoxFlat_eggpx") -SpinBoxPanel/base_type = &"PanelContainer" -SpinBoxPanel/styles/panel = SubResource("StyleBoxFlat_tm0m6") -TabBar/colors/font_disabled_color = Color(0.890196, 0.894118, 0.921569, 0.3) -TabBar/colors/font_hovered_color = Color(0.890196, 0.894118, 0.921569, 1) -TabBar/colors/font_selected_color = Color(0.890196, 0.894118, 0.921569, 1) -TabBar/colors/font_unselected_color = Color(0.890196, 0.894118, 0.921569, 0.8) -TabBar/constants/h_separation = 8 -TabBar/font_sizes/font_size = 16 -TabBar/styles/button_highlight = SubResource("StyleBoxEmpty_c1m33") -TabBar/styles/button_pressed = SubResource("StyleBoxEmpty_3f21d") -TabBar/styles/tab_disabled = SubResource("StyleBoxFlat_j02or") -TabBar/styles/tab_focus = SubResource("StyleBoxFlat_78rkj") -TabBar/styles/tab_hovered = SubResource("StyleBoxFlat_61iib") -TabBar/styles/tab_selected = SubResource("StyleBoxFlat_ydnyu") -TabBar/styles/tab_unselected = SubResource("StyleBoxFlat_onp0e") -TextEdit/font_sizes/font_size = 16 -TextEdit/fonts/font = ExtResource("4_flt0i") -TextEdit/styles/focus = SubResource("StyleBoxFlat_olm6c") -TextEdit/styles/normal = SubResource("StyleBoxFlat_k4pvp") -TextEdit/styles/read_only = SubResource("StyleBoxFlat_bdyw7") -TimelineCellNumber/base_type = &"PanelContainer" -TimelineCellNumber/styles/panel = SubResource("StyleBoxFlat_5aedr") -TimelineLayerPanel/base_type = &"PanelContainer" -TimelineLayerPanel/styles/panel = SubResource("StyleBoxFlat_ibb5s") -Tree/colors/relashion_ship_line_color = Color(0.890196, 0.894118, 0.921569, 0.1) -Tree/constants/children_hl_line_width = 0 -Tree/constants/draw_guides = 0 -Tree/constants/draw_relationship_lines = 1 -Tree/constants/h_separation = 8 -Tree/constants/icon_max_width = 14 -Tree/constants/inner_item_margin_bottom = 8 -Tree/constants/inner_item_margin_left = 8 -Tree/constants/inner_item_margin_right = 8 -Tree/constants/inner_item_margin_top = 8 -Tree/constants/parent_hl_line_width = 1 -Tree/constants/relationship_line_width = 0 -Tree/constants/v_separation = 4 -Tree/icons/checked = ExtResource("1_ks7w5") -Tree/icons/checked_disabled = ExtResource("2_uqgmq") -Tree/icons/unchecked = ExtResource("7_rb0lk") -Tree/icons/unchecked_disabled = ExtResource("8_30fyb") -Tree/styles/focus = SubResource("StyleBoxFlat_341sr") -Tree/styles/hovered = SubResource("StyleBoxFlat_rb0lk") -Tree/styles/hovered_dimmed = SubResource("StyleBoxFlat_rb0lk") -Tree/styles/panel = SubResource("StyleBoxFlat_xrd2a") -Tree/styles/selected = SubResource("StyleBoxFlat_30fyb") -Tree/styles/selected_focus = SubResource("StyleBoxFlat_30fyb") -TreeContainer/base_type = &"PanelContainer" -TreeContainer/styles/panel = SubResource("StyleBoxFlat_muvro") -VBoxContainer/constants/separation = 8 -VDottedSeparator/base_type = &"VSeparator" -VDottedSeparator/constants/separation = 1 -VDottedSeparator/styles/separator = SubResource("StyleBoxTexture_j2kio") -VScrollBar/styles/grabber = SubResource("StyleBoxFlat_q56mj") -VScrollBar/styles/grabber_highlight = SubResource("StyleBoxFlat_q56mj") -VScrollBar/styles/grabber_pressed = SubResource("StyleBoxFlat_q56mj") -VScrollBar/styles/scroll = SubResource("StyleBoxFlat_hvbmk") -VScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_lvjcx") -VSeparator/constants/separation = 1 -VSeparator/styles/separator = SubResource("StyleBoxLine_tafav") -VSeparatorGrow/base_type = &"VSeparator" -VSeparatorGrow/constants/separation = 1 -VSeparatorGrow/styles/separator = SubResource("StyleBoxLine_xlkkc") -WarnLabel/base_type = &"Label" -WarnLabel/colors/font_color = Color(0.768627, 0.180392, 0.25098, 1) -script = ExtResource("5_gye7x") diff --git a/ui/theme_default/theme_default.gd b/ui/theme_default/theme_default.gd deleted file mode 100644 index 37ffcc81..00000000 --- a/ui/theme_default/theme_default.gd +++ /dev/null @@ -1,805 +0,0 @@ -@tool -class_name MonologueTheme extends Theme - -var scale: float -var dark_theme: bool = true -var contrast: float = 0.15 -# Colors -var text_color: Color = Color("e3e4eb") -var background_color: Color = Color("19191c") -var primary_color: Color = Color("a9a8c0") -var secondary_color: Color = Color("676278") -var accent_color: Color = Color("d15050") -var warn_color: Color = Color("c42e40") -# Constants -var base_spacing: int = 8 -var corner_radius: int = 6 -var relationship_line_opacity: float = 0.2 -var border_width: int = 1 - - -func _init() -> void: - scale = 1.0 - var _use_high_ppi: bool = scale >= 1.0 - - _generate_theme() - - -func _generate_theme() -> void: - # Globals - var base_margin: float = base_spacing - var base_border_color: Color = _get_text_color(0.2) - var outer_radius: float = base_spacing + corner_radius - - # Main stylebox - var base_sb: StyleBoxFlat = StyleBoxFlat.new() - base_sb.bg_color = background_color - base_sb.set_content_margin_all(base_margin) - base_sb.set_corner_radius_all(int(corner_radius)) - base_sb.border_color = base_border_color - - var base_empty_sb: StyleBoxFlat = base_sb.duplicate() - base_empty_sb.draw_center = false - - var base_field_sb: StyleBoxFlat = base_sb.duplicate() - base_field_sb.content_margin_top = base_spacing / 2 - base_field_sb.content_margin_bottom = base_spacing / 2 - base_field_sb.bg_color = _get_secondary_color(contrast) - - var button_sb: StyleBoxFlat = base_sb.duplicate() - button_sb.bg_color = _get_secondary_color(contrast) - button_sb.content_margin_left = base_margin - button_sb.content_margin_top = base_margin * 0.5 - button_sb.content_margin_right = base_margin - button_sb.content_margin_bottom = base_margin * 0.5 - - var button_hover_sb: StyleBoxFlat = button_sb.duplicate() - button_hover_sb.bg_color = _get_secondary_color(contrast + 0.05) - - var button_pressed_sb: StyleBoxFlat = button_sb.duplicate() - button_pressed_sb.bg_color = _get_secondary_color(contrast + 0.1) - - var button_disabled_sb: StyleBoxFlat = button_sb.duplicate() - button_disabled_sb.bg_color = _get_secondary_color(0.05) - - var flat_button_sb: StyleBoxFlat = base_sb.duplicate() - flat_button_sb.bg_color = Color.TRANSPARENT - flat_button_sb.set_border_width_all(border_width) - _set_border(flat_button_sb, _get_text_color(contrast)) - - var flat_button_hover_sb: StyleBoxFlat = flat_button_sb.duplicate() - flat_button_hover_sb.bg_color = _get_secondary_color(0.1) - _set_border(flat_button_hover_sb, _get_text_color(contrast + 0.05)) - - var flat_button_pressed_sb: StyleBoxFlat = flat_button_sb.duplicate() - button_pressed_sb.bg_color = _get_secondary_color(contrast / 2) - _set_border(flat_button_hover_sb, _get_text_color(contrast + 0.1)) - - # Button - - set_color("font_color", "Button", _get_text_color(0.8)) - set_color("font_disabled_color", "Button", _get_text_color(0.3)) - set_color("font_focus_color", "Button", text_color) - set_color("font_hover_color", "Button", text_color) - set_color("font_hover_pressed_color", "Button", text_color) - set_color("font_pressed_color", "Button", text_color) - set_color("icon_disabled_color", "Button", _get_text_color(0.3)) - set_color("icon_normal_color", "Button", _get_text_color(0.8)) - set_constant("outline_size", "Button", 0) - set_stylebox("disabled", "Button", button_disabled_sb) - set_stylebox("disabled_mirrored", "Button", button_disabled_sb) - set_stylebox("focus", "Button", base_empty_sb) - set_stylebox("hover", "Button", button_hover_sb) - set_stylebox("hover_mirrored", "Button", button_hover_sb) - set_stylebox("hover_pressed", "Button", button_pressed_sb) - set_stylebox("hover_pressed_mirrored", "Button", button_pressed_sb) - set_stylebox("normal", "Button", button_sb) - set_stylebox("normal_mirrored", "Button", button_sb) - set_stylebox("pressed", "Button", button_pressed_sb) - set_stylebox("pressed_mirrored", "Button", button_pressed_sb) - - # ButtonAccent - - set_type_variation("ButtonAccent", "Button") - - var button_accent_base_sb: StyleBoxFlat = base_sb.duplicate() - button_accent_base_sb.bg_color = accent_color - - set_stylebox("disabled", "ButtonAccent", button_accent_base_sb) - set_stylebox("disabled_mirrored", "ButtonAccent", button_accent_base_sb) - set_stylebox("focus", "ButtonAccent", button_accent_base_sb) - set_stylebox("hover", "ButtonAccent", button_accent_base_sb) - set_stylebox("hover_mirrored", "ButtonAccent", button_accent_base_sb) - set_stylebox("hover_pressed", "ButtonAccent", button_accent_base_sb) - set_stylebox("hover_pressed_mirrored", "ButtonAccent", button_accent_base_sb) - set_stylebox("normal", "ButtonAccent", button_accent_base_sb) - set_stylebox("normal_mirrored", "ButtonAccent", button_accent_base_sb) - set_stylebox("pressed", "ButtonAccent", button_accent_base_sb) - set_stylebox("pressed_mirrored", "ButtonAccent", button_accent_base_sb) - - # ButtonWarning - - set_type_variation("ButtonWarning", "Button") - - var delete_button_sb: StyleBoxFlat = button_sb.duplicate() - delete_button_sb.bg_color = _get_color(warn_color, contrast) - - var delete_button_hover_sb: StyleBoxFlat = button_sb.duplicate() - delete_button_hover_sb.bg_color = _get_color(warn_color, contrast + 0.05) - - var delete_button_pressed_sb: StyleBoxFlat = button_sb.duplicate() - delete_button_pressed_sb.bg_color = _get_color(warn_color, contrast + 0.1) - - var delete_button_disabled_sb: StyleBoxFlat = button_sb.duplicate() - delete_button_disabled_sb.bg_color = _get_color(warn_color, 0.05) - - set_constant("outline_size", "ButtonWarning", 0) - set_stylebox("disabled", "ButtonWarning", delete_button_disabled_sb) - set_stylebox("disabled_mirrored", "ButtonWarning", delete_button_disabled_sb) - set_stylebox("focus", "ButtonWarning", base_empty_sb) - set_stylebox("hover", "ButtonWarning", delete_button_hover_sb) - set_stylebox("hover_mirrored", "ButtonWarning", delete_button_hover_sb) - set_stylebox("hover_pressed", "ButtonWarning", delete_button_pressed_sb) - set_stylebox("hover_pressed_mirrored", "ButtonWarning", delete_button_pressed_sb) - set_stylebox("normal", "ButtonWarning", delete_button_sb) - set_stylebox("normal_mirrored", "ButtonWarning", delete_button_sb) - set_stylebox("pressed", "ButtonWarning", delete_button_pressed_sb) - set_stylebox("pressed_mirrored", "ButtonWarning", delete_button_pressed_sb) - - # CheckBox - - var check_box_sb: StyleBoxFlat = base_empty_sb.duplicate() - check_box_sb.set_content_margin_all(0) - - var check_box_hover_sb: StyleBoxFlat = check_box_sb.duplicate() - #check_box_hover_sb.bg_color = _get_primary_color(contrast) - - var check_box_pressed_sb: StyleBoxFlat = check_box_sb.duplicate() - #check_box_pressed_sb.bg_color = _get_primary_color(contrast) - - var check_box_disabled_sb: StyleBoxFlat = check_box_sb.duplicate() - #check_box_disabled_sb.bg_color = _get_primary_color(0.05) - - set_color("font_hover_pressed_color", "CheckBox", text_color) - set_color("font_pressed_color", "CheckBox", _get_text_color(0.7)) - set_constant("h_separation", "CheckBox", int(base_margin)) - set_icon("checked", "CheckBox", preload("res://ui/theme_default/assets/checked.svg")) - set_icon("unchecked", "CheckBox", preload("res://ui/theme_default/assets/unchecked.svg")) - set_icon( - "radio_checked", "CheckBox", preload("res://ui/theme_default/assets/radio_checked.svg") - ) - set_icon( - "radio_unchecked", "CheckBox", preload("res://ui/theme_default/assets/radio_unchecked.svg") - ) - set_icon( - "checked_disabled", - "CheckBox", - preload("res://ui/theme_default/assets/checked_disabled.svg") - ) - set_icon( - "unchecked_disabled", - "CheckBox", - preload("res://ui/theme_default/assets/unchecked_disabled.svg") - ) - set_icon( - "radio_checked_disabled", - "CheckBox", - preload("res://ui/theme_default/assets/radio_checked_disabled.svg") - ) - set_icon( - "radio_unchecked_disabled", - "CheckBox", - preload("res://ui/theme_default/assets/radio_unchecked_disabled.svg") - ) - set_stylebox("focus", "CheckBox", check_box_hover_sb) - set_stylebox("disabled", "CheckBox", check_box_disabled_sb) - set_stylebox("disabled_mirrored", "CheckBox", check_box_disabled_sb) - set_stylebox("hover", "CheckBox", check_box_hover_sb) - set_stylebox("hover_mirrored", "CheckBox", check_box_hover_sb) - set_stylebox("hover_pressed", "CheckBox", check_box_pressed_sb) - set_stylebox("hover_pressed_mirrored", "CheckBox", check_box_pressed_sb) - set_stylebox("pressed", "CheckBox", check_box_pressed_sb) - set_stylebox("pressed_mirrored", "CheckBox", check_box_pressed_sb) - set_stylebox("normal", "CheckBox", check_box_sb) - set_stylebox("normal_mirrored", "CheckBox", check_box_sb) - - # CheckButton - - set_color("font_focus_color", "CheckButton", _get_text_color(0.7)) - set_color("font_hover_pressed_color", "CheckButton", text_color) - set_color("font_pressed_color", "CheckButton", text_color) - set_icon("checked", "CheckButton", preload("res://ui/assets/icons/toggle_on.svg")) - set_icon("unchecked", "CheckButton", preload("res://ui/assets/icons/toggle_off.svg")) - set_stylebox("focus", "CheckButton", check_box_hover_sb) - set_stylebox("disabled", "CheckButton", check_box_disabled_sb) - set_stylebox("disabled_mirrored", "CheckButton", check_box_disabled_sb) - set_stylebox("hover", "CheckButton", check_box_hover_sb) - set_stylebox("hover_mirrored", "CheckButton", check_box_hover_sb) - set_stylebox("hover_pressed", "CheckButton", check_box_pressed_sb) - set_stylebox("hover_pressed_mirrored", "CheckButton", check_box_pressed_sb) - set_stylebox("pressed", "CheckButton", check_box_pressed_sb) - set_stylebox("pressed_mirrored", "CheckButton", check_box_pressed_sb) - set_stylebox("normal", "CheckButton", check_box_sb) - set_stylebox("normal_mirrored", "CheckButton", check_box_sb) - - # CollapsibleFieldPanel - - set_type_variation("CollapsibleFieldPanel", "PanelContainer") - var sb: StyleBoxFlat = base_sb.duplicate() - sb.bg_color = _get_secondary_color(contrast / 2) - set_stylebox("panel", "CollapsibleFieldPanel", sb) - - # EditorBackground - - set_type_variation("EditorBackground", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - sb.set_corner_radius_all(0) - sb.set_content_margin_all(0) - set_stylebox("panel", "EditorBackground", sb) - - # EditorSidePanel - - set_type_variation("EditorSidePanel", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - sb.set_corner_radius_all(0) - sb.set_border_width_all(0) - sb.border_width_left = 1 - set_stylebox("panel", "EditorSidePanel", sb) - - # EditorSidePanelTopBox - - set_type_variation("EditorSidePanelTopBox", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - sb.set_corner_radius_all(0) - sb.set_border_width_all(0) - sb.set_content_margin_all(0) - sb.set_expand_margin_all(base_spacing) - sb.expand_margin_left -= 1 - set_stylebox("panel", "EditorSidePanelTopBox", sb) - - # FlatButton - - set_color("font_color", "FlatButton", _get_text_color(0.8)) - set_color("font_disabled_color", "FlatButton", _get_text_color(0.3)) - set_color("font_focus_color", "FlatButton", text_color) - set_color("font_hover_color", "FlatButton", text_color) - set_color("font_hover_pressed_color", "FlatButton", text_color) - set_color("font_pressed_color", "FlatButton", text_color) - set_color("icon_disabled_color", "FlatButton", _get_text_color(0.3)) - set_color("icon_normal_color", "FlatButton", _get_text_color(0.8)) - set_constant("outline_size", "FlatButton", 0) - set_stylebox("disabled", "FlatButton", button_disabled_sb) - set_stylebox("disabled_mirrored", "FlatButton", button_disabled_sb) - set_stylebox("normal", "FlatButton", flat_button_sb) - set_stylebox("normal_mirrored", "FlatButton", flat_button_sb) - set_stylebox("hover", "FlatButton", flat_button_hover_sb) - set_stylebox("hover_mirrored", "FlatButton", flat_button_hover_sb) - set_stylebox("hover_pressed", "FlatButton", flat_button_pressed_sb) - set_stylebox("hover_pressed_mirrored", "FlatButton", flat_button_pressed_sb) - set_stylebox("pressed", "FlatButton", flat_button_pressed_sb) - set_stylebox("pressed_mirrored", "FlatButton", flat_button_pressed_sb) - - # GraphEdit - - sb = base_sb.duplicate() - sb.set_content_margin_all(0) - sb.set_corner_radius_all(0) - sb.set_border_width_all(0) - set_color("grid_major", "GraphEdit", _get_text_color(contrast)) - set_color("grid_minor", "GraphEdit", _get_text_color(contrast)) - set_stylebox("panel", "GraphEdit", sb) - - # GraphNode - - var graph_node_sb: StyleBoxFlat = base_sb.duplicate() - graph_node_sb.bg_color = _get_primary_color(contrast, false) - graph_node_sb.corner_radius_top_left = 0 - graph_node_sb.corner_radius_top_right = 0 - graph_node_sb.shadow_color = Color("#000000", contrast) - graph_node_sb.shadow_size = 30 - _set_border(graph_node_sb, base_border_color) - graph_node_sb.border_width_top = 0 - - var graph_node_selected_sb: StyleBoxFlat = graph_node_sb.duplicate() - graph_node_selected_sb.border_color.a += 0.1 - - var graph_node_titlebar_sb: StyleBoxFlat = base_sb.duplicate() - graph_node_titlebar_sb.corner_radius_bottom_left = 0 - graph_node_titlebar_sb.corner_radius_bottom_right = 0 - graph_node_titlebar_sb.shadow_color = Color("#000000", contrast) - graph_node_titlebar_sb.shadow_size = 30 - _set_border(graph_node_titlebar_sb, base_border_color) - graph_node_titlebar_sb.border_width_bottom = 0 - - var graph_node_titlebar_selected_sb: StyleBoxFlat = graph_node_titlebar_sb.duplicate() - graph_node_titlebar_selected_sb.border_color.a += 0.1 - - set_icon("port", "GraphNode", preload("res://ui/assets/icons/slot.svg")) - set_constant("separation", "GraphNode", base_spacing) - set_stylebox("panel", "GraphNode", graph_node_sb) - set_stylebox("panel_selected", "GraphNode", graph_node_selected_sb) - set_stylebox("titlebar", "GraphNode", graph_node_titlebar_sb) - set_stylebox("titlebar_selected", "GraphNode", graph_node_titlebar_selected_sb) - set_stylebox("slot", "GraphNode", StyleBoxEmpty.new()) - - # GraphNodePicker - - set_type_variation("GraphNodePicker", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - set_stylebox("panel", "GraphNodePicker", sb) - - # HBoxContainer & VBoxContainer - - set_type_variation("FieldContainer", "VBoxContainer") - - set_constant("separation", "HBoxContainer", base_spacing) - set_constant("separation", "VBoxContainer", base_spacing) - set_constant("separation", "FieldContainer", base_spacing / 2) - - # HDottedSeparator & VDottedSeparator - - set_type_variation("HDottedSeparator", "HSeparator") - set_type_variation("VDottedSeparator", "VSeparator") - var dotted_sb: StyleBoxTexture = StyleBoxTexture.new() - dotted_sb.texture = preload("res://ui/theme_default/assets/dash.svg") - dotted_sb.modulate_color = base_border_color - dotted_sb.axis_stretch_horizontal = StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT - dotted_sb.axis_stretch_vertical = StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT - dotted_sb.texture_margin_top = 1 - set_constant("separation", "HDottedSeparator", 1) - set_constant("separation", "VDottedSeparator", 1) - set_stylebox("separator", "HDottedSeparator", dotted_sb) - set_stylebox("separator", "VDottedSeparator", dotted_sb) - - # HSeparator & VSeparator - - var separator_sb: StyleBoxLine = StyleBoxLine.new() - separator_sb.color = base_border_color - separator_sb.vertical = false - separator_sb.grow_begin = 0 - separator_sb.grow_end = 0 - set_constant("separation", "HSeparator", 1) - set_constant("separation", "VSeparator", 1) - set_stylebox("separator", "HSeparator", separator_sb) - separator_sb = separator_sb.duplicate() - separator_sb.vertical = true - set_stylebox("separator", "VSeparator", separator_sb) - - # HSeparatorGrow & VSeparatorGrow - - set_type_variation("HSeparatorGrow", "HSeparator") - set_type_variation("VSeparatorGrow", "VSeparator") - separator_sb = separator_sb.duplicate() - separator_sb.vertical = false - separator_sb.grow_begin = base_spacing - separator_sb.grow_end = base_spacing - set_constant("separation", "HSeparatorGrow", 1) - set_constant("separation", "VSeparatorGrow", 1) - set_stylebox("separator", "HSeparatorGrow", separator_sb) - separator_sb = separator_sb.duplicate() - separator_sb.vertical = true - set_stylebox("separator", "VSeparatorGrow", separator_sb) - - # HSlider - - var slider_sb: StyleBoxFlat = StyleBoxFlat.new() - slider_sb.content_margin_top = 5 - slider_sb.set_corner_radius_all(5) - slider_sb.bg_color = _get_primary_color(contrast) - - var grabber_area: StyleBoxFlat = slider_sb.duplicate() - grabber_area.bg_color = accent_color - - set_icon("grabber", "HSlider", preload("res://ui/theme_default/assets/grabber.svg")) - set_icon("grabber_highlight", "HSlider", preload("res://ui/theme_default/assets/grabber.svg")) - set_icon("grabber_disabled", "HSlider", preload("res://ui/theme_default/assets/grabber.svg")) - set_stylebox("slider", "HSlider", slider_sb) - set_stylebox("grabber_area", "HSlider", grabber_area) - set_stylebox("grabber_area_highlight", "HSlider", grabber_area) - - # ItemContainer - - set_type_variation("ItemContainer", "PanelContainer") - - sb = base_empty_sb.duplicate() - set_stylebox("panel", "ItemContainer", sb) - - # ItemContainerFlat - - set_type_variation("ItemContainerFlat", "PanelContainer") - - sb = base_empty_sb.duplicate() - sb.set_content_margin_all(0) - set_stylebox("panel", "ItemContainerFlat", sb) - - # Label - - set_type_variation("NodeValue", "Label") - set_type_variation("NoteLabel", "Label") - set_type_variation("WarnLabel", "Label") - sb = base_sb.duplicate() - sb.content_margin_top = base_spacing / 2 - sb.content_margin_bottom = base_spacing / 2 - set_color("font_color", "Label", text_color) - set_color("font_color", "NodeValue", text_color) - set_color("font_color", "NoteLabel", _get_text_color(0.6)) - set_color("font_color", "WarnLabel", warn_color) - set_stylebox("normal", "NodeValue", sb) - - # LineEdit - - var line_edit_sb: StyleBoxFlat = base_field_sb.duplicate() - - var line_edit_focus_sb: StyleBoxFlat = line_edit_sb.duplicate() - line_edit_focus_sb.draw_center = false - line_edit_focus_sb.set_border_width_all(1) - - var line_edit_disabled_sb: StyleBoxFlat = line_edit_sb.duplicate() - line_edit_disabled_sb.bg_color = _get_primary_color(0.05) - - set_stylebox("normal", "LineEdit", line_edit_sb) - set_stylebox("focus", "LineEdit", line_edit_focus_sb) - set_stylebox("disabled", "LineEdit", line_edit_disabled_sb) - - # LineEditPortraitOption - - set_type_variation("LineEditPortraitOption", "LineEdit") - - var po_line_edit_sb: StyleBoxFlat = line_edit_sb.duplicate() - - var po_line_edit_focus_sb: StyleBoxFlat = po_line_edit_sb.duplicate() - po_line_edit_focus_sb.draw_center = true - po_line_edit_focus_sb.bg_color = background_color - po_line_edit_focus_sb.set_border_width_all(1) - - var po_line_edit_disabled_sb: StyleBoxFlat = line_edit_disabled_sb.duplicate() - - set_color("font_uneditable_color", "LineEditPortraitOption", text_color) - set_color("font_color", "LineEditPortraitOption", text_color) - set_stylebox("normal", "LineEditPortraitOption", po_line_edit_sb) - set_stylebox("focus", "LineEditPortraitOption", po_line_edit_focus_sb) - set_stylebox("disabled", "LineEditPortraitOption", po_line_edit_disabled_sb) - - # MarginContainer - - set_constant("margin_left", "MarginContainer", int(base_margin)) - set_constant("margin_top", "MarginContainer", int(base_margin)) - set_constant("margin_right", "MarginContainer", int(base_margin)) - set_constant("margin_bottom", "MarginContainer", int(base_margin)) - - # Panel - - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - set_stylebox("panel", "Panel", sb) - set_stylebox("panel", "PanelContainer", sb) - - # PopupMenu - - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - _set_border(sb, _get_color(base_border_color, base_border_color.a, false)) - var popup_menu_hover_sb: StyleBoxFlat = base_field_sb.duplicate() - popup_menu_hover_sb.bg_color = _get_secondary_color(contrast) - separator_sb.color = text_color - separator_sb.vertical = true - separator_sb.grow_begin = 0 - separator_sb.grow_end = 0 - set_constant("icon_max_width", "PopupMenu", 14) - set_constant("item_end_padding", "PopupMenu", base_spacing) - set_constant("item_start_padding", "PopupMenu", base_spacing) - set_constant("h_separation", "PopupMenu", base_spacing) - set_constant("v_separation", "PopupMenu", 4) - set_font_size("font_size", "PopupMenu", 16) - set_icon("checked", "PopupMenu", preload("res://ui/theme_default/assets/checked.svg")) - set_icon("unchecked", "PopupMenu", preload("res://ui/theme_default/assets/unchecked.svg")) - set_icon( - "radio_checked", "PopupMenu", preload("res://ui/theme_default/assets/radio_checked.svg") - ) - set_icon( - "radio_unchecked", "PopupMenu", preload("res://ui/theme_default/assets/radio_unchecked.svg") - ) - set_icon( - "checked_disabled", - "PopupMenu", - preload("res://ui/theme_default/assets/checked_disabled.svg") - ) - set_icon( - "unchecked_disabled", - "PopupMenu", - preload("res://ui/theme_default/assets/unchecked_disabled.svg") - ) - set_icon( - "radio_checked_disabled", - "PopupMenu", - preload("res://ui/theme_default/assets/radio_checked_disabled.svg") - ) - set_icon( - "radio_unchecked_disabled", - "PopupMenu", - preload("res://ui/theme_default/assets/radio_unchecked_disabled.svg") - ) - set_stylebox("panel", "PopupMenu", sb) - set_stylebox("hover", "PopupMenu", popup_menu_hover_sb) - set_stylebox("separator", "PopupMenu", separator_sb) - - # ScrollBar - var _side_panel_bg_color: Color = _get_primary_color(contrast, false) - var _scroll_bar_color: Color = _get_color( - base_border_color, base_border_color.a, false, _side_panel_bg_color - ) - - var scroll_sb: StyleBoxFlat = base_empty_sb.duplicate() - scroll_sb.border_color = _scroll_bar_color - scroll_sb.set_content_margin_all(2) - scroll_sb.set_corner_radius_all(0) - - # ScrollBar's focus stylebox is not working. - var scroll_focus_sb: StyleBoxFlat = scroll_sb.duplicate() - scroll_focus_sb.draw_center = true - - var grabber_sb: StyleBoxFlat = base_sb.duplicate() - grabber_sb.set_corner_radius_all(5) - grabber_sb.bg_color = _scroll_bar_color - - set_stylebox("scroll", "VScrollBar", scroll_sb) - set_stylebox("scroll_focus", "VScrollBar", scroll_focus_sb) - set_stylebox("grabber", "VScrollBar", grabber_sb) - set_stylebox("grabber", "VScrollBar", grabber_sb) - set_stylebox("grabber_highlight", "VScrollBar", grabber_sb) - set_stylebox("grabber_pressed", "VScrollBar", grabber_sb) - - set_stylebox("scroll", "HScrollBar", scroll_sb) - set_stylebox("scroll_focus", "HScrollBar", scroll_focus_sb) - set_stylebox("grabber", "HScrollBar", grabber_sb) - set_stylebox("grabber", "HScrollBar", grabber_sb) - set_stylebox("grabber_highlight", "HScrollBar", grabber_sb) - set_stylebox("grabber_pressed", "HScrollBar", grabber_sb) - - # SpinBoxButton - - set_type_variation("SpinBoxButtonLeft", "Button") - set_type_variation("SpinBoxButtonRight", "Button") - - var spin_box_button_sb: StyleBoxFlat = base_empty_sb.duplicate() - spin_box_button_sb.set_content_margin_all(base_spacing / 2) - var spin_box_button_pressed_sb: StyleBoxFlat = base_sb.duplicate() - spin_box_button_pressed_sb.set_content_margin_all(base_spacing / 2) - spin_box_button_pressed_sb.bg_color = _get_primary_color(contrast) - spin_box_button_pressed_sb.corner_radius_top_right = 0 - spin_box_button_pressed_sb.corner_radius_bottom_right = 0 - - set_stylebox("normal", "SpinBoxButtonLeft", spin_box_button_sb) - set_stylebox("pressed", "SpinBoxButtonLeft", spin_box_button_pressed_sb) - set_stylebox("focus", "SpinBoxButtonLeft", spin_box_button_sb) - set_stylebox("hover", "SpinBoxButtonLeft", spin_box_button_sb) - set_stylebox("disabled", "SpinBoxButtonLeft", spin_box_button_sb) - - spin_box_button_pressed_sb = spin_box_button_pressed_sb.duplicate() - spin_box_button_pressed_sb.set_corner_radius_all(corner_radius) - spin_box_button_pressed_sb.corner_radius_top_left = 0 - spin_box_button_pressed_sb.corner_radius_bottom_left = 0 - - set_stylebox("normal", "SpinBoxButtonRight", spin_box_button_sb) - set_stylebox("pressed", "SpinBoxButtonRight", spin_box_button_pressed_sb) - set_stylebox("focus", "SpinBoxButtonRight", spin_box_button_sb) - set_stylebox("hover", "SpinBoxButtonRight", spin_box_button_sb) - set_stylebox("disabled", "SpinBoxButtonRight", spin_box_button_sb) - - # SpinBoxLineEdit - - set_type_variation("SpinBoxLineEdit", "LineEdit") - - var spin_box_line_edit_sb: StyleBoxFlat = base_field_sb.duplicate() - spin_box_line_edit_sb.draw_center = false - spin_box_line_edit_sb.set_content_margin_all(0) - var spin_box_line_edit_focus_sb: StyleBoxFlat = spin_box_line_edit_sb.duplicate() - spin_box_line_edit_focus_sb.bg_color = _get_primary_color(contrast) - spin_box_line_edit_focus_sb.set_corner_radius_all(0) - - set_stylebox("normal", "SpinBoxLineEdit", spin_box_line_edit_sb) - set_stylebox("focus", "SpinBoxLineEdit", spin_box_line_edit_focus_sb) - set_stylebox("read_only", "SpinBoxLineEdit", spin_box_line_edit_sb) - - # SpinBoxPanel - - set_type_variation("SpinBoxPanel", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast / 2) - sb.set_content_margin_all(0) - set_stylebox("panel", "SpinBoxPanel", sb) - - # TabBar - - var tab_unselected_sb: StyleBoxFlat = base_sb.duplicate() - tab_unselected_sb.draw_center = false - tab_unselected_sb.set_border_width_all(0) - tab_unselected_sb.border_width_right = 1 - tab_unselected_sb.set_corner_radius_all(0) - - var tab_hovered_sb: StyleBoxFlat = tab_unselected_sb.duplicate() - var tab_selected_sb: StyleBoxFlat = tab_unselected_sb.duplicate() - tab_selected_sb.draw_center = true - tab_selected_sb.bg_color = accent_color - - var tab_disabled_sb: StyleBoxFlat = tab_unselected_sb.duplicate() - var tab_focus_sb: StyleBoxFlat = tab_unselected_sb.duplicate() - - set_color("font_disabled_color", "TabBar", _get_text_color(0.3)) - set_color("font_unselected_color", "TabBar", _get_text_color(0.8)) - set_color("font_hovered_color", "TabBar", text_color) - set_color("font_selected_color", "TabBar", text_color) - set_constant("h_separation", "TabBar", base_spacing) - set_font_size("font_size", "TabBar", 16) - set_stylebox("button_highlight", "TabBar", StyleBoxEmpty.new()) - set_stylebox("button_pressed", "TabBar", StyleBoxEmpty.new()) - set_stylebox("tab_unselected", "TabBar", tab_unselected_sb) - set_stylebox("tab_hovered", "TabBar", tab_hovered_sb) - set_stylebox("tab_selected", "TabBar", tab_selected_sb) - set_stylebox("tab_disabled", "TabBar", tab_disabled_sb) - set_stylebox("tab_focus", "TabBar", tab_focus_sb) - - # TextEdit - - var text_edit_sb: StyleBoxFlat = line_edit_sb.duplicate() - - var text_edit_focus_sb: StyleBoxFlat = line_edit_focus_sb.duplicate() - - var text_edit_disabled_sb: StyleBoxFlat = line_edit_disabled_sb.duplicate() - - set_font("font", "TextEdit", preload("res://ui/assets/fonts/CourierNewPSMT.ttf")) - set_font_size("font_size", "TextEdit", 16) - set_stylebox("normal", "TextEdit", text_edit_sb) - set_stylebox("focus", "TextEdit", text_edit_focus_sb) - set_stylebox("read_only", "TextEdit", text_edit_disabled_sb) - - # TimelineCellNumber - - set_type_variation("TimelineCellNumber", "PanelContainer") - - sb = base_sb.duplicate() - sb.set_corner_radius_all(0) - sb.bg_color = _get_primary_color(contrast, false) - sb.border_width_right = border_width - sb.border_color = Color.BLACK - - set_stylebox("panel", "TimelineCellNumber", sb) - - # TimelineLayerPanel - - set_type_variation("TimelineLayerPanel", "PanelContainer") - - sb = base_sb.duplicate() - sb.set_corner_radius_all(0) - sb.bg_color = _get_primary_color(contrast, false) - sb.border_width_bottom = border_width - sb.border_color = Color.BLACK - - set_stylebox("panel", "TimelineLayerPanel", sb) - - # Tree - - var tree_sb: StyleBoxFlat = base_sb.duplicate() - var tree_focus_sb: StyleBoxFlat = base_empty_sb.duplicate() - - set_color( - "relashion_ship_line_color", "Tree", Color(base_border_color, base_border_color.a / 2) - ) - #set_color("guide_color", "Tree", Color(base_border_color, base_border_color.a/2)) - set_constant("icon_max_width", "Tree", 14) - set_constant("h_separation", "Tree", base_spacing) - set_constant("v_separation", "Tree", base_spacing / 2) - set_constant("inner_item_margin_bottom", "Tree", base_spacing) - set_constant("inner_item_margin_left", "Tree", base_spacing) - set_constant("inner_item_margin_top", "Tree", base_spacing) - set_constant("inner_item_margin_right", "Tree", base_spacing) - set_constant("draw_relationship_lines", "Tree", 1) - set_constant("draw_guides", "Tree", 0) - set_constant("relationship_line_width", "Tree", 0) - set_constant("parent_hl_line_width", "Tree", border_width) - set_constant("children_hl_line_width", "Tree", 0) - set_icon("checked", "Tree", preload("res://ui/theme_default/assets/checked.svg")) - set_icon("unchecked", "Tree", preload("res://ui/theme_default/assets/unchecked.svg")) - set_icon( - "checked_disabled", "Tree", preload("res://ui/theme_default/assets/checked_disabled.svg") - ) - set_icon( - "unchecked_disabled", - "Tree", - preload("res://ui/theme_default/assets/unchecked_disabled.svg") - ) - set_stylebox("panel", "Tree", tree_sb) - set_stylebox("focus", "Tree", tree_focus_sb) - set_stylebox("hovered", "Tree", button_hover_sb) - set_stylebox("hovered_dimmed", "Tree", button_hover_sb) - set_stylebox("selected", "Tree", button_pressed_sb) - set_stylebox("selected_focus", "Tree", button_pressed_sb) - - # TreeContainer - - set_type_variation("TreeContainer", "PanelContainer") - - sb = base_sb.duplicate() - sb.set_corner_radius_all(0) - sb.bg_color = _get_primary_color(contrast, false) - set_stylebox("panel", "TreeContainer", sb) - - # OptionButton - - var option_button_sb = base_field_sb.duplicate() - option_button_sb.bg_color = _get_secondary_color(contrast) - - var option_button_hover_sb: StyleBoxFlat = button_sb.duplicate() - option_button_hover_sb.bg_color = _get_secondary_color(contrast + 0.05) - - var option_button_pressed_sb: StyleBoxFlat = button_sb.duplicate() - option_button_pressed_sb.bg_color = _get_secondary_color(contrast + 0.1) - - var option_button_disabled_sb: StyleBoxFlat = button_sb.duplicate() - option_button_disabled_sb.bg_color = _get_secondary_color(0.05) - - set_constant("arrow_margin", "OptionButton", base_spacing) - set_constant("h_separation", "OptionButton", base_spacing) - set_stylebox("disabled", "OptionButton", option_button_disabled_sb) - set_stylebox("disabled_mirrored", "OptionButton", option_button_disabled_sb) - set_stylebox("focus", "OptionButton", base_empty_sb) - set_stylebox("hover", "OptionButton", option_button_hover_sb) - set_stylebox("hover_mirrored", "OptionButton", option_button_hover_sb) - set_stylebox("hover_pressed", "OptionButton", option_button_pressed_sb) - set_stylebox("hover_pressed_mirrored", "OptionButton", option_button_pressed_sb) - set_stylebox("normal", "OptionButton", option_button_sb) - set_stylebox("normal_mirrored", "OptionButton", option_button_sb) - set_stylebox("pressed", "OptionButton", option_button_pressed_sb) - set_stylebox("pressed_mirrored", "OptionButton", option_button_pressed_sb) - - # OuterPanel - - set_type_variation("OuterPanel", "PanelContainer") - sb = base_sb.duplicate() - sb.bg_color = _get_primary_color(contrast, false) - sb.set_corner_radius_all(int(outer_radius)) - _set_border(sb, _get_color(base_border_color, base_border_color.a, false)) - set_stylebox("panel", "OuterPanel", sb) - - -func _get_primary_color(alpha: float = 1.0, transparent: bool = true) -> Color: - #return _get_color(secondary_color, alpha, transparent) - return _get_color(primary_color, alpha, transparent) - - -func _get_secondary_color(alpha: float = 1.0, transparent: bool = true) -> Color: - #return _get_color(primary_color, alpha, transparent) - return _get_color(secondary_color, alpha, transparent) - - -func _get_text_color(alpha: float = 1.0, transparent: bool = true) -> Color: - return _get_color(text_color, alpha, transparent) - - -func _get_color( - color: Color, alpha: float = 1.0, transparent: bool = true, blend_with: Color = background_color -) -> Color: - if transparent: - return Color(color, alpha) - return Color(blend_with).blend(Color(color, alpha)) - - -# Shorthand content margin setter -func _set_margin( - sb: StyleBox, left: float, top: float, right: float = left, bottom: float = top -) -> void: - sb.content_margin_left = left * scale - sb.content_margin_top = top * scale - sb.content_margin_right = right * scale - sb.content_margin_bottom = bottom * scale - - -# Shorthand border setter -func _set_border(sb: StyleBoxFlat, color: Color, width: float = 1, blend: bool = false) -> void: - sb.border_color = color - sb.border_blend = blend - sb.set_border_width_all(int(ceilf(width * scale))) diff --git a/ui/theme_default/theme_default.gd.uid b/ui/theme_default/theme_default.gd.uid deleted file mode 100644 index e8168ff0..00000000 --- a/ui/theme_default/theme_default.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c2nmcbo0pbk5u diff --git a/ui/theme_generator.gd b/ui/theme_generator.gd deleted file mode 100644 index 7ae79fb3..00000000 --- a/ui/theme_generator.gd +++ /dev/null @@ -1,31 +0,0 @@ -@tool -extends EditorScript - -var _save_path: String = "res://ui/theme_default/main.tres" - - -func _run() -> void: - var theme := MonologueTheme.new() - _update_existing_theme_instance(theme) - ResourceSaver.save(theme, _save_path) - print("Theme generated!") - - -func _update_existing_theme_instance(new_theme: Theme): - # When the editor uses the generated theme file, it loads the resource into - # memory. This means that when the new theme is saved, the existing one in - # memory is not updated or invalidated until the editor is restarted, - # leaving the UI unaffected. - # To fix this issue, the cached theme resource in memory is fetched and - # mutated in-place (using the fact that when a resource is loaded, Godot uses - # the shared instance in memory instead of loading a new instance from disk). - - if not ResourceLoader.exists(_save_path): - return - - var existing_theme = load(_save_path) - if not existing_theme is Theme: - return - - existing_theme.clear() - existing_theme.merge_with(new_theme) diff --git a/ui/theme_generator.gd.uid b/ui/theme_generator.gd.uid deleted file mode 100644 index 7f03c6ff..00000000 --- a/ui/theme_generator.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bern2031c4l7s diff --git a/ui/theme_layout.gd b/ui/theme_layout.gd new file mode 100644 index 00000000..e1287275 --- /dev/null +++ b/ui/theme_layout.gd @@ -0,0 +1,907 @@ +@abstract class_name ThemeLayout + +static var font_main: FontVariation = preload("res://ui/assets/fonts/Main.tres") +static var font_typing: FontVariation = preload("res://ui/assets/fonts/Typing.tres") + +static var font_size_sm: int = 12 +static var font_size_md: int = 14 +static var font_size_lg: int = 18 + +static var bg_primary_color: Color +static var bg_surface_color: Color +static var bg_elevated_color: Color +static var bg_higher_color: Color + +static var text_primary_color: Color +static var text_muted_color: Color +static var border_color: Color +static var selection_color: Color +static var disabled_selection_color: Color + +static var accent_color: Color +static var fail_color: Color +static var success_color: Color +static var highlight_color: Color + +static var radius_sm: int = 4 +static var radius_md: int = 6 +static var margin_sm: Vector2 = Vector2(5, 2) +static var margin_md: Vector2 = Vector2(10, 5) +static var spacing_sm: int = 5 +static var spacing_md: int = 10 +static var spacing_lg: int = 12 + +static var icon_xs: int = 12 +static var icon_sm: int = 16 +static var icon_md: int = 18 +static var icon_lg: int = 20 + +static var radio_checked_icon: DPITexture +static var radio_unchecked_icon: DPITexture + +static var is_theme_dark: bool + +static var _colormap_dict: Dictionary + + +static func color_difference(color1: Color, color2: Color) -> float: + return (absf(color1.r - color2.r) + absf(color1.g - color2.g) + absf(color1.b - color2.b)) / 3.0 + +static func recalculate_colors() -> void: + is_theme_dark = (bg_primary_color.get_luminance() < 0.5) + + bg_primary_color = Color("161616") if is_theme_dark else Color("f2f2f2") + bg_surface_color = Color("202020") if is_theme_dark else Color("e8e8e8") + bg_elevated_color = Color("323232") if is_theme_dark else Color("d6d6d6") + bg_higher_color = Color("424242") if is_theme_dark else Color("c8c8c8") + + text_primary_color = Color("d3d3d3") if is_theme_dark else Color("2c2c2c") + text_muted_color = Color("777777") if is_theme_dark else Color("888888") + border_color = Color("323232") if is_theme_dark else Color("d0d0d0") + selection_color = Color(bg_higher_color, 0.5) + disabled_selection_color = Color(bg_elevated_color, 0.5) + + accent_color = Color("b44040") + fail_color = Color("e86666ff") + success_color = Color("72e879") + highlight_color = Color("966fc5") + + _colormap_dict = { + "bg_primary": "#" + bg_primary_color.to_html(false), + "bg_surface": "#" + bg_surface_color.to_html(false), + "bg_elevated": "#" + bg_elevated_color.to_html(false), + "bg_higher": "#" + bg_higher_color.to_html(false), + "text_primary": "#" + text_primary_color.to_html(false), + "text_muted": "#" + text_muted_color.to_html(false), + "accent": "#" + accent_color.to_html(false), + "radius_sm": "%s" % radius_sm + } + + radio_checked_icon = DPITexture.create_from_string( + """ + + + """.format(_colormap_dict) + ) + + radio_unchecked_icon = DPITexture.create_from_string( + """ + + """.format(_colormap_dict) + ) + +static func rebuild_fonts() -> void: + font_main.base_font = FontFile.new() + font_typing.base_font = FontFile.new() + +static func generate_theme() -> Theme: + var start_time: float = Time.get_unix_time_from_system() + + recalculate_colors() + var theme := Theme.new() + theme.default_font = font_main + theme.default_font_size = 12 + + _setup_panel(theme) + _setup_button(theme) + _setup_checkbox(theme) + _setup_optionbutton(theme) + _setup_lineedit(theme) + _setup_scrollbar(theme) + _setup_separator(theme) + _setup_margincontainer(theme) + _setup_label(theme) + _setup_textedit(theme) + _setup_richtextlabel(theme) + _setup_foldablecontainer(theme) + _setup_boxcontainer(theme) + _setup_graphnode(theme) + _setup_graphedit(theme) + _setup_splitcontainer(theme) + _setup_tree(theme) + _setup_popupmenu(theme) + + var end_time: float = Time.get_unix_time_from_system() + var elasped: float = end_time - start_time + + Log.info("Theme generated in %s seconds." % elasped) + + return theme + +static func generate_and_apply_theme() -> void: + var default_theme: Theme = ThemeDB.get_project_theme() + default_theme.default_font = font_main + default_theme.default_font_size = 12 + var generated_theme: Theme = generate_theme() + default_theme.merge_with(generated_theme) + +static func _setup_panel(theme: Theme) -> void: + theme.add_type("PanelContainer") + var stylebox: StyleBoxFlat = StyleBoxFlat.new() + stylebox.set_corner_radius_all(radius_sm) + stylebox.content_margin_top = margin_md.y + stylebox.content_margin_bottom = margin_md.y + stylebox.content_margin_left = margin_md.x + stylebox.content_margin_right = margin_md.x + stylebox.bg_color = bg_surface_color + theme.set_stylebox("panel", "PanelContainer", stylebox) + + theme.add_type("EditorBackground") + theme.set_type_variation("EditorBackground", "PanelContainer") + var editor_bg_stylebox: StyleBoxFlat = StyleBoxFlat.new() + editor_bg_stylebox.bg_color = bg_primary_color + theme.set_stylebox("panel", "EditorBackground", editor_bg_stylebox) + + theme.add_type("EditorHeader") + theme.set_type_variation("EditorHeader", "PanelContainer") + var editor_header_stylebox: StyleBoxFlat = StyleBoxFlat.new() + editor_header_stylebox.content_margin_top = margin_md.y + editor_header_stylebox.content_margin_bottom = margin_md.y + editor_header_stylebox.content_margin_left = margin_md.x + editor_header_stylebox.content_margin_right = margin_md.x + editor_header_stylebox.bg_color = bg_surface_color + theme.set_stylebox("panel", "EditorHeader", editor_header_stylebox) + + theme.add_type("EditorPanel") + theme.set_type_variation("EditorPanel", "PanelContainer") + var editor_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + editor_panel_stylebox.set_corner_radius_all(radius_md) + editor_panel_stylebox.content_margin_top = margin_md.y + editor_panel_stylebox.content_margin_bottom = margin_md.y + editor_panel_stylebox.content_margin_left = margin_md.x + editor_panel_stylebox.content_margin_right = margin_md.x + editor_panel_stylebox.bg_color = bg_surface_color + theme.set_stylebox("panel", "EditorPanel", editor_panel_stylebox) + + theme.add_type("EditorGraphPanel") + theme.set_type_variation("EditorGraphPanel", "PanelContainer") + var editor_graph_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + editor_graph_panel_stylebox.set_corner_radius_all(radius_md) + editor_graph_panel_stylebox.set_content_margin_all(1) + editor_graph_panel_stylebox.set_border_width_all(1) + editor_graph_panel_stylebox.bg_color = bg_surface_color + editor_graph_panel_stylebox.border_color = bg_surface_color + theme.set_stylebox("panel", "EditorGraphPanel", editor_graph_panel_stylebox) + + theme.add_type("ConsolePanel") + theme.set_type_variation("ConsolePanel", "PanelContainer") + var console_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + console_panel_stylebox.bg_color = bg_primary_color + console_panel_stylebox.set_corner_radius_all(radius_md) + console_panel_stylebox.content_margin_top = margin_md.y + console_panel_stylebox.content_margin_bottom = margin_md.y + console_panel_stylebox.content_margin_left = margin_md.x + console_panel_stylebox.content_margin_right = margin_md.x + theme.set_stylebox("panel", "ConsolePanel", console_panel_stylebox) + + theme.add_type("ListFieldPanel") + theme.set_type_variation("ListFieldPanel", "PanelContainer") + var list_field_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + list_field_panel_stylebox.bg_color = bg_surface_color + list_field_panel_stylebox.set_corner_radius_all(radius_md) + list_field_panel_stylebox.set_content_margin_all(0) + theme.set_stylebox("panel", "ListFieldPanel", list_field_panel_stylebox) + + theme.add_type("ListItemPanel") + theme.set_type_variation("ListItemPanel", "PanelContainer") + var list_item_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + list_item_panel_stylebox.bg_color = Color(bg_primary_color, 0.5) + list_item_panel_stylebox.set_corner_radius_all(0) + list_item_panel_stylebox.content_margin_top = margin_md.y + list_item_panel_stylebox.content_margin_bottom = margin_md.y + list_item_panel_stylebox.content_margin_left = margin_md.x + list_item_panel_stylebox.content_margin_right = margin_md.x + theme.set_stylebox("panel", "ListItemPanel", list_item_panel_stylebox) + + theme.add_type("ListItemOddPanel") + theme.set_type_variation("ListItemOddPanel", "PanelContainer") + var list_item_odd_panel_stylebox: StyleBoxFlat = list_item_panel_stylebox.duplicate() + list_item_odd_panel_stylebox.bg_color = bg_primary_color + theme.set_stylebox("panel", "ListItemOddPanel", list_item_odd_panel_stylebox) + + theme.add_type("ColorPanel") + theme.set_type_variation("ColorPanel", "PanelContainer") + var color_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + color_panel_stylebox.bg_color = bg_surface_color + color_panel_stylebox.set_corner_radius_all(radius_md) + color_panel_stylebox.set_content_margin_all(0) + theme.set_stylebox("panel", "ColorPanel", color_panel_stylebox) + + +static func _setup_button(theme: Theme) -> void: + theme.add_type("Button") + theme.set_constant("h_separation", "Button", spacing_sm) + theme.set_constant("icon_max_width", "Button", icon_xs) + theme.set_color("font_color", "Button", text_primary_color) + theme.set_color("font_disabled_color", "Button", text_muted_color) + theme.set_color("font_focus_color", "Button", text_primary_color) + theme.set_color("font_hover_color", "Button", text_primary_color) + theme.set_color("font_pressed_color", "Button", text_primary_color) + theme.set_color("font_hover_pressed_color", "Button", text_primary_color) + theme.set_color("icon_normal_color", "Button", text_primary_color) + theme.set_color("icon_hover_color", "Button", text_primary_color) + theme.set_color("icon_pressed_color", "Button", text_primary_color) + theme.set_color("icon_hover_pressed_color", "Button", text_primary_color) + theme.set_color("icon_focus_color", "Button", text_primary_color) + theme.set_color("icon_disabled_color", "Button", text_muted_color) + theme.set_font_size("font_size", "Button", font_size_sm) + + var button_stylebox: StyleBoxFlat = StyleBoxFlat.new() + button_stylebox.set_corner_radius_all(radius_sm) + button_stylebox.content_margin_top = margin_sm.y + button_stylebox.content_margin_bottom = margin_sm.y + button_stylebox.content_margin_left = margin_sm.x + button_stylebox.content_margin_right = margin_sm.x + + var normal_button_stylebox: StyleBoxFlat = button_stylebox.duplicate() + normal_button_stylebox.draw_center = false + theme.set_stylebox("normal", "Button", normal_button_stylebox) + + var hover_button_stylebox: StyleBoxFlat = button_stylebox.duplicate() + hover_button_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("hover", "Button", hover_button_stylebox) + + var pressed_button_stylebox: StyleBoxFlat = button_stylebox.duplicate() + pressed_button_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("pressed", "Button", pressed_button_stylebox) + + var hover_pressed_button_stylebox: StyleBoxFlat = button_stylebox.duplicate() + hover_pressed_button_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("hover_pressed", "Button", hover_pressed_button_stylebox) + + var disabled_button_stylebox: StyleBoxFlat = button_stylebox.duplicate() + disabled_button_stylebox.draw_center = false + theme.set_stylebox("disabled", "Button", disabled_button_stylebox) + + var button_focus_stylebox: StyleBoxFlat = button_stylebox.duplicate() + button_focus_stylebox.draw_center = false + theme.set_stylebox("focus", "Button", button_focus_stylebox) + + theme.add_type("ToggleButton") + theme.set_type_variation("ToggleButton", "Button") + + var pressed_toggle_button_stylebox: StyleBoxFlat = pressed_button_stylebox.duplicate() + pressed_toggle_button_stylebox.bg_color = accent_color + theme.set_stylebox("pressed", "ToggleButton", pressed_toggle_button_stylebox) + + var hover_pressed_toggle_button_stylebox: StyleBoxFlat = hover_pressed_button_stylebox.duplicate() + hover_pressed_toggle_button_stylebox.bg_color = accent_color + theme.set_stylebox("hover_pressed", "ToggleButton", hover_pressed_toggle_button_stylebox) + + theme.add_type("PlainButton") + theme.set_type_variation("PlainButton", "Button") + + var normal_plain_button_stylebox: StyleBoxFlat = pressed_button_stylebox.duplicate() + normal_plain_button_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("normal", "PlainButton", normal_plain_button_stylebox) + + theme.add_type("IconButton") + theme.set_type_variation("IconButton", "Button") + + var normal_icon_button_stylebox: StyleBoxFlat = normal_button_stylebox.duplicate() + normal_icon_button_stylebox.draw_center = true + normal_icon_button_stylebox.bg_color = bg_elevated_color + normal_icon_button_stylebox.set_content_margin_all(margin_sm.x) + theme.set_stylebox("normal", "IconButton", normal_icon_button_stylebox) + + var hover_icon_button_stylebox: StyleBoxFlat = hover_button_stylebox.duplicate() + hover_icon_button_stylebox.set_content_margin_all(margin_sm.x) + theme.set_stylebox("hover", "IconButton", hover_icon_button_stylebox) + + var pressed_icon_button_stylebox: StyleBoxFlat = pressed_button_stylebox.duplicate() + pressed_icon_button_stylebox.bg_color = accent_color + pressed_icon_button_stylebox.set_content_margin_all(margin_sm.x) + theme.set_stylebox("pressed", "IconButton", pressed_icon_button_stylebox) + + var hover_pressed_icon_button_stylebox: StyleBoxFlat = hover_pressed_button_stylebox.duplicate() + hover_pressed_icon_button_stylebox.set_content_margin_all(margin_sm.x) + theme.set_stylebox("hover_pressed", "IconButton", hover_pressed_icon_button_stylebox) + + var disabled_icon_button_stylebox: StyleBoxFlat = disabled_button_stylebox.duplicate() + disabled_icon_button_stylebox.draw_center = true + disabled_icon_button_stylebox.bg_color = bg_elevated_color + disabled_icon_button_stylebox.set_content_margin_all(margin_sm.x) + theme.set_stylebox("disabled", "IconButton", disabled_icon_button_stylebox) + + theme.add_type("ToggleIconButton") + theme.set_type_variation("ToggleIconButton", "IconButton") + + var hover_pressed_toggle_icon_button_stylebox: StyleBoxFlat = hover_pressed_icon_button_stylebox.duplicate() + hover_pressed_toggle_icon_button_stylebox.bg_color = accent_color + theme.set_stylebox("hover_pressed", "ToggleIconButton", hover_pressed_toggle_icon_button_stylebox) + + theme.add_type("ListItemIconButton") + theme.set_type_variation("ListItemIconButton", "IconButton") + theme.set_color("icon_normal_color", "ListItemIconButton", text_muted_color) + theme.set_color("icon_hover_color", "ListItemIconButton", text_muted_color) + theme.set_color("icon_pressed_color", "ListItemIconButton", text_muted_color) + theme.set_color("icon_hover_pressed_color", "ListItemIconButton", text_muted_color) + theme.set_color("icon_focus_color", "ListItemIconButton", text_muted_color) + theme.set_color("icon_disabled_color", "ListItemIconButton", Color(text_muted_color, 0.5)) + + var normal_list_item_icon_button_stylebox: StyleBoxFlat = normal_icon_button_stylebox.duplicate() + normal_list_item_icon_button_stylebox.draw_center = false + theme.set_stylebox("normal", "ListItemIconButton", normal_list_item_icon_button_stylebox) + + var hover_list_item_icon_button_stylebox: StyleBoxFlat = hover_icon_button_stylebox.duplicate() + hover_list_item_icon_button_stylebox.draw_center = false + theme.set_stylebox("hover", "ListItemIconButton", hover_list_item_icon_button_stylebox) + + var pressed_list_item_icon_button_stylebox: StyleBoxFlat = pressed_icon_button_stylebox.duplicate() + pressed_list_item_icon_button_stylebox.draw_center = false + theme.set_stylebox("pressed", "ListItemIconButton", pressed_list_item_icon_button_stylebox) + + var hover_pressed_list_item_icon_button_stylebox: StyleBoxFlat = hover_pressed_icon_button_stylebox.duplicate() + hover_pressed_list_item_icon_button_stylebox.draw_center = false + theme.set_stylebox("hover_pressed", "ListItemIconButton", hover_pressed_list_item_icon_button_stylebox) + + var disabled_list_item_icon_button_stylebox: StyleBoxFlat = disabled_icon_button_stylebox.duplicate() + disabled_list_item_icon_button_stylebox.draw_center = false + theme.set_stylebox("disabled", "ListItemIconButton", disabled_list_item_icon_button_stylebox) + +static func _setup_checkbox(theme: Theme) -> void: + theme.add_type("CheckBox") + theme.set_constant("h_separation", "CheckBox", spacing_sm) + theme.set_constant("icon_max_width", "CheckBox", icon_lg) + theme.set_color("font_color", "CheckBox", text_primary_color) + theme.set_color("font_disabled_color", "CheckBox", text_muted_color) + theme.set_color("font_focus_color", "CheckBox", text_primary_color) + theme.set_color("font_hover_color", "CheckBox", text_primary_color) + theme.set_color("font_pressed_color", "CheckBox", text_primary_color) + theme.set_color("font_hover_pressed_color", "CheckBox", text_primary_color) + + var checked_texture: DPITexture = DPITexture.create_from_string( + """ + + + """.format(_colormap_dict) + ) + var checked_disabled_texture: DPITexture = DPITexture.create_from_string( + """ + + + """.format(_colormap_dict) + ) + var unchecked_texture: DPITexture = DPITexture.create_from_string( + """ + + """.format(_colormap_dict) + ) + var unchecked_disabled_texture: DPITexture = DPITexture.create_from_string( + """ + + """.format(_colormap_dict) + ) + + theme.set_icon("checked", "CheckBox", checked_texture) + theme.set_icon("checked_disabled", "CheckBox", checked_disabled_texture) + theme.set_icon("unchecked", "CheckBox", unchecked_texture) + theme.set_icon("unchecked_disabled", "CheckBox", unchecked_disabled_texture) + + var checkbox_stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + checkbox_stylebox.content_margin_top = margin_sm.y + checkbox_stylebox.content_margin_bottom = margin_sm.y + checkbox_stylebox.content_margin_left = margin_sm.x + checkbox_stylebox.content_margin_right = margin_sm.x + theme.set_stylebox("normal", "CheckBox", checkbox_stylebox) + theme.set_stylebox("pressed", "CheckBox", checkbox_stylebox) + theme.set_stylebox("hover", "CheckBox", checkbox_stylebox) + theme.set_stylebox("hover_pressed", "CheckBox", checkbox_stylebox) + theme.set_stylebox("focus", "CheckBox", checkbox_stylebox) + theme.set_stylebox("disabled", "CheckBox", checkbox_stylebox) + +static func _setup_optionbutton(theme: Theme) -> void: + theme.add_type("OptionButton") + theme.set_constant("h_separation", "OptionButton", spacing_sm) + theme.set_constant("icon_max_width", "OptionButton", icon_xs) + theme.set_constant("align_to_largest_style", "OptionButton", 1) + theme.set_constant("arrow_margin", "OptionButton", spacing_sm) + theme.set_constant("modulate_arrow", "OptionButton", 1) + theme.set_font_size("font_size", "OptionButton", font_size_sm) + theme.set_icon("arrow", "OptionButton", preload("res://ui/assets/icons/arrow_down.svg")) + + var stylebox: StyleBoxFlat = StyleBoxFlat.new() + stylebox.set_corner_radius_all(radius_sm) + stylebox.content_margin_top = margin_md.y + stylebox.content_margin_bottom = margin_md.y + stylebox.content_margin_left = margin_md.x + stylebox.content_margin_right = margin_md.x + + var normal_stylebox: StyleBoxFlat = stylebox.duplicate() + normal_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("normal", "OptionButton", normal_stylebox) + theme.set_stylebox("pressed", "OptionButton", normal_stylebox) + theme.set_stylebox("hover_pressed", "OptionButton", normal_stylebox) + + var focus_stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + theme.set_stylebox("focus", "OptionButton", focus_stylebox) + + var hover_stylebox: StyleBoxFlat = stylebox.duplicate() + hover_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("hover", "OptionButton", hover_stylebox) + + var disabled_stylebox: StyleBoxFlat = stylebox.duplicate() + disabled_stylebox.bg_color = bg_primary_color + theme.set_stylebox("disabled", "OptionButton", disabled_stylebox) + +static func _setup_lineedit(theme: Theme) -> void: + theme.add_type("LineEdit") + theme.set_color("caret_color", "LineEdit", text_primary_color) + theme.set_color("font_color", "LineEdit", text_primary_color) + theme.set_color("font_uneditable_color", "LineEdit", text_muted_color) + theme.set_color("font_placeholder_color", "LineEdit", text_muted_color) + theme.set_color("selection_color", "LineEdit", selection_color) + theme.set_color("disabled_selection_color", "LineEdit", selection_color) + theme.set_font_size("font_size", "LineEdit", font_size_sm) + theme.set_font("font", "LineEdit", font_main) + + var stylebox: StyleBoxFlat = StyleBoxFlat.new() + stylebox.set_corner_radius_all(radius_sm) + stylebox.content_margin_top = margin_md.y + stylebox.content_margin_bottom = margin_md.y + stylebox.content_margin_left = margin_md.x + stylebox.content_margin_right = margin_md.x + + var disabled_stylebox: StyleBoxFlat = stylebox.duplicate() + disabled_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("read_only", "LineEdit", disabled_stylebox) + + var normal_stylebox: StyleBoxFlat = stylebox.duplicate() + normal_stylebox.bg_color = bg_primary_color + theme.set_stylebox("normal", "LineEdit", normal_stylebox) + + var hover_stylebox: StyleBoxFlat = stylebox.duplicate() + hover_stylebox.draw_center = false + theme.set_stylebox("hover", "LineEdit", hover_stylebox) + + var focus_stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + theme.set_stylebox("focus", "LineEdit", focus_stylebox) + + theme.add_type("LineEditListItemPreview") + theme.set_type_variation("LineEditListItemPreview", "LineEdit") + + var disabled_preview_stylebox: StyleBoxFlat = disabled_stylebox.duplicate() + disabled_preview_stylebox.bg_color = bg_surface_color + theme.set_stylebox("read_only", "LineEditListItemPreview", disabled_preview_stylebox) + + var normal_preview_stylebox: StyleBoxFlat = normal_stylebox.duplicate() + normal_preview_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("normal", "LineEditListItemPreview", normal_preview_stylebox) + + var hover_preview_stylebox: StyleBoxFlat = hover_stylebox.duplicate() + theme.set_stylebox("hover", "LineEditListItemPreview", hover_preview_stylebox) + + var focus_preview_stylebox: StyleBoxEmpty = focus_stylebox.duplicate() + theme.set_stylebox("focus", "LineEditListItemPreview", focus_preview_stylebox) + +static func _setup_scrollbar(theme: Theme) -> void: + theme.add_type("HScrollBar") + theme.add_type("VScrollBar") + var stylebox: StyleBoxFlat = StyleBoxFlat.new() + stylebox.set_corner_radius_all(1) + stylebox.content_margin_left = 1.0 + stylebox.content_margin_right = 1.0 + + var grabber_stylebox: StyleBoxFlat = stylebox.duplicate() + grabber_stylebox.bg_color = border_color + theme.set_stylebox("grabber", "HScrollBar", grabber_stylebox) + theme.set_stylebox("grabber", "VScrollBar", grabber_stylebox) + + var grabber_stylebox_pressed: StyleBoxFlat = stylebox.duplicate() + grabber_stylebox_pressed.bg_color = text_muted_color + theme.set_stylebox("grabber_highlight", "HScrollBar", grabber_stylebox_pressed) + theme.set_stylebox("grabber_pressed", "HScrollBar", grabber_stylebox_pressed) + theme.set_stylebox("grabber_highlight", "VScrollBar", grabber_stylebox_pressed) + theme.set_stylebox("grabber_pressed", "VScrollBar", grabber_stylebox_pressed) + + var h_scroll_stylebox: StyleBoxFlat = StyleBoxFlat.new() + h_scroll_stylebox.set_corner_radius_all(3) + h_scroll_stylebox.content_margin_top = margin_sm.y + h_scroll_stylebox.content_margin_bottom = margin_sm.y + h_scroll_stylebox.content_margin_left = margin_sm.x + h_scroll_stylebox.content_margin_right = margin_sm.x + h_scroll_stylebox.draw_center = false + theme.set_stylebox("scroll", "HScrollBar", h_scroll_stylebox) + + var v_scroll_stylebox: StyleBoxFlat = StyleBoxFlat.new() + v_scroll_stylebox.set_corner_radius_all(3) + v_scroll_stylebox.content_margin_top = margin_sm.x + v_scroll_stylebox.content_margin_bottom = margin_sm.x + v_scroll_stylebox.content_margin_left = margin_sm.y + v_scroll_stylebox.content_margin_right = margin_sm.y + v_scroll_stylebox.draw_center = false + theme.set_stylebox("scroll", "VScrollBar", v_scroll_stylebox) + +static func _setup_separator(theme: Theme) -> void: + theme.add_type("HSeparator") + var h_stylebox: StyleBoxLine = StyleBoxLine.new() + h_stylebox.color = border_color + h_stylebox.thickness = 1 + theme.set_stylebox("separator", "HSeparator", h_stylebox) + + theme.add_type("SmallHSeparator") + theme.set_type_variation("SmallHSeparator", "HSeparator") + var h_small_stylebox: StyleBoxLine = h_stylebox.duplicate() + h_small_stylebox.grow_begin = -spacing_sm + h_small_stylebox.grow_end = -spacing_sm + theme.set_stylebox("separator", "SmallHSeparator", h_small_stylebox) + + theme.add_type("WideHSeparator") + theme.set_type_variation("SmallHSeparator", "HSeparator") + var h_wide_stylebox: StyleBoxLine = h_stylebox.duplicate() + h_wide_stylebox.grow_begin = spacing_sm + h_wide_stylebox.grow_end = spacing_sm + theme.set_stylebox("separator", "SmallHSeparator", h_wide_stylebox) + + theme.add_type("UltraWideHSeparator") + theme.set_type_variation("UltraWideHSeparator", "HSeparator") + var h_ultra_wide_stylebox: StyleBoxLine = h_stylebox.duplicate() + h_ultra_wide_stylebox.grow_begin = spacing_lg + h_ultra_wide_stylebox.grow_end = spacing_lg + theme.set_stylebox("separator", "UltraWideHSeparator", h_ultra_wide_stylebox) + + theme.add_type("VSeparator") + var v_stylebox: StyleBoxLine = h_stylebox.duplicate() + v_stylebox.vertical = true + theme.set_stylebox("separator", "VSeparator", v_stylebox) + + theme.add_type("SmallVSeparator") + theme.set_type_variation("SmallVSeparator", "VSeparator") + var v_small_stylebox: StyleBoxLine = v_stylebox.duplicate() + v_small_stylebox.vertical = true + theme.set_stylebox("separator", "SmallVSeparator", v_small_stylebox) + + +static func _setup_margincontainer(theme: Theme) -> void: + theme.add_type("MarginContainer") + theme.set_constant("margin_left", "MarginContainer", spacing_sm) + theme.set_constant("margin_top", "MarginContainer", spacing_sm) + theme.set_constant("margin_right", "MarginContainer", spacing_sm) + theme.set_constant("margin_bottom", "MarginContainer", spacing_sm) + + theme.add_type("MediumMarginContainer") + theme.set_type_variation("MediumMarginContainer", "MarginContainer") + theme.set_constant("margin_left", "MarginContainer", spacing_md) + theme.set_constant("margin_top", "MarginContainer", spacing_md) + theme.set_constant("margin_right", "MarginContainer", spacing_md) + theme.set_constant("margin_bottom", "MarginContainer", spacing_md) + +static func _setup_label(theme: Theme) -> void: + var stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + stylebox.content_margin_top = margin_md.y + stylebox.content_margin_bottom = margin_md.y + + theme.add_type("Label") + theme.set_font("font", "Label", font_main) + theme.set_color("font_color", "Label", text_primary_color) + theme.set_font_size("font_size", "Label", font_size_md) + # TODO: Differenciate more labels. + #theme.set_stylebox("normal", "Label", stylebox) + + theme.add_type("TitleLabel") + theme.set_type_variation("TitleLabel", "Label") + theme.set_font_size("font_size", "TitleLabel", font_size_sm) + theme.set_color("font_color", "TitleLabel", text_muted_color) + + theme.add_type("NoteLabel") + theme.set_type_variation("NoteLabel", "Label") + theme.set_color("font_color", "NoteLabel", text_muted_color) + + theme.add_type("WarnLabel") + theme.set_type_variation("WarnLabel", "Label") + theme.set_color("font_color", "WarnLabel", fail_color) + theme.set_font_size("font_size", "WarnLabel", font_size_md) + + theme.add_type("GraphNodeViewTitleLabel") + theme.set_type_variation("GraphNodeViewTitleLabel", "Label") + theme.set_color("font_color", "GraphNodeViewTitleLabel", text_muted_color) + theme.set_font_size("font_size", "GraphNodeViewTitleLabel", font_size_lg) + + theme.add_type("GraphNodeViewValueLabel") + theme.set_type_variation("GraphNodeViewValueLabel", "Label") + theme.set_color("font_color", "GraphNodeViewValueLabel", text_primary_color) + theme.set_font_size("font_size", "GraphNodeViewValueLabel", font_size_md) + + theme.add_type("TooltipLabel") + theme.set_color("font_color", "TooltipLabel", text_primary_color) + theme.set_font_size("font_size", "TooltipLabel", font_size_sm) + theme.set_font("font", "TooltipLabel", font_main) + +static func _setup_textedit(theme: Theme) -> void: + theme.add_type("TextEdit") + theme.set_color("search_result_color", "TextEdit", accent_color) + theme.set_color("search_result_border", "TextEdit", Color.TRANSPARENT) + theme.set_color("caret_color", "TextEdit", text_primary_color) + theme.set_color("caret_background_color", "TextEdit", bg_primary_color) + theme.set_color("selection_color", "TextEdit", selection_color) + theme.set_color("font_color", "TextEdit", text_primary_color) + theme.set_color("font_readonly_color", "TextEdit", text_muted_color) + theme.set_color("font_placeholder_color", "TextEdit", text_muted_color) + theme.set_font("font", "TextEdit", font_main) + theme.set_font_size("font_size", "TextEdit", font_size_sm) + + var normal_stylebox: StyleBoxFlat = StyleBoxFlat.new() + normal_stylebox.bg_color = bg_primary_color + normal_stylebox.set_corner_radius_all(radius_sm) + normal_stylebox.content_margin_top = margin_sm.y + normal_stylebox.content_margin_bottom = margin_sm.y + normal_stylebox.content_margin_left = margin_sm.x + normal_stylebox.content_margin_right = margin_sm.x + theme.set_stylebox("normal", "TextEdit", normal_stylebox) + + var focus_stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + theme.set_stylebox("focus", "TextEdit", focus_stylebox) + + var disabled_stylebox: StyleBoxFlat = normal_stylebox.duplicate() + disabled_stylebox.bg_color = bg_elevated_color + theme.set_stylebox("read_only", "TextEdit", disabled_stylebox) + + +static func _setup_richtextlabel(theme: Theme) -> void: + theme.add_type("RichTextLabel") + theme.set_color("default_color", "RichTextLabel", text_primary_color) + theme.set_color("selection_color", "RichTextLabel", selection_color) + theme.set_color("table_odd_row_bg", "RichTextLabel", Color(bg_higher_color, 0.25)) + theme.set_color("table_even_row_bg", "RichTextLabel", Color(bg_higher_color, 0.1)) + theme.set_color("table_border", "RichTextLabel", border_color) + theme.set_font_size("normal_font_size", "RichTextLabel", font_size_sm) + theme.set_font_size("bold_font_size", "RichTextLabel", font_size_sm) + theme.set_font_size("bold_italics_font_size", "RichTextLabel", font_size_sm) + theme.set_font_size("italics_font_size", "RichTextLabel", font_size_sm) + theme.set_font_size("mono_font_size", "RichTextLabel", font_size_sm) + + +static func _setup_foldablecontainer(theme: Theme) -> void: + theme.add_type("FoldableContainer") + theme.set_color("font_color", "FoldableContainer", text_muted_color) + theme.set_color("hover_font_color", "FoldableContainer", text_muted_color) + theme.set_color("collapsed_font_color", "FoldableContainer", text_muted_color) + theme.set_constant("h_separation", "FoldableContainer", spacing_md) + theme.set_font_size("font_size", "FoldableContainer", font_size_sm) + theme.set_icon("expanded_arrow", "FoldableContainer", preload("res://ui/assets/icons/expanded_arrow.svg")) + theme.set_icon("expanded_arrow_mirrored", "FoldableContainer", preload("res://ui/assets/icons/expanded_arrow_mirrored.svg")) + theme.set_icon("folded_arrow", "FoldableContainer", preload("res://ui/assets/icons/folded_arrow.svg")) + theme.set_icon("folded_arrow_mirrored", "FoldableContainer", preload("res://ui/assets/icons/folded_arrow_mirrored.svg")) + + var title_panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + title_panel_stylebox.bg_color = bg_surface_color + title_panel_stylebox.content_margin_top = margin_sm.y + title_panel_stylebox.content_margin_bottom = margin_sm.y + title_panel_stylebox.content_margin_left = margin_sm.x + title_panel_stylebox.content_margin_right = margin_sm.x + theme.set_stylebox("title_panel", "FoldableContainer", title_panel_stylebox) + + var title_hover_panel_stylebox: StyleBoxFlat = title_panel_stylebox.duplicate() + theme.set_stylebox("title_hover_panel", "FoldableContainer", title_hover_panel_stylebox) + + var title_collapsed_panel_stylebox: StyleBoxFlat = title_panel_stylebox.duplicate() + theme.set_stylebox("title_collapsed_panel", "FoldableContainer", title_collapsed_panel_stylebox) + + var title_collapsed_hover_panel_stylebox: StyleBoxFlat = title_collapsed_panel_stylebox.duplicate() + theme.set_stylebox("title_collapsed_hover_panel", "FoldableContainer", title_collapsed_hover_panel_stylebox) + + var panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + panel_stylebox.bg_color = bg_surface_color + panel_stylebox.content_margin_top = margin_sm.y + panel_stylebox.content_margin_bottom = margin_sm.y + panel_stylebox.content_margin_left = margin_sm.x + panel_stylebox.content_margin_right = margin_sm.x + theme.set_stylebox("panel", "FoldableContainer", panel_stylebox) + + var focus_stylebox: StyleBoxEmpty = StyleBoxEmpty.new() + theme.set_stylebox("focus", "FoldableContainer", focus_stylebox) + + theme.add_type("ProjectFoldableContainer") + theme.set_type_variation("ProjectFoldableContainer", "FoldableContainer") + +static func _setup_boxcontainer(theme: Theme) -> void: + theme.add_type("HBoxContainer") + theme.set_constant("separation", "HBoxContainer", spacing_sm) + + theme.add_type("HBoxContainerMedium") + theme.set_type_variation("HBoxContainerMedium", "HBoxContainer") + theme.set_constant("separation", "HBoxContainerMedium", spacing_md) + + theme.add_type("HBoxContainerLarge") + theme.set_type_variation("HBoxContainerLarge", "HBoxContainer") + theme.set_constant("separation", "HBoxContainerLarge", spacing_lg) + + theme.add_type("HBoxContainerCompact") + theme.set_type_variation("HBoxContainerCompact", "HBoxContainer") + theme.set_constant("separation", "HBoxContainerCompact", 0) + + theme.add_type("VBoxContainer") + theme.set_constant("separation", "VBoxContainer", spacing_sm) + + theme.add_type("VBoxContainerMedium") + theme.set_type_variation("VBoxContainerMedium", "VBoxContainer") + theme.set_constant("separation", "VBoxContainerMedium", spacing_md) + + theme.add_type("VBoxContainerLarge") + theme.set_type_variation("VBoxContainerLarge", "VBoxContainer") + theme.set_constant("separation", "VBoxContainerLarge", spacing_lg) + + theme.add_type("VBoxContainerCompact") + theme.set_type_variation("VBoxContainerCompact", "VBoxContainer") + theme.set_constant("separation", "VBoxContainerCompact", 0) + +static func _setup_graphnode(theme: Theme) -> void: + theme.add_type("GraphNode") + theme.set_constant("separation", "GraphNode", spacing_sm) + + var panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + panel_stylebox.set_corner_radius_all(radius_md) + panel_stylebox.set_border_width_all(1) + panel_stylebox.bg_color = bg_surface_color + panel_stylebox.content_margin_top = margin_md.y + panel_stylebox.content_margin_bottom = margin_md.y + panel_stylebox.content_margin_left = margin_md.x + panel_stylebox.content_margin_right = margin_md.x + panel_stylebox.border_color = border_color + panel_stylebox.shadow_color = Color("000000", 0.1) + panel_stylebox.shadow_size = 12 + + var panel_selected_stylebox: StyleBoxFlat = panel_stylebox.duplicate() + panel_selected_stylebox.bg_color = bg_elevated_color + panel_selected_stylebox.border_color = bg_elevated_color + + theme.set_stylebox("panel", "GraphNode", panel_stylebox) + theme.set_stylebox("panel_selected", "GraphNode", panel_selected_stylebox) + theme.set_stylebox("titlebar", "GraphNode", StyleBoxEmpty.new()) + theme.set_stylebox("titlebar_selected", "GraphNode", StyleBoxEmpty.new()) + theme.set_stylebox("slot", "GraphNode", StyleBoxEmpty.new()) + +static func _setup_graphedit(theme: Theme) -> void: + theme.add_type("GraphEdit") + theme.set_color("grid_major", "GraphEdit", Color("ffffff", 0.1)) + theme.set_color("grid_minor", "GraphEdit", Color("ffffff", 0.1)) + + var stylebox := StyleBoxFlat.new() + stylebox.bg_color = bg_primary_color + stylebox.set_corner_radius_all(0) + theme.set_stylebox("panel", "GraphEdit", stylebox) + +static func _setup_splitcontainer(theme: Theme) -> void: + theme.add_type("SplitContainer") + theme.set_constant("separation", "SplitContainer", spacing_md) + theme.set_constant("minimum_grab_thickness", "SplitContainer", spacing_lg) + +static func _setup_tree(theme: Theme) -> void: + theme.add_type("Tree") + theme.set_constant("icon_max_width", "Tree", 14) + theme.set_constant("h_separation", "Tree", margin_sm.x as int) + theme.set_constant("v_separation", "Tree", margin_sm.y as int) + theme.set_constant("inner_item_margin_bottom", "Tree", margin_sm.x as int) + theme.set_constant("inner_item_margin_left", "Tree", margin_sm.x as int) + theme.set_constant("inner_item_margin_top", "Tree", margin_sm.x as int) + theme.set_constant("inner_item_margin_right", "Tree", margin_sm.x as int) + theme.set_constant("draw_relationship_lines", "Tree", 1) + theme.set_constant("draw_guides", "Tree", 0) + theme.set_constant("relationship_line_width", "Tree", 0) + theme.set_constant("parent_hl_line_width", "Tree", 1) + theme.set_constant("children_hl_line_width", "Tree", 0) + theme.set_icon("checked", "Tree", preload("res://ui/assets/icons/checked.svg")) + #theme.set_icon("unchecked", "Tree", preload("res://ui/assets/icons/unchecked.svg")) + theme.set_icon( + "checked_disabled", "Tree", preload("res://ui/assets/icons/checked_disabled.svg") + ) + #theme.set_icon( + #"unchecked_disabled", + #"Tree", + #preload("res://ui/assets/icons/unchecked_disabled.svg") + #) + + var tree_stylebox: StyleBoxFlat = StyleBoxFlat.new() + tree_stylebox.bg_color = bg_surface_color + tree_stylebox.set_corner_radius_all(radius_sm) + tree_stylebox.content_margin_top = margin_sm.y + tree_stylebox.content_margin_bottom = margin_sm.y + tree_stylebox.content_margin_left = margin_sm.x + tree_stylebox.content_margin_right = margin_sm.x + theme.set_stylebox("panel", "Tree", tree_stylebox) + + +static func _setup_popupmenu(theme: Theme) -> void: + theme.add_type("PopupMenu") + theme.set_color("font_color", "PopupMenu", text_primary_color) + theme.set_color("font_accelerator_color", "PopupMenu", text_muted_color) + theme.set_color("font_disabled_color", "PopupMenu", text_muted_color) + theme.set_color("font_hover_color", "PopupMenu", text_primary_color) + theme.set_color("font_separator_color", "PopupMenu", border_color) + theme.set_constant("indent", "PopupMenu", spacing_md) + theme.set_constant("h_separation", "PopupMenu", int(margin_md.x)) + theme.set_constant("v_separation", "PopupMenu", int(margin_md.y)) + theme.set_constant("outline_size", "PopupMenu", 0) + theme.set_constant("separator_outline_size", "PopupMenu", 0) + theme.set_constant("item_start_padding", "PopupMenu", int(margin_md.x)) + theme.set_constant("item_end_padding", "PopupMenu", int(margin_md.x)) + theme.set_constant("icon_max_width", "PopupMenu", icon_xs) + theme.set_constant("gutter_compact", "PopupMenu", 0) + theme.set_font("font", "PopupMenu", font_main) + theme.set_font("font_separator", "PopupMenu", font_main) + theme.set_font_size("font_size", "PopupMenu", font_size_sm) + theme.set_font_size("font_separator_size", "PopupMenu", font_size_sm) + + var menu_checked_icon: DPITexture = preload("res://ui/assets/theme/menu_checked.svg") + menu_checked_icon.color_map[Color.WHITE] = text_primary_color + menu_checked_icon.color_map[Color.BLACK] = bg_primary_color + theme.set_icon("checked", "PopupMenu", menu_checked_icon) + + var menu_checked_disabled_icon: DPITexture = preload("res://ui/assets/theme/menu_checked.svg") + menu_checked_disabled_icon.color_map[Color.WHITE] = text_muted_color + menu_checked_disabled_icon.color_map[Color.BLACK] = bg_primary_color + theme.set_icon("checked_disabled", "PopupMenu", menu_checked_disabled_icon) + + var menu_unchecked_icon: DPITexture = preload("res://ui/assets/theme/menu_unchecked.svg") + menu_unchecked_icon.color_map[Color.WHITE] = text_muted_color + theme.set_icon("unchecked", "PopupMenu", menu_unchecked_icon) + + var menu_unchecked_disabled_icon: DPITexture = preload("res://ui/assets/theme/menu_unchecked.svg") + menu_unchecked_disabled_icon.color_map[Color.WHITE] = text_muted_color + theme.set_icon("unchecked_disabled", "PopupMenu", menu_unchecked_disabled_icon) + + theme.set_icon("radio_checked", "PopupMenu", radio_checked_icon) + + var menu_radio_checked_disabled_icon: DPITexture = preload("res://ui/assets/theme/menu_radio_checked.svg") + menu_radio_checked_disabled_icon.color_map[Color.WHITE] = text_muted_color + menu_radio_checked_disabled_icon.color_map[Color.BLACK] = bg_primary_color + theme.set_icon("radio_checked_disabled", "PopupMenu", menu_radio_checked_disabled_icon) + + theme.set_icon("radio_unchecked", "PopupMenu", radio_unchecked_icon) + + var menu_radio_unchecked_disabled_icon: DPITexture = preload("res://ui/assets/theme/menu_radio_unchecked.svg") + menu_radio_unchecked_disabled_icon.color_map[Color.WHITE] = text_muted_color + theme.set_icon("radio_unchecked_disabled", "PopupMenu", menu_radio_unchecked_disabled_icon) + + theme.set_icon("submenu", "PopupMenu", preload("res://ui/assets/icons/folded_arrow.svg")) + theme.set_icon("submenu_mirrored", "PopupMenu", preload("res://ui/assets/icons/folded_arrow_mirrored.svg")) + + var panel_stylebox: StyleBoxFlat = StyleBoxFlat.new() + panel_stylebox.bg_color = bg_primary_color + panel_stylebox.set_corner_radius_all(radius_md) + panel_stylebox.set_border_width_all(1) + panel_stylebox.border_color = border_color + panel_stylebox.content_margin_top = margin_md.y + panel_stylebox.content_margin_bottom = margin_md.y + panel_stylebox.content_margin_left = margin_md.x + panel_stylebox.content_margin_right = margin_md.x + theme.set_stylebox("panel", "PopupMenu", panel_stylebox) + + var hover_stylebox: StyleBoxFlat = StyleBoxFlat.new() + hover_stylebox.bg_color = bg_surface_color + panel_stylebox.set_corner_radius_all(radius_sm) + panel_stylebox.set_border_width_all(1) + panel_stylebox.border_color = border_color + panel_stylebox.content_margin_top = margin_md.y + panel_stylebox.content_margin_bottom = margin_md.y + panel_stylebox.content_margin_left = margin_md.x + panel_stylebox.content_margin_right = margin_md.x + theme.set_stylebox("hover", "PopupMenu", hover_stylebox) + + var separator_stylebox: StyleBoxLine = StyleBoxLine.new() + separator_stylebox.color = border_color + separator_stylebox.thickness = 1 + theme.set_stylebox("separator", "PopupMenu", separator_stylebox) + theme.set_stylebox("labeled_separator_left", "PopupMenu", separator_stylebox) + theme.set_stylebox("labeled_separator_right", "PopupMenu", separator_stylebox) + diff --git a/ui/theme_layout.gd.uid b/ui/theme_layout.gd.uid new file mode 100644 index 00000000..8b43e373 --- /dev/null +++ b/ui/theme_layout.gd.uid @@ -0,0 +1 @@ +uid://dvjadnh82hqsn diff --git a/unit/empty_test.tscn b/unit/empty_test.tscn index 799dab48..e0c73cd3 100644 --- a/unit/empty_test.tscn +++ b/unit/empty_test.tscn @@ -1,6 +1,6 @@ [gd_scene format=3 uid="uid://dg6mqgx8xwcm6"] -[node name="EmptyTest" type="Control"] +[node name="EmptyTest" type="Control" unique_id=1001060751] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 diff --git a/unit/test_action_node.gd b/unit/test_action_node.gd deleted file mode 100644 index d1a8186d..00000000 --- a/unit/test_action_node.gd +++ /dev/null @@ -1,47 +0,0 @@ -extends GdUnitTestSuite - -var action_node: ActionNode - - -func before_test(): - action_node = auto_free(ActionNode.new()) - var graph_runner = scene_runner("res://common/layouts/graph_edit/monologue_graph_edit.tscn") - graph_runner.invoke("add_child", action_node) - - action_node.arg_box = VBoxContainer.new() - action_node.add_child(action_node.arg_box) - action_node.action_label = mock(Label) - action_node.no_args_label = mock(Label) - - -func test_from_dict(): - var arg1_dict = {"Name": "first_argument", "Type": "Variable", "Value": "my_variable_name"} - var arg2_dict = {"Name": "second_argument", "Type": "Boolean", "Value": true} - - action_node._from_dict( - { - "$type": "NodeAction", - "ID": "test_v3_id", - "NextID": "next_v3_id", - "Action": "my_function_name", - "Arguments": [arg1_dict, arg2_dict] - } - ) - action_node.load_arguments(action_node.arguments.value) - assert_str(action_node.action.value).is_equal("my_function_name") - assert_dict(action_node.get_arguments()[0]._to_dict()).is_equal(arg1_dict) - assert_dict(action_node.get_arguments()[1]._to_dict()).is_equal(arg2_dict) - - -func test_arguments_to_dict(): - var arg1_dict = {"Name": "the_first_argument", "Type": "Integer", "Value": -24} - var arg1 = action_node.add_argument(arg1_dict) - var arg2 = action_node.add_argument() - action_node.load_arguments([arg1._to_dict(), arg2._to_dict()]) - - var test_dict = {} - action_node._to_fields(test_dict) - assert_dict(test_dict).contains_keys(["Arguments"]) - - var args = test_dict.get("Arguments", []) - assert_array(args).is_equal([arg1_dict, {"Name": "", "Type": "Boolean", "Value": false}]) diff --git a/unit/test_action_node.gd.uid b/unit/test_action_node.gd.uid deleted file mode 100644 index a5b089e1..00000000 --- a/unit/test_action_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b6assbdrjxxfe diff --git a/unit/test_choice_node.gd b/unit/test_choice_node.gd deleted file mode 100644 index 88556b37..00000000 --- a/unit/test_choice_node.gd +++ /dev/null @@ -1,152 +0,0 @@ -extends GdUnitTestSuite - -var switcher # language switcher -var lang1 # language option 1 -var lang2 # language option 2 - - -func before_test(): - switcher = mock(LanguageSwitcher) - lang1 = auto_free(LanguageOption.new()) - lang2 = auto_free(LanguageOption.new()) - lang1.line_edit = mock(LineEdit) - lang2.line_edit = mock(LineEdit) - lang1.name = "LanguageOption1" - lang1.language_name = "Lang1" - lang2.name = "LanguageOption2" - lang2.language_name = "Lang2" - do_return(lang1).on(switcher).get_by_node_name(&"LanguageOption1") - do_return(lang1).on(switcher).get_by_node_name("LanguageOption1") - do_return(lang2).on(switcher).get_by_node_name(&"LanguageOption2") - do_return(lang2).on(switcher).get_by_node_name("LanguageOption2") - do_return({"Lang1": lang1.name, "Lang2": lang2.name}).on(switcher).get_languages() - - do_return(lang1).on(switcher).get_current_language() - GlobalVariables.language_switcher = switcher - - -func test_add_option(): - var runner = scene_runner("res://unit/empty_test.tscn") - var choice_node = auto_free(ChoiceNode.new()) - var mock_graph_edit = mock(MonologueGraphEdit, CALL_REAL_FUNC) - mock_graph_edit.add_child(choice_node) - runner.invoke("add_child", mock_graph_edit) - - var option_node = choice_node.add_option( - { - "$type": "NodeOption", - "ID": "addid", - "NextID": "nexop", - "Option": {"Lang2": "lang2test"}, - "EnableByDefault": true, - "OneShot": false - } - ) - choice_node.options.value.append(option_node._to_dict()) - var option_dict = choice_node.options.value[2].get("Option") - assert_dict(option_dict).is_equal({"Lang2": "lang2test"}) - - -func test_to_fields(): - var runner = scene_runner("res://nodes/choice_node/choice_node.tscn") - var test_dict = {} - runner.invoke("_to_fields", test_dict) - assert_dict(test_dict).contains_keys(["OptionsID"]) - - var options_id = test_dict.get("OptionsID", []) - assert_array(options_id).has_size(2) - assert_bool(options_id[0] is String) - assert_bool(options_id[1] is String) - - -func test_language_integration(): - # setup choice node - var runner = scene_runner("res://unit/empty_test.tscn") - var choice_node = auto_free(ChoiceNode.new()) - var mock_graph_edit = mock(MonologueGraphEdit, CALL_REAL_FUNC) - mock_graph_edit.add_child(choice_node) - runner.invoke("add_child", mock_graph_edit) - var option1 = choice_node.get_child(0) - var opt1_text = "we are number one" - option1.choice_node = choice_node - option1.option.value = (opt1_text) - option1.update_parent("", opt1_text) # simulate field update - var option2 = choice_node.get_child(1) - var opt2_text = "i am number two" - option2.choice_node = choice_node - option2.option.value = (opt2_text) - option2.update_parent("", opt2_text) # simulate field update - - # check option preview text before language switch - choice_node.reload_preview() - assert_str(choice_node.get_child(0).preview_label.text).is_equal(opt1_text) - assert_str(choice_node.get_child(1).preview_label.text).is_equal(opt2_text) - var e1 = {"Lang1": "we are number one"} - var e2 = {"Lang1": "i am number two"} - assert_dict(choice_node.get_child(0).option.to_raw_value()).is_equal(e1) - assert_dict(choice_node.get_child(1).option.to_raw_value()).is_equal(e2) - - # switch languages and reload_preview() should be empty string - do_return(lang2).on(switcher).get_current_language() - choice_node.reload_preview() - assert_str(choice_node.get_child(0).preview_label.text).is_equal("") - assert_str(choice_node.get_child(1).preview_label.text).is_equal("") - - var opt2_text2 = "they are twins" - choice_node.get_child(1).option.value = (opt2_text2) - choice_node.get_child(1).update_parent("", opt2_text2) - var d2 = e2.merged({"Lang2": opt2_text2}) - assert_dict(choice_node.get_child(1).option.to_raw_value()).is_equal(d2) - - -func test_restore_options(): - var runner = scene_runner("res://unit/empty_test.tscn") - var choice_node = auto_free(ChoiceNode.new()) - var mock_graph_edit = mock(MonologueGraphEdit, CALL_REAL_FUNC) - mock_graph_edit.add_child(choice_node) - runner.invoke("add_child", mock_graph_edit) - var option1 = choice_node.get_child(0) - var opt1_text = "first" - option1.choice_node = choice_node - option1.option.value = (opt1_text) - option1.update_parent("", opt1_text) - var option2 = choice_node.get_child(1) - var opt2_text = "second" - option2.choice_node = choice_node - option2.option.raw_data = {"LanguageOption1": "bla bla bla", "LanguageOption2": opt2_text} - option2.update_parent("", opt2_text) - - var test_option = auto_free(LanguageOption.new()) - do_return(test_option).on(switcher).add_language(lang2.language_name) - var deletion = DeleteLanguageHistory.new(mock_graph_edit, lang2.language_name, lang2.name) - choice_node.store_options(deletion.node_name, deletion.restoration, deletion.choices) - deletion.redo() - assert_object(lang2).is_queued_for_deletion() - deletion.undo() - var expected = {"Lang1": "bla bla bla", "Lang2": opt2_text} - assert_dict(choice_node.options.value[1].get("Option")).is_equal(expected) - - -func test_update_parent(): - var option_node = auto_free(OptionNode.new()) - var choice_node = auto_free(ChoiceNode.new()) - choice_node.options.value = [ - { - "$type": "NodeOption", - "ID": "upid", - "NextID": -1, - "Option": {"Lang2": "tu vis bien"}, - "EnableByDefault": false, - "OneShot": true - } - ] - - var expected = {"Lang1": "whatever", "Lang2": "como sea"} - option_node.choice_node = choice_node - option_node.option.value = expected - option_node.update_parent("before", "after") - assert_dict(choice_node.options.value[0].get("Option")).is_equal(expected) - - -func after_test(): - GlobalVariables.language_switcher = null diff --git a/unit/test_choice_node.gd.uid b/unit/test_choice_node.gd.uid deleted file mode 100644 index e047f748..00000000 --- a/unit/test_choice_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bfugnwx6yrmvp diff --git a/unit/test_fields.gd b/unit/test_fields.gd new file mode 100644 index 00000000..c4bdc650 --- /dev/null +++ b/unit/test_fields.gd @@ -0,0 +1,7 @@ +extends GdUnitTestSuite + + +func test_create_all_fields() -> void: + for field_name: String in FieldBucket._descriptors: + var field: Variant = auto_free(FieldBucket.safe_create_field(field_name)) + assert_object(field).is_not_null() diff --git a/unit/test_fields.gd.uid b/unit/test_fields.gd.uid new file mode 100644 index 00000000..ff57ca5a --- /dev/null +++ b/unit/test_fields.gd.uid @@ -0,0 +1 @@ +uid://dxqaydk3gro3c diff --git a/unit/test_graph_edit_switcher.gd b/unit/test_graph_edit_switcher.gd deleted file mode 100644 index 4a626409..00000000 --- a/unit/test_graph_edit_switcher.gd +++ /dev/null @@ -1,143 +0,0 @@ -extends GdUnitTestSuite - -var switcher: GraphEditSwitcher - - -func before_test(): - switcher = auto_free(GraphEditSwitcher.new()) - switcher.graph_edits = auto_free(Control.new()) - switcher.graph_edits.add_child(auto_free(Control.new())) - switcher.tab_bar = auto_free(TabBar.new()) - switcher.tab_bar.add_tab("+") - switcher.side_panel = mock(SidePanel) - - -func test_add_root(): - var ge = mock(MonologueGraphEdit, CALL_REAL_FUNC) - switcher.graph_edits.add_child(ge) - switcher.tab_bar.add_tab("new") - switcher.tab_bar.current_tab = 1 - assert_object(ge.get_root_node()).is_null() - switcher.add_root() - assert_object(ge.get_root_node()).is_instanceof(RootNode) - # calling add_root() again should not increase child count - var child_count = ge.get_child_count() - switcher.add_root() - assert_int(ge.get_child_count()).is_equal(child_count) - - -func test_add_tab_first(): - switcher.add_tab("kitty.json") - assert_int(switcher.tab_bar.current_tab).is_equal(0) - assert_str(switcher.tab_bar.get_tab_title(0)).is_equal("kitty.json") - - -func test_add_tab_with_previous(): - switcher.tab_bar.remove_tab(0) - switcher.tab_bar.add_tab("zero") - switcher.tab_bar.add_tab("one") - switcher.tab_bar.add_tab("+") - switcher.add_tab("two") - assert_int(switcher.tab_bar.current_tab).is_equal(2) - assert_str(switcher.tab_bar.get_tab_title(2)).is_equal("two") - - -func test_connect_side_panel(): - var ge = mock(MonologueGraphEdit, CALL_REAL_FUNC) - switcher.connect_side_panel(ge) - var select = switcher.side_panel.on_graph_node_selected - assert_bool(ge.is_connected("node_selected", select)).is_true() - var deselect = switcher.side_panel.on_graph_node_deselected - assert_bool(ge.is_connected("node_deselected", deselect)).is_true() - var save = switcher.update_save_state - assert_bool(ge.undo_redo.is_connected("version_changed", save)).is_true() - - -func test_commit_side_panel(): - var node = mock(MonologueGraphNode) - switcher.commit_side_panel(node) - verify(switcher.side_panel, 1).refocus(node) - - -func test_get_current_graph_edit(): - switcher.tab_bar.add_tab("one") - switcher.tab_bar.add_tab("two") - var one = auto_free(Control.new()) - var two = auto_free(Control.new()) - switcher.graph_edits.add_child(one) - switcher.graph_edits.add_child(two) - switcher.tab_bar.current_tab = 2 - assert_object(switcher.current).is_same(two) - - -func test_is_file_opened_false(): - assert_bool(switcher.is_file_opened("kennel/doggy.json")).is_false() - - -func test_is_file_opened_true(): - var doggy = mock(MonologueGraphEdit) - doggy.file_path = "kennel/doggy.json" - switcher.graph_edits.add_child(doggy) - assert_bool(switcher.is_file_opened("kennel/doggy.json")).is_true() - - -func test_new_graph_edit(): - switcher.new_graph_edit() - var graph_edit = switcher.graph_edits.get_child(1) - var root = graph_edit.get_nodes()[0] - # check if RootNode was created in the new graph edit - assert_object(root).is_instanceof(RootNode) - graph_edit.free() - - -func test_on_tab_close_pressed_saved(): - var ge = mock(MonologueGraphEdit) - do_return(false).on(ge).is_unsaved() - switcher.graph_edits.add_child(ge) - var spy_switcher = spy(switcher) - spy_switcher._on_tab_close_pressed(1) - verify(spy_switcher, 1)._close_tab(ge, 1) - - -func test_on_tab_close_pressed_unsaved(): - var ge = mock(MonologueGraphEdit) - do_return(true).on(ge).is_unsaved() - switcher.graph_edits.add_child(ge) - switcher._on_tab_close_pressed(1) - - var save_prompt = null - for node in switcher.get_children(): - if node is PromptWindow: - save_prompt = node - break - assert_object(save_prompt).is_not_null() - save_prompt.free() - - -func test_show_current_config(): - var ge = mock(MonologueGraphEdit) - switcher.graph_edits.add_child(ge) - switcher.tab_bar.add_tab("ge") - switcher.tab_bar.current_tab = 1 - switcher.show_current_config() - verify(ge, 1).get_root_node() - - -func test_update_save_state_saved(): - var ge = mock(MonologueGraphEdit) - do_return(false).on(ge).is_unsaved() - switcher.graph_edits.add_child(ge) - switcher.tab_bar.add_tab("wassup*") - switcher.tab_bar.current_tab = 1 - switcher.update_save_state() - assert_str(switcher.tab_bar.get_tab_title(1)).is_equal("wassup") - - -func test_update_save_state_unsaved(): - var ge = mock(MonologueGraphEdit) - do_return(true).on(ge).is_unsaved() - switcher.graph_edits.add_child(ge) - switcher.tab_bar.add_tab("wassup") - switcher.tab_bar.current_tab = 1 - switcher.update_save_state() - assert_str(switcher.tab_bar.get_tab_title(1)).is_equal("wassup*") diff --git a/unit/test_graph_edit_switcher.gd.uid b/unit/test_graph_edit_switcher.gd.uid deleted file mode 100644 index 605c3bab..00000000 --- a/unit/test_graph_edit_switcher.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c7ygpfi5gcrbq diff --git a/unit/test_graph_node_picker.gd b/unit/test_graph_node_picker.gd deleted file mode 100644 index bc7bde9a..00000000 --- a/unit/test_graph_node_picker.gd +++ /dev/null @@ -1,41 +0,0 @@ -extends GdUnitTestSuite - - -func test_enable_picker_mode(): - var from = "Omelette du Fromage" - var port = 4 - var release = Vector2(-100.05, 100.09) - var center = Vector2(123.456, -123.456) - - var runner = scene_runner("res://common/windows/graph_node_picker/graph_node_picker.tscn") - var switcher = mock(GraphEditSwitcher) - var fake_graph = mock(MonologueGraphEdit) - fake_graph.file_path = "path present!" - do_return(fake_graph).on(switcher).get_current_graph_edit() - - runner.set_property("switcher", switcher) - runner.invoke("set_visible", false) - runner.invoke("_on_enable_picker_mode", from, port, release, null, center) - - assert_str(runner.get_property("from_node")).is_equal(from) - assert_int(runner.get_property("from_port")).is_equal(port) - assert_vector(runner.get_property("center")).is_equal(center) - assert_vector(runner.get_property("release")).is_equal(release) - assert_bool(runner.get_property("visible")).is_true() - - -func test_enable_picker_mode_invalid_graph(): - var runner = scene_runner("res://common/windows/graph_node_picker/graph_node_picker.tscn") - var switcher = mock(GraphEditSwitcher) - var fake_graph = mock(MonologueGraphEdit) - do_return(fake_graph).on(switcher).get_current_graph_edit() - runner.set_property("switcher", switcher) - runner.invoke("_on_enable_picker_mode", "", 0, Vector2.ONE, Vector2.ZERO, Vector2.ZERO) - assert_bool(runner.get_property("visible")).is_false() - - -func test_close(): - var selector = auto_free(GraphNodePicker.new()) - selector.visible = true - selector.close() - assert_bool(selector.visible).is_false() diff --git a/unit/test_graph_node_picker.gd.uid b/unit/test_graph_node_picker.gd.uid deleted file mode 100644 index 5181e72a..00000000 --- a/unit/test_graph_node_picker.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cjqwjhuu1hqmt diff --git a/unit/test_language_switcher.gd b/unit/test_language_switcher.gd deleted file mode 100644 index 50bb1bab..00000000 --- a/unit/test_language_switcher.gd +++ /dev/null @@ -1,141 +0,0 @@ -extends GdUnitTestSuite - -var runner -var mock_graph_edit - - -func before_test(): - runner = scene_runner("res://common/layouts/language_switcher/language_switcher.tscn") - mock_graph_edit = mock(MonologueGraphEdit) - runner.set_property("graph_edit", mock_graph_edit) - - -func test_delete_language(): - runner.invoke("load_languages", ["One", "Two", "Three", "Four"], mock_graph_edit) - runner.set_property("selected_index", 3) - var four = runner.get_property("vbox").get_child(3) - mock_graph_edit.current_language_index = 3 - var deletion = DeleteLanguageHistory.new(mock_graph_edit, "Four", four.name) - deletion.redo() - assert_int(mock_graph_edit.current_language_index).is_equal(2) - assert_int(runner.get_property("selected_index")).is_equal(2) - - -func test_delete_language_destroys_raw_data(): - # when user sets a value in a language and then deletes that language - # the localizable should still have that raw data for undo/redo purposes - var localizable = auto_free(Localizable.new(null)) - localizable.value = "im a test" - runner.invoke("add_language", "Finnish") - runner.set_property("selected_index", 1) - localizable.value = "olen testi" - runner.get_property("vbox").get_child(1)._on_btn_delete_pressed() - var no_persist = {"English": "im a test"} - assert_dict(localizable.to_raw_value()).is_equal(no_persist) - - -func test_delete_language_undo_restores_data(): - runner.invoke("load_languages", ["Present", "Del"], mock_graph_edit) - runner.set_property("selected_index", 1) - var localizable = auto_free(Localizable.new(null)) - var data = "the data that will be deleted" - localizable.value = data - var persist = {"Del": data} - assert_dict(localizable.to_raw_value()).is_equal(persist) - - var node = runner.get_property("vbox").get_child(1) - var deletion = DeleteLanguageHistory.new(mock_graph_edit, "Del", node.name) - deletion.redo() - deletion.undo() - assert_dict(localizable.to_raw_value()).is_equal(persist) - - -func test_first_language_does_not_show_delete_button(): - runner.invoke("load_languages", ["Sindarin", "Khuzdul"], mock_graph_edit) - var first_option: LanguageOption = runner.get_property("vbox").get_child(0) - assert_str(first_option.line_edit.text).is_equal("Sindarin") - assert_bool(first_option.del_button.visible).is_false() - var second_option: LanguageOption = runner.get_property("vbox").get_child(1) - assert_str(second_option.line_edit.text).is_equal("Khuzdul") - assert_bool(second_option.del_button.visible).is_true() - - -func test_language_get_value_renamed(): - var localizable = auto_free(Localizable.new(null)) - localizable.value = "beep boop" - assert_dict(localizable.to_raw_value()).is_equal({"English": "beep boop"}) - runner.get_property("vbox").get_child(0).set_language_name("Martian") - assert_dict(localizable.to_raw_value()).is_equal({"Martian": "beep boop"}) - assert_array(runner.invoke("get_languages").keys()).is_equal(["Martian"]) - - -func test_language_get_value_nonexist_after_switch(): - runner.invoke("load_languages", ["Alien", "Broken"], mock_graph_edit) - var localizable = auto_free(Localizable.new(null)) - localizable.value = "hello" - runner.set_property("selected_index", 1) - assert_str(localizable.value).is_equal("") - assert_dict(localizable.to_raw_value()).is_equal({"Alien": "hello"}) - - -func test_language_set_value_locale_dictionary(): - # this scenario happens when .json is first loaded and the value - # is the entire raw locale dictionary rather than the actual value - runner.invoke("load_languages", [], mock_graph_edit) - var localizable = auto_free(Localizable.new(null)) - localizable.value = {"English": "test"} - assert_str(localizable.value).is_equal("test") - - -func test_language_set_value_locale_dictionary_non_default(): - runner.invoke("load_languages", ["Español", "Italiano"], mock_graph_edit) - runner.set_property("selected_index", 1) - var localizable = auto_free(Localizable.new(null)) - localizable.value = {"Español": "hola soy una prueba", "Italiano": "ciao sono un test"} - assert_str(localizable.value).is_equal("ciao sono un test") - - -func test_language_set_value_string(): - var localizable = auto_free(Localizable.new(null)) - localizable.value = "test" - assert_dict(localizable.to_raw_value()).is_equal({"English": "test"}) - - -func test_language_set_value_and_switch_locales(): - runner.invoke("load_languages", ["Deutsch", "Français", "日本語"], mock_graph_edit) - var localizable = auto_free(Localizable.new(null)) - var de = "hallo! ich bin ein test" - localizable.value = de - assert_str(localizable.value).is_equal(de) - - runner.set_property("selected_index", 1) - var fr = "bonjour je suis un test" - localizable.value = fr - assert_str(localizable.value).is_equal(fr) - - runner.set_property("selected_index", 2) - var jp = "ちゃっす、テストだよ" - localizable.value = jp - assert_str(localizable.value).is_equal(jp) - - assert_dict(localizable.to_raw_value()).is_equal({"Deutsch": de, "Français": fr, "日本語": jp}) - - -func test_load_languages(): - runner.invoke("load_languages", ["English", "Spanish"], mock_graph_edit) - var vbox: VBoxContainer = runner.get_property("vbox") - assert_int(vbox.get_child_count()).is_equal(2) - assert_str(str(vbox.get_child(0))).is_equal("English") - assert_str(str(vbox.get_child(1))).is_equal("Spanish") - - -func test_load_languages_duplicate(): - runner.invoke("load_languages", ["English", "Irish", "English", "English"], mock_graph_edit) - var vbox: VBoxContainer = runner.get_property("vbox") - assert_int(vbox.get_child_count()).is_equal(2) - assert_str(str(vbox.get_child(0))).is_equal("English") - assert_str(str(vbox.get_child(1))).is_equal("Irish") - - -func after_test(): - GlobalVariables.language_switcher = null diff --git a/unit/test_language_switcher.gd.uid b/unit/test_language_switcher.gd.uid deleted file mode 100644 index 33276e44..00000000 --- a/unit/test_language_switcher.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cy0foacg2v0fc diff --git a/unit/test_option_node.gd b/unit/test_option_node.gd deleted file mode 100644 index 370c64eb..00000000 --- a/unit/test_option_node.gd +++ /dev/null @@ -1,57 +0,0 @@ -extends GdUnitTestSuite - - -func test_to_dict(): - var runner = scene_runner("res://nodes/option_node/option_node.tscn") - runner.get_property("id").value = "12345" - runner.get_property("option").value = "heyho" - runner.get_property("one_shot").value = true - runner.get_property("next_id").value = "abcde" - - var result = runner.invoke("_to_dict") - assert_str(result.get("$type")).is_equal("NodeOption") - assert_str(result.get("ID")).is_equal("12345") - assert_str(result.get("Option")).is_equal("heyho") - assert_bool(result.get("OneShot")).is_true() - assert_bool(result.get("EnableByDefault")).is_true() - assert_str(result.get("NextID")).is_equal("abcde") - - -func test_from_dict_v2(): - var option_node = auto_free(OptionNode.new()) - option_node._from_dict( - { - "$type": "NodeOption", - "ID": "test_v2_id", - "NextID": "next_v2_id", - "Sentence": "v2 here", - "Enable": false, - "OneShot": true - } - ) - - assert_str(option_node.id.value).is_equal("test_v2_id") - assert_str(option_node.next_id.value).is_equal("next_v2_id") - assert_str(option_node.option.value).is_equal("v2 here") - assert_bool(option_node.enable_by_default.value).is_false() - assert_bool(option_node.one_shot.value).is_true() - - -func test_from_dict_v3(): - var option_node = auto_free(OptionNode.new()) - option_node._from_dict( - { - "$type": "NodeOption", - "ID": "test_v3_id", - "NextID": "next_v3_id", - "Option": "v3 here", - "EnableByDefault": false, - "OneShot": true - } - ) - - assert_str(option_node.id.value).is_equal("test_v3_id") - assert_str(option_node.next_id.value).is_equal("next_v3_id") - assert_str(option_node.option.value).is_equal("v3 here") - assert_bool(option_node.enable_by_default.value).is_false() - assert_bool(option_node.one_shot.value).is_true() diff --git a/unit/test_option_node.gd.uid b/unit/test_option_node.gd.uid deleted file mode 100644 index cf16fdad..00000000 --- a/unit/test_option_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cd4wj7bb4x2tb diff --git a/unit/test_path.gd b/unit/test_path.gd deleted file mode 100644 index 45a53128..00000000 --- a/unit/test_path.gd +++ /dev/null @@ -1,57 +0,0 @@ -extends GdUnitTestSuite - -var linux_file = "/home/mrsharpener/Pen/1_n.json" -var linux_params = [ - ["/home/mrsharpener/Pen/w/b/20/ac.mp3", "w/b/20/ac.mp3"], - ["/home/mrsharpener/Pen/1.txt", "1.txt"], - ["/home/missyeraser/K/y.o.u", "../../missyeraser/K/y.o.u"], - ["/opt/x/1/bro", "/opt/x/1/bro"], -] - -var windows_file = "C:\\Users\\MrSharpener\\Pen\\1_n.json" -var windows_params = [ - ["C:\\Users\\MrSharpener\\Pen\\w\\b\\20\\ac.mp3", "w/b/20/ac.mp3"], - ["C:\\Users\\MrSharpener\\Pen\\1.txt", "1.txt"], - ["C:\\Users\\MissyEraser\\K\\y.o.u", "../../MissyEraser/K/y.o.u"], - ["E:\\10. __World\\g1d a", "E:\\10. __World\\g1d a"], -] - - -func reverse_parameters(params: Array): - var copy = params.duplicate() - copy.reverse() - return copy.map(func(s: String): return s.replace("\\", "/")) - - -@warning_ignore("unused_parameter") - - -func test_absolute_to_relative_linux(path: String, result: String, test_parameters = linux_params): - assert_str(Path.absolute_to_relative(path, linux_file)).is_equal(result) - - -@warning_ignore("unused_parameter") - - -func test_absolute_to_relative_windows( - path: String, result: String, test_parameters = windows_params -): - assert_str(Path.absolute_to_relative(path, windows_file)).is_equal(result) - - -@warning_ignore("unused_parameter") - - -func test_relative_to_absolute_linux( - path: String, result: String, test_parameters = linux_params.map(reverse_parameters) -): - assert_str(Path.relative_to_absolute(path, linux_file)).is_equal(result) - - -@warning_ignore("unused_parameter") - - -func test_relative_to_absolute_windows( - path: String, result: String, test_parameters = windows_params.map(reverse_parameters) -): - assert_str(Path.relative_to_absolute(path, windows_file)).is_equal(result) diff --git a/unit/test_path.gd.uid b/unit/test_path.gd.uid deleted file mode 100644 index d8fc44e8..00000000 --- a/unit/test_path.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cwet7jy1gioy diff --git a/unit/test_recent_file_container.gd b/unit/test_recent_file_container.gd deleted file mode 100644 index 41fbb83a..00000000 --- a/unit/test_recent_file_container.gd +++ /dev/null @@ -1,73 +0,0 @@ -extends GdUnitTestSuite - -var container: RecentFilesContainer -var directory - - -func before_test(): - container = auto_free(RecentFilesContainer.new()) - container.button_container = auto_free(Control.new()) - container.button_scene = preload("res://common/windows/welcome_window/recent_file_button.tscn") - container.save_path = "user://test_history.save" - directory = DirAccess.open(container.save_path.get_base_dir()) - directory.remove(container.save_path) - - -func test_add_from_empty(): - container.add("whatever.json") - var text = FileAccess.get_file_as_string(container.save_path) - assert_str(text).is_equal('["whatever.json"]') - - -func test_add_override(): - var file = FileAccess.open(container.save_path, FileAccess.WRITE) - file.store_string('["one.json", "two.json"]') - file.close() - container.recent_filepaths = ["three.json", "four.json"] - container.add("five.json") - var text = FileAccess.get_file_as_string(container.save_path) - var json = JSON.parse_string(text) - # test the order of the array, "five.json" should be first! - assert_array(json).is_equal(["five.json", "three.json", "four.json"]) - - -func test_create_file(): - assert_bool(FileAccess.file_exists(container.save_path)).is_false() - container.create_file() - assert_bool(FileAccess.file_exists(container.save_path)).is_true() - - -func test_load_file_empty(): - container.load_file() - assert_int(container.button_container.get_child_count()).is_equal(0) - - -func test_load_file_exclude_not_exist(): - var file = FileAccess.open(container.save_path, FileAccess.WRITE) - file.store_string( - '["res://DOES_NOT_EXIST.json", ' + '"res://examples/mr_sharpener/ending_02.json"]' - ) - file.close() - container.load_file() - assert_int(container.button_container.get_child_count()).is_equal(1) - for button in container.button_container.get_children(): - button.free() - - -func test_load_file_existing(): - var file = FileAccess.open(container.save_path, FileAccess.WRITE) - file.store_string( - ( - '["res://examples/mr_sharpener/intro.json", ' - + '"res://examples/mr_sharpener/ending_01.json"]' - ) - ) - file.close() - container.load_file() - assert_int(container.button_container.get_child_count()).is_equal(2) - for button in container.button_container.get_children(): - button.free() - - -func after_test(): - directory.remove(container.save_path) diff --git a/unit/test_recent_file_container.gd.uid b/unit/test_recent_file_container.gd.uid deleted file mode 100644 index ce5e4758..00000000 --- a/unit/test_recent_file_container.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dqcdri25ak5qw diff --git a/unit/test_sentence_node.gd b/unit/test_sentence_node.gd deleted file mode 100644 index 5df700ba..00000000 --- a/unit/test_sentence_node.gd +++ /dev/null @@ -1,26 +0,0 @@ -extends GdUnitTestSuite - - -func test_backwards_compatibility(): - var node = auto_free(SentenceNode.new()) - var ge = mock(MonologueGraphEdit, CALL_REAL_FUNC) - ge.add_child(node) - node._preview = mock(Label) - - node._from_dict( - { - "$type": "NodeSentence", - "ID": "old-test-sentence", - "NextID": "old-next-sentence", - "Sentence": "monster", - "SpeakerID": 3, - "DisplaySpeakerName": "teela", - "DisplayVariant": "kagha", - "VoicelinePath": "../Voice/to_me.mp3", - "EditorPosition": {"x": 2180, "y": 880} - } - ) - - assert_int(node.speaker.value).is_equal(3) - assert_str(node.display_name.value).is_equal("teela") - assert_str(node.voiceline.value).is_equal("../Voice/to_me.mp3") diff --git a/unit/test_sentence_node.gd.uid b/unit/test_sentence_node.gd.uid deleted file mode 100644 index c6f20cc6..00000000 --- a/unit/test_sentence_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b1piujqcmtgkm