diff --git a/addons/Todo_Manager/doc/images/Instruct1.png.import b/addons/Todo_Manager/doc/images/Instruct1.png.import
index 98608c1a..ba6557dd 100644
--- a/addons/Todo_Manager/doc/images/Instruct1.png.import
+++ b/addons/Todo_Manager/doc/images/Instruct1.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/Instruct1.png-698c6faa3ef3ac4960807faa3e028bd
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/Todo_Manager/doc/images/Instruct2.png.import b/addons/Todo_Manager/doc/images/Instruct2.png.import
index 18b26207..01b8f923 100644
--- a/addons/Todo_Manager/doc/images/Instruct2.png.import
+++ b/addons/Todo_Manager/doc/images/Instruct2.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/Instruct2.png-4e8664e49d00a397bbd539f7dee976f
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/Todo_Manager/doc/images/Instruct3.png.import b/addons/Todo_Manager/doc/images/Instruct3.png.import
index e1ee1989..354d866e 100644
--- a/addons/Todo_Manager/doc/images/Instruct3.png.import
+++ b/addons/Todo_Manager/doc/images/Instruct3.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/Instruct3.png-3cc62ed99bf29d90b803cb8eb40881e
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/Todo_Manager/doc/images/Instruct4.png.import b/addons/Todo_Manager/doc/images/Instruct4.png.import
index 309098ca..2270274b 100644
--- a/addons/Todo_Manager/doc/images/Instruct4.png.import
+++ b/addons/Todo_Manager/doc/images/Instruct4.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/Instruct4.png-bf5aa1cffc066175cecf9281b077480
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/Todo_Manager/doc/images/Instruct5.png.import b/addons/Todo_Manager/doc/images/Instruct5.png.import
index 99b31909..beaa777f 100644
--- a/addons/Todo_Manager/doc/images/Instruct5.png.import
+++ b/addons/Todo_Manager/doc/images/Instruct5.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/Instruct5.png-001538ed8b5682dcf232de08035aab3
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/Todo_Manager/doc/images/TODO_Manager_Logo.png.import b/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import
index 75d1a2da..9da3bd92 100644
--- a/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import
+++ b/addons/Todo_Manager/doc/images/TODO_Manager_Logo.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/TODO_Manager_Logo.png-e07d7ec75201c66b732ef87
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/Todo_Manager/doc/images/example1.png.import b/addons/Todo_Manager/doc/images/example1.png.import
index a88dd0c9..c792ce01 100644
--- a/addons/Todo_Manager/doc/images/example1.png.import
+++ b/addons/Todo_Manager/doc/images/example1.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/example1.png-6386c332ca46e1e62ea061b956a901cd
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/Todo_Manager/doc/images/example2.png.import b/addons/Todo_Manager/doc/images/example2.png.import
index bb9a7bc1..05753d63 100644
--- a/addons/Todo_Manager/doc/images/example2.png.import
+++ b/addons/Todo_Manager/doc/images/example2.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/example2.png-2e3a8f9cd1e178daf22b83dc0513f37a
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/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/dockable_container/LICENSE b/addons/dockable_container/LICENSE
new file mode 100644
index 00000000..0e259d42
--- /dev/null
+++ b/addons/dockable_container/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/addons/dockable_container/dockable_container.gd b/addons/dockable_container/dockable_container.gd
new file mode 100644
index 00000000..cbb95c8c
--- /dev/null
+++ b/addons/dockable_container/dockable_container.gd
@@ -0,0 +1,524 @@
+@tool
+class_name DockableContainer
+extends Container
+
+const SplitHandle := preload("split_handle.gd")
+const DockablePanel := preload("dockable_panel.gd")
+const DragNDropPanel := preload("drag_n_drop_panel.gd")
+
+@export var tab_alignment := TabBar.ALIGNMENT_CENTER:
+ get:
+ return _tab_align
+ set(value):
+ _tab_align = value
+ for i in range(1, _panel_container.get_child_count()):
+ var panel := _panel_container.get_child(i) as DockablePanel
+ panel.tab_alignment = value
+@export var use_hidden_tabs_for_min_size := false:
+ get:
+ return _use_hidden_tabs_for_min_size
+ set(value):
+ _use_hidden_tabs_for_min_size = value
+ for i in range(1, _panel_container.get_child_count()):
+ var panel := _panel_container.get_child(i) as DockablePanel
+ panel.use_hidden_tabs_for_min_size = value
+@export var tabs_visible := true:
+ get:
+ return _tabs_visible
+ set(value):
+ _tabs_visible = value
+ for i in range(1, _panel_container.get_child_count()):
+ var panel := _panel_container.get_child(i) as DockablePanel
+ panel.show_tabs = _tabs_visible
+## If [code]true[/code] and a panel only has one tab, it keeps that tab hidden even if
+## [member tabs_visible] is [code]true[/code].
+## Only takes effect is [member tabs_visible] is [code]true[/code].
+@export var hide_single_tab := false:
+ get:
+ return _hide_single_tab
+ set(value):
+ _hide_single_tab = value
+ for i in range(1, _panel_container.get_child_count()):
+ var panel := _panel_container.get_child(i) as DockablePanel
+ panel.hide_single_tab = _hide_single_tab
+ queue_sort()
+@export var rearrange_group := 0
+@export var layout := DockableLayout.new():
+ get:
+ return _layout
+ set(value):
+ set_layout(value)
+## If `clone_layout_on_ready` is true, `layout` will be cloned checked `_ready`.
+## This is useful for leaving layout Resources untouched in case you want to
+## restore layout to its default later.
+@export var clone_layout_on_ready := true
+
+var _layout := DockableLayout.new()
+var _panel_container := Container.new()
+var _windows_container := Container.new()
+var _split_container := Container.new()
+var _drag_n_drop_panel := DragNDropPanel.new()
+var _drag_panel: DockablePanel
+var _tab_align := TabBar.ALIGNMENT_CENTER
+var _tabs_visible := true
+var _use_hidden_tabs_for_min_size := false
+var _hide_single_tab := false
+var _current_panel_index := 0
+var _current_split_index := 0
+var _children_names := {}
+var _layout_dirty := false
+
+
+func _init() -> void:
+ child_entered_tree.connect(_child_entered_tree)
+ child_exiting_tree.connect(_child_exiting_tree)
+
+
+func _ready() -> void:
+ set_process_input(false)
+ _panel_container.name = "_panel_container"
+ add_child(_panel_container)
+ move_child(_panel_container, 0)
+ _split_container.name = "_split_container"
+ _split_container.mouse_filter = MOUSE_FILTER_PASS
+ _panel_container.add_child(_split_container)
+ _windows_container.name = "_windows_container"
+ get_parent().call_deferred("add_child", _windows_container)
+
+ _drag_n_drop_panel.name = "_drag_n_drop_panel"
+ _drag_n_drop_panel.mouse_filter = MOUSE_FILTER_PASS
+ _drag_n_drop_panel.visible = false
+ add_child(_drag_n_drop_panel)
+
+ if not _layout:
+ set_layout(null)
+ elif clone_layout_on_ready and not Engine.is_editor_hint():
+ set_layout(_layout.clone())
+
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_SORT_CHILDREN:
+ _resort()
+ elif (
+ what == NOTIFICATION_DRAG_BEGIN
+ and _can_handle_drag_data(get_viewport().gui_get_drag_data())
+ ):
+ _drag_n_drop_panel.set_enabled(true, not _layout.root.is_empty())
+ set_process_input(true)
+ elif what == NOTIFICATION_DRAG_END:
+ _drag_n_drop_panel.set_enabled(false)
+ set_process_input(false)
+
+
+func _input(event: InputEvent) -> void:
+ assert(get_viewport().gui_is_dragging(), "FIXME: should only be called when dragging")
+ if event is InputEventMouseMotion:
+ var local_position := get_local_mouse_position()
+ var panel: DockablePanel
+ for i in range(1, _panel_container.get_child_count()):
+ var p := _panel_container.get_child(i) as DockablePanel
+ if p.get_rect().has_point(local_position):
+ panel = p
+ break
+ _drag_panel = panel
+ if not panel:
+ return
+
+ var current_tab: DockableReferenceControl = panel.get_current_tab_control()
+ var tab_name: String = current_tab.reference_to.name
+ if tab_name.begins_with("_"):
+ return
+
+ fit_child_in_rect(_drag_n_drop_panel, panel.get_child_rect())
+
+
+func _child_entered_tree(node: Node) -> void:
+ if node == _panel_container or node == _drag_n_drop_panel:
+ return
+ _drag_n_drop_panel.move_to_front()
+ _track_and_add_node(node)
+
+
+func _child_exiting_tree(node: Node) -> void:
+ if node == _panel_container or node == _drag_n_drop_panel:
+ return
+ _untrack_node(node)
+
+
+func _can_drop_data(_position: Vector2, data) -> bool:
+ return _can_handle_drag_data(data)
+
+
+func _drop_data(_position: Vector2, data) -> void:
+ var from_node := get_node(data.from_path)
+ if from_node is TabBar:
+ from_node = from_node.get_parent()
+ if from_node == _drag_panel and _drag_panel.get_child_count() == 1:
+ return
+ var tab_index = data.tabc_element if data.has("tabc_element") else data.tab_index
+ var moved_tab = from_node.get_tab_control(tab_index)
+ if moved_tab is DockableReferenceControl:
+ moved_tab = moved_tab.reference_to
+ if not _is_managed_node(moved_tab):
+ moved_tab.get_parent().remove_child(moved_tab)
+ add_child(moved_tab)
+
+ if _drag_panel != null:
+ var margin := _drag_n_drop_panel.get_hover_margin()
+ _layout.split_leaf_with_node(_drag_panel.leaf, moved_tab, margin)
+
+ _layout_dirty = true
+ queue_sort()
+
+
+func _add_floating_options(tab_container: DockablePanel) -> void:
+ var options := PopupMenu.new()
+ options.add_item("Make Floating")
+ options.id_pressed.connect(_toggle_floating.bind(tab_container))
+ options.size.y = 0
+ _windows_container.add_child(options)
+ tab_container.set_popup(options)
+
+
+## Required when converting a window back to panel.
+func _refresh_tabs_visible() -> void:
+ if tabs_visible:
+ tabs_visible = false
+ await get_tree().process_frame
+ await get_tree().process_frame
+ tabs_visible = true
+
+
+func _toggle_floating(_id: int, tab_container: DockablePanel) -> void:
+ var node_name := tab_container.get_tab_title(tab_container.current_tab)
+ var node := get_node(node_name)
+ if is_instance_valid(node):
+ var tab_position := maxi(tab_container.leaf.find_child(node), 0)
+ _convert_to_window(node, {"tab_position": tab_position, "tab_container": tab_container})
+ else:
+ print("Node ", node_name, " not found!")
+
+
+## Converts a panel to floating window.
+func _convert_to_window(content: Control, previous_data := {}) -> void:
+ var old_owner := content.owner
+ var data := {}
+ if content.name in layout.windows:
+ data = layout.windows[content.name]
+ var window := FloatingWindow.new(content, data)
+ _windows_container.add_child(window)
+ window.show()
+ _refresh_tabs_visible()
+ window.close_requested.connect(_convert_to_panel.bind(window, old_owner, previous_data))
+ window.data_changed.connect(layout.save_window_properties)
+
+
+## Converts a floating window into a panel.
+func _convert_to_panel(window: FloatingWindow, old_owner: Node, previous_data := {}) -> void:
+ var content := window.window_content
+ window.remove_child(content)
+ window.destroy()
+ add_child(content)
+ content.owner = old_owner
+ if previous_data.has("tab_container") and is_instance_valid(previous_data["tab_container"]):
+ var tab_position := previous_data.get("tab_position", 0) as int
+ previous_data["tab_container"].leaf.insert_node(tab_position, content)
+ _refresh_tabs_visible()
+
+
+func set_control_as_current_tab(control: Control) -> void:
+ assert(
+ control.get_parent_control() == self,
+ "Trying to focus a control not managed by this container"
+ )
+ if is_control_hidden(control):
+ push_warning("Trying to focus a hidden control")
+ return
+ var leaf := _layout.get_leaf_for_node(control)
+ if not leaf:
+ return
+ var position_in_leaf := leaf.find_child(control)
+ if position_in_leaf < 0:
+ return
+ var panel: DockablePanel
+ for i in range(1, _panel_container.get_child_count()):
+ var p := _panel_container.get_child(i) as DockablePanel
+ if p.leaf == leaf:
+ panel = p
+ break
+ if not panel:
+ return
+ panel.current_tab = clampi(position_in_leaf, 0, panel.get_tab_count() - 1)
+
+
+func set_layout(value: DockableLayout) -> void:
+ if value == null:
+ value = DockableLayout.new()
+ if value == _layout:
+ return
+ if _layout and _layout.changed.is_connected(queue_sort):
+ _layout.changed.disconnect(queue_sort)
+ _layout = value
+ _layout.changed.connect(queue_sort)
+ for window in _windows_container.get_children():
+ if not window.name in _layout.windows and window is FloatingWindow:
+ window.prevent_data_erasure = true # We don't want to delete data.
+ window.close_requested.emit() # Removes the window.
+ continue
+ for window: String in _layout.windows.keys():
+ var panel := find_child(window, false)
+ # Only those windows get created which were not previously created.
+ if panel:
+ _convert_to_window(panel)
+ _layout_dirty = true
+ queue_sort()
+
+
+func set_use_hidden_tabs_for_min_size(value: bool) -> void:
+ _use_hidden_tabs_for_min_size = value
+ for i in range(1, _panel_container.get_child_count()):
+ var panel := _panel_container.get_child(i) as DockablePanel
+ panel.use_hidden_tabs_for_min_size = value
+
+
+func get_use_hidden_tabs_for_min_size() -> bool:
+ return _use_hidden_tabs_for_min_size
+
+
+func set_control_hidden(child: Control, is_hidden: bool) -> void:
+ _layout.set_node_hidden(child, is_hidden)
+
+
+func is_control_hidden(child: Control) -> bool:
+ return _layout.is_node_hidden(child)
+
+
+func get_tabs() -> Array[Control]:
+ var tabs: Array[Control] = []
+ for i in get_child_count():
+ var child := get_child(i)
+ if _is_managed_node(child):
+ tabs.append(child)
+ return tabs
+
+
+func get_tab_count() -> int:
+ var count := 0
+ for i in get_child_count():
+ var child := get_child(i)
+ if _is_managed_node(child):
+ count += 1
+ return count
+
+
+func _can_handle_drag_data(data) -> bool:
+ if data is Dictionary and data.get("type") in ["tab_container_tab", "tabc_element"]:
+ var tabc := get_node_or_null(data.get("from_path"))
+ return (
+ tabc
+ and tabc.has_method("get_tabs_rearrange_group")
+ and tabc.get_tabs_rearrange_group() == rearrange_group
+ )
+ return false
+
+
+func _is_managed_node(node: Node) -> bool:
+ return (
+ node.get_parent() == self
+ and node != _panel_container
+ and node != _drag_n_drop_panel
+ and node is Control
+ and not node.top_level
+ )
+
+
+func _update_layout_with_children() -> void:
+ var names := PackedStringArray()
+ _children_names.clear()
+ for i in range(1, get_child_count() - 1):
+ var c := get_child(i)
+ if _track_node(c):
+ names.append(c.name)
+ _layout.update_nodes(names)
+ _layout_dirty = false
+
+
+func _track_node(node: Node) -> bool:
+ if not _is_managed_node(node):
+ return false
+ _children_names[node] = node.name
+ _children_names[node.name] = node
+ if not node.renamed.is_connected(_on_child_renamed):
+ node.renamed.connect(_on_child_renamed.bind(node))
+ if not node.tree_exiting.is_connected(_untrack_node):
+ node.tree_exiting.connect(_untrack_node.bind(node))
+ return true
+
+
+func _track_and_add_node(node: Node) -> void:
+ var tracked_name = _children_names.get(node)
+ if not _track_node(node):
+ return
+ if tracked_name and tracked_name != node.name:
+ _layout.rename_node(tracked_name, node.name)
+ _layout_dirty = true
+
+
+func _untrack_node(node: Node) -> void:
+ _children_names.erase(node)
+ _children_names.erase(node.name)
+ if node.renamed.is_connected(_on_child_renamed):
+ node.renamed.disconnect(_on_child_renamed)
+ if node.tree_exiting.is_connected(_untrack_node):
+ node.tree_exiting.disconnect(_untrack_node)
+ _layout_dirty = true
+
+
+func _resort() -> void:
+ assert(_panel_container, "FIXME: resorting without _panel_container")
+ if _panel_container.get_index() != 0:
+ move_child(_panel_container, 0)
+ if _drag_n_drop_panel.get_index() < get_child_count() - 1:
+ _drag_n_drop_panel.move_to_front()
+
+ if _layout_dirty:
+ _update_layout_with_children()
+
+ var rect := Rect2(Vector2.ZERO, size)
+ fit_child_in_rect(_panel_container, rect)
+ _panel_container.fit_child_in_rect(_split_container, rect)
+
+ _current_panel_index = 1
+ _current_split_index = 0
+
+ var children_list := []
+ _calculate_panel_and_split_list(children_list, _layout.root)
+ _fit_panel_and_split_list_to_rect(children_list, rect)
+
+ _untrack_children_after(_panel_container, _current_panel_index)
+ _untrack_children_after(_split_container, _current_split_index)
+
+
+## Calculate DockablePanel and SplitHandle minimum sizes, skipping empty
+## branches.
+##
+## Returns a DockablePanel checked non-empty leaves, a SplitHandle checked non-empty
+## splits, `null` if the whole branch is empty and no space should be used.
+##
+## `result` will be filled with the non-empty nodes in this post-order tree
+## traversal.
+func _calculate_panel_and_split_list(result: Array, layout_node: DockableLayoutNode):
+ if layout_node is DockableLayoutPanel:
+ var nodes: Array[Control] = []
+ for n in layout_node.names:
+ var node: Control = _children_names.get(n)
+ if node:
+ assert(node is Control, "FIXME: node is not a control %s" % node)
+ assert(
+ node.get_parent_control() == self,
+ "FIXME: node is not child of container %s" % node
+ )
+ if is_control_hidden(node):
+ node.visible = false
+ else:
+ nodes.append(node)
+ if nodes.is_empty():
+ return null
+ else:
+ var panel := _get_panel(_current_panel_index)
+ _current_panel_index += 1
+ panel.track_nodes(nodes, layout_node)
+ result.append(panel)
+ return panel
+ elif layout_node is DockableLayoutSplit:
+ # by processing `second` before `first`, traversing `result` from back
+ # to front yields a nice pre-order tree traversal
+ var second_result = _calculate_panel_and_split_list(result, layout_node.second)
+ var first_result = _calculate_panel_and_split_list(result, layout_node.first)
+ if first_result and second_result:
+ var split := _get_split(_current_split_index)
+ _current_split_index += 1
+ split.layout_split = layout_node
+ split.first_minimum_size = first_result.get_layout_minimum_size()
+ split.second_minimum_size = second_result.get_layout_minimum_size()
+ result.append(split)
+ return split
+ elif first_result:
+ return first_result
+ else: # NOTE: this returns null if `second_result` is null
+ return second_result
+ else:
+ push_warning("FIXME: invalid Resource, should be branch or leaf, found %s" % layout_node)
+
+
+## Traverse list from back to front fitting controls where they belong.
+##
+## Be sure to call this with the result from `_calculate_split_minimum_sizes`.
+func _fit_panel_and_split_list_to_rect(panel_and_split_list: Array, rect: Rect2) -> void:
+ var control = panel_and_split_list.pop_back()
+ if control is DockablePanel:
+ _panel_container.fit_child_in_rect(control, rect)
+ elif control is SplitHandle:
+ var split_rects = control.get_split_rects(rect)
+ _split_container.fit_child_in_rect(control, split_rects["self"])
+ _fit_panel_and_split_list_to_rect(panel_and_split_list, split_rects["first"])
+ _fit_panel_and_split_list_to_rect(panel_and_split_list, split_rects["second"])
+
+
+## Get the idx'th DockablePanel, reusing an instanced one if possible
+func _get_panel(idx: int) -> DockablePanel:
+ assert(_panel_container, "FIXME: creating panel without _panel_container")
+ if idx < _panel_container.get_child_count():
+ return _panel_container.get_child(idx)
+ var panel := DockablePanel.new()
+ panel.tab_alignment = _tab_align
+ panel.show_tabs = _tabs_visible
+ panel.hide_single_tab = _hide_single_tab
+ panel.use_hidden_tabs_for_min_size = _use_hidden_tabs_for_min_size
+ panel.set_tabs_rearrange_group(maxi(0, rearrange_group))
+ _add_floating_options(panel)
+ _panel_container.add_child(panel)
+ panel.tab_layout_changed.connect(_on_panel_tab_layout_changed.bind(panel))
+ return panel
+
+
+## Get the idx'th SplitHandle, reusing an instanced one if possible
+func _get_split(idx: int) -> SplitHandle:
+ assert(_split_container, "FIXME: creating split without _split_container")
+ if idx < _split_container.get_child_count():
+ return _split_container.get_child(idx)
+ var split := SplitHandle.new()
+ _split_container.add_child(split)
+ return split
+
+
+## Helper for removing and freeing all remaining children from node
+func _untrack_children_after(node: Control, idx: int) -> void:
+ for i in range(idx, node.get_child_count()):
+ var child := node.get_child(idx)
+ node.remove_child(child)
+ child.queue_free()
+
+
+## Handler for `DockablePanel.tab_layout_changed`, update its DockableLayoutPanel
+func _on_panel_tab_layout_changed(tab: int, panel: DockablePanel) -> void:
+ _layout_dirty = true
+ var control := panel.get_tab_control(tab)
+ if control is DockableReferenceControl:
+ control = control.reference_to
+ if not _is_managed_node(control):
+ control.get_parent().remove_child(control)
+ add_child(control)
+ _layout.move_node_to_leaf(control, panel.leaf, tab)
+ queue_sort()
+
+
+## Handler for `Node.renamed` signal, updates tracked name for node
+func _on_child_renamed(child: Node) -> void:
+ var old_name: String = _children_names.get(child)
+ if old_name == str(child.name):
+ return
+ _children_names.erase(old_name)
+ _children_names[child] = child.name
+ _children_names[child.name] = child
+ _layout.rename_node(old_name, child.name)
diff --git a/addons/dockable_container/dockable_container.gd.uid b/addons/dockable_container/dockable_container.gd.uid
new file mode 100644
index 00000000..d3304fc5
--- /dev/null
+++ b/addons/dockable_container/dockable_container.gd.uid
@@ -0,0 +1 @@
+uid://k2o7qui1lr6l
diff --git a/addons/dockable_container/dockable_panel.gd b/addons/dockable_container/dockable_panel.gd
new file mode 100644
index 00000000..bd064113
--- /dev/null
+++ b/addons/dockable_container/dockable_panel.gd
@@ -0,0 +1,168 @@
+@tool
+extends TabContainer
+
+signal tab_layout_changed(tab)
+
+var leaf: DockableLayoutPanel:
+ get:
+ return get_leaf()
+ set(value):
+ set_leaf(value)
+var show_tabs := true:
+ get:
+ return _show_tabs
+ set(value):
+ _show_tabs = value
+ _handle_tab_visibility()
+var hide_single_tab := false:
+ get:
+ return _hide_single_tab
+ set(value):
+ _hide_single_tab = value
+ _handle_tab_visibility()
+
+var _leaf: DockableLayoutPanel
+var _show_tabs := true
+var _hide_single_tab := false
+var _have_focus := false
+
+
+func _ready() -> void:
+ drag_to_rearrange_enabled = true
+ theme_type_variation = "EditorSection"
+
+ _update_style()
+
+
+func _enter_tree() -> void:
+ active_tab_rearranged.connect(_on_tab_changed)
+ tab_selected.connect(_on_tab_selected)
+ tab_changed.connect(_on_tab_changed)
+
+ _update_style()
+
+
+func _exit_tree() -> void:
+ active_tab_rearranged.disconnect(_on_tab_changed)
+ tab_selected.disconnect(_on_tab_selected)
+ tab_changed.disconnect(_on_tab_changed)
+ if is_instance_valid(get_popup()):
+ get_popup().queue_free()
+
+ _update_style()
+
+
+func track_nodes(nodes: Array[Control], new_leaf: DockableLayoutPanel) -> void:
+ _leaf = null # avoid using previous leaf in tab_changed signals
+ var min_size := mini(nodes.size(), get_child_count())
+ # remove spare children
+ for i in range(min_size, get_child_count()):
+ var child := get_child(min_size) as DockableReferenceControl
+ child.reference_to = null
+ remove_child(child)
+ child.queue_free()
+ # add missing children
+ for i in range(min_size, nodes.size()):
+ var ref_control := DockableReferenceControl.new()
+ add_child(ref_control)
+ assert(nodes.size() == get_child_count(), "FIXME")
+ # setup children
+ for i in nodes.size():
+ var ref_control := get_child(i) as DockableReferenceControl
+ ref_control.reference_to = nodes[i]
+ set_tab_title(i, nodes[i].name)
+ set_leaf(new_leaf)
+ _handle_tab_visibility()
+
+
+func get_child_rect() -> Rect2:
+ var control := get_current_tab_control()
+ return Rect2(position + control.position, control.size)
+
+
+func set_leaf(value: DockableLayoutPanel) -> void:
+ if get_tab_count() > 0 and value:
+ current_tab = clampi(value.current_tab, 0, get_tab_count() - 1)
+ _leaf = value
+
+ _update_style()
+
+
+func get_leaf() -> DockableLayoutPanel:
+ return _leaf
+
+
+func get_layout_minimum_size() -> Vector2:
+ hide()
+ show()
+ return get_combined_minimum_size()
+
+
+func _on_tab_selected(tab: int) -> void:
+ if _leaf:
+ _leaf.current_tab = tab
+
+
+func _on_tab_changed(tab: int) -> void:
+ if not _leaf:
+ return
+ var control := get_tab_control(tab)
+ if not control:
+ return
+ var tab_name := control.name
+ var name_index_in_leaf := _leaf.find_name(tab_name)
+ if name_index_in_leaf != tab: # NOTE: this handles added tabs (index == -1)
+ tab_layout_changed.emit(tab)
+
+
+func _handle_tab_visibility() -> void:
+ if _hide_single_tab and get_tab_count() == 1:
+ tabs_visible = false
+ else:
+ tabs_visible = _show_tabs
+
+ if is_node_ready():
+ _update_style()
+
+
+func _update_style() -> void:
+ var mouse_hovering: bool = is_mouse_on_section()
+
+ if leaf:
+ for name in leaf.get_names():
+ if not name.begins_with("_"):
+ continue
+ tabs_visible = false
+
+ var panel_style_name: String = "panel"
+ var tabbar_style_name: String = "tabbar_background"
+ _have_focus = mouse_hovering
+ if mouse_hovering:
+ panel_style_name += "_focus"
+ tabbar_style_name += "_focus"
+ else:
+ panel_style_name += "_unfocus"
+ tabbar_style_name += "_unfocus"
+
+ if tabs_visible:
+ panel_style_name = "tab_" + panel_style_name
+
+ add_theme_stylebox_override("panel", get_theme_stylebox(panel_style_name, "EditorSection"))
+ add_theme_stylebox_override(
+ "tabbar_background", get_theme_stylebox(tabbar_style_name, "EditorSection")
+ )
+
+
+func _input(event: InputEvent) -> void:
+ if event is InputEventMouseButton and event.is_pressed():
+ _update_style()
+
+
+func is_mouse_on_section() -> bool:
+ var local_mouse_pos: Vector2 = get_global_mouse_position() - global_position
+ return not (
+ local_mouse_pos.x < 0
+ or local_mouse_pos.y < 0
+ or local_mouse_pos.x > size.x
+ or local_mouse_pos.y > size.y
+ )
diff --git a/addons/dockable_container/dockable_panel.gd.uid b/addons/dockable_container/dockable_panel.gd.uid
new file mode 100644
index 00000000..aa5a5aff
--- /dev/null
+++ b/addons/dockable_container/dockable_panel.gd.uid
@@ -0,0 +1 @@
+uid://dca5brtlainkk
diff --git a/addons/dockable_container/dockable_panel_reference_control.gd b/addons/dockable_container/dockable_panel_reference_control.gd
new file mode 100644
index 00000000..06dc11b9
--- /dev/null
+++ b/addons/dockable_container/dockable_panel_reference_control.gd
@@ -0,0 +1,49 @@
+@tool
+class_name DockableReferenceControl
+extends Container
+## Control that mimics its own visibility and rect into another Control.
+
+var reference_to: Control:
+ get:
+ return _reference_to
+ set(control):
+ if _reference_to != control:
+ if is_instance_valid(_reference_to):
+ _reference_to.renamed.disconnect(_on_reference_to_renamed)
+ _reference_to.minimum_size_changed.disconnect(update_minimum_size)
+ _reference_to = control
+
+ minimum_size_changed.emit()
+ if not is_instance_valid(_reference_to):
+ return
+ _reference_to.renamed.connect(_on_reference_to_renamed)
+ _reference_to.minimum_size_changed.connect(update_minimum_size)
+ _reference_to.visible = visible
+ _reposition_reference()
+
+var _reference_to: Control = null
+
+
+func _ready() -> void:
+ mouse_filter = MOUSE_FILTER_IGNORE
+ set_notify_transform(true)
+
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_VISIBILITY_CHANGED and _reference_to:
+ _reference_to.visible = visible
+ elif what == NOTIFICATION_TRANSFORM_CHANGED and _reference_to:
+ _reposition_reference()
+
+
+func _get_minimum_size() -> Vector2:
+ return _reference_to.get_combined_minimum_size() if _reference_to else Vector2.ZERO
+
+
+func _reposition_reference() -> void:
+ _reference_to.global_position = global_position
+ _reference_to.size = size
+
+
+func _on_reference_to_renamed() -> void:
+ name = _reference_to.name
diff --git a/addons/dockable_container/dockable_panel_reference_control.gd.uid b/addons/dockable_container/dockable_panel_reference_control.gd.uid
new file mode 100644
index 00000000..8ff02d12
--- /dev/null
+++ b/addons/dockable_container/dockable_panel_reference_control.gd.uid
@@ -0,0 +1 @@
+uid://8fcumdc7ut8
diff --git a/addons/dockable_container/drag_n_drop_panel.gd b/addons/dockable_container/drag_n_drop_panel.gd
new file mode 100644
index 00000000..7e5d7714
--- /dev/null
+++ b/addons/dockable_container/drag_n_drop_panel.gd
@@ -0,0 +1,82 @@
+@tool
+extends Control
+
+enum { MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM, MARGIN_CENTER }
+
+const DRAW_NOTHING := -1
+const DRAW_CENTERED := -2
+const MARGIN_NONE := -1
+
+var _draw_margin := DRAW_NOTHING
+var _should_split := false
+
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_MOUSE_EXIT:
+ _draw_margin = DRAW_NOTHING
+ queue_redraw()
+ elif what == NOTIFICATION_MOUSE_ENTER and not _should_split:
+ _draw_margin = DRAW_CENTERED
+ queue_redraw()
+
+
+func _gui_input(event: InputEvent) -> void:
+ if _should_split and event is InputEventMouseMotion:
+ _draw_margin = _find_hover_margin(event.position)
+ queue_redraw()
+
+
+func _draw() -> void:
+ var rect: Rect2
+ if _draw_margin == DRAW_NOTHING:
+ return
+ elif _draw_margin == DRAW_CENTERED:
+ rect = Rect2(Vector2.ZERO, size)
+ elif _draw_margin == MARGIN_LEFT:
+ rect = Rect2(0, 0, size.x * 0.5, size.y)
+ elif _draw_margin == MARGIN_TOP:
+ rect = Rect2(0, 0, size.x, size.y * 0.5)
+ elif _draw_margin == MARGIN_RIGHT:
+ var half_width = size.x * 0.5
+ rect = Rect2(half_width, 0, half_width, size.y)
+ elif _draw_margin == MARGIN_BOTTOM:
+ var half_height = size.y * 0.5
+ rect = Rect2(0, half_height, size.x, half_height)
+ var stylebox := get_theme_stylebox("panel", "TooltipPanel")
+ draw_style_box(stylebox, rect)
+
+
+func set_enabled(enabled: bool, should_split: bool = true) -> void:
+ visible = enabled
+ _should_split = should_split
+ if enabled:
+ _draw_margin = DRAW_NOTHING
+ queue_redraw()
+
+
+func get_hover_margin() -> int:
+ return _draw_margin
+
+
+func _find_hover_margin(point: Vector2) -> int:
+ var half_size := size * 0.5
+
+ var left := point.distance_squared_to(Vector2(0, half_size.y))
+ var lesser := left
+ var lesser_margin := MARGIN_LEFT
+
+ var top := point.distance_squared_to(Vector2(half_size.x, 0))
+ if lesser > top:
+ lesser = top
+ lesser_margin = MARGIN_TOP
+
+ var right := point.distance_squared_to(Vector2(size.x, half_size.y))
+ if lesser > right:
+ lesser = right
+ lesser_margin = MARGIN_RIGHT
+
+ var bottom := point.distance_squared_to(Vector2(half_size.x, size.y))
+ if lesser > bottom:
+ #lesser = bottom # unused result
+ lesser_margin = MARGIN_BOTTOM
+ return lesser_margin
diff --git a/addons/dockable_container/drag_n_drop_panel.gd.uid b/addons/dockable_container/drag_n_drop_panel.gd.uid
new file mode 100644
index 00000000..535296f3
--- /dev/null
+++ b/addons/dockable_container/drag_n_drop_panel.gd.uid
@@ -0,0 +1 @@
+uid://btfkmp6r4s6kb
diff --git a/addons/dockable_container/floating_window.gd b/addons/dockable_container/floating_window.gd
new file mode 100644
index 00000000..99f5c7ca
--- /dev/null
+++ b/addons/dockable_container/floating_window.gd
@@ -0,0 +1,81 @@
+class_name FloatingWindow
+extends Window
+
+## Emitted when the window's position or size changes, or when it's closed.
+signal data_changed
+
+var window_content: Control
+var prevent_data_erasure := false
+var _is_initialized := false
+
+
+func _init(content: Control, data := {}) -> void:
+ hide()
+ window_content = content
+ title = window_content.name
+ name = window_content.name
+ min_size = window_content.get_minimum_size()
+ unresizable = false
+ wrap_controls = true
+ always_on_top = false
+ force_native = true
+ ready.connect(_deserialize.bind(data))
+
+
+func _ready() -> void:
+ set_deferred(&"size", Vector2(300, 300))
+ current_screen = get_window().current_screen
+ await get_tree().process_frame
+ await get_tree().process_frame
+ # Enable always_on_top for all child windows,
+ # to fix a bug where the child windows of floating windows appear behind them.
+ # TODO: Remove the loop when this bug gets fixed in Godot's side.
+ # Probably when https://github.com/godotengine/godot/issues/92848 is closed.
+ for dialog_child in find_children("", "Window", true, false):
+ if dialog_child is Window:
+ dialog_child.always_on_top = always_on_top
+
+ show()
+
+
+func _input(event: InputEvent) -> void:
+ if event is InputEventMouse:
+ # Emit `data_changed` when the window is being moved.
+ if not window_content.get_rect().has_point(event.position) and _is_initialized:
+ data_changed.emit(name, serialize())
+
+
+func serialize() -> Dictionary:
+ return {"size": size, "position": position}
+
+
+func _deserialize(data: Dictionary) -> void:
+ window_content.get_parent().remove_child(window_content)
+ window_content.visible = true
+ window_content.global_position = Vector2.ZERO
+ add_child(window_content)
+ size_changed.connect(window_size_changed)
+ if "position" in data:
+ await get_tree().process_frame
+ await get_tree().process_frame
+ position = data["position"]
+ if "size" in data:
+ set_deferred(&"size", data["size"])
+ _is_initialized = true
+
+
+func window_size_changed() -> void:
+ window_content.size = size
+ window_content.position = Vector2.ZERO
+ if _is_initialized:
+ data_changed.emit(name, serialize())
+
+
+func destroy() -> void:
+ size_changed.disconnect(window_size_changed)
+ queue_free()
+
+
+func _exit_tree() -> void:
+ if _is_initialized and !prevent_data_erasure:
+ data_changed.emit(name, {})
diff --git a/addons/dockable_container/floating_window.gd.uid b/addons/dockable_container/floating_window.gd.uid
new file mode 100644
index 00000000..ab69fc16
--- /dev/null
+++ b/addons/dockable_container/floating_window.gd.uid
@@ -0,0 +1 @@
+uid://bj1b6rdd8u8dy
diff --git a/addons/dockable_container/icon.svg b/addons/dockable_container/icon.svg
new file mode 100644
index 00000000..d87d598d
--- /dev/null
+++ b/addons/dockable_container/icon.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/addons/dockable_container/icon.svg.import b/addons/dockable_container/icon.svg.import
new file mode 100644
index 00000000..7ffd9942
--- /dev/null
+++ b/addons/dockable_container/icon.svg.import
@@ -0,0 +1,18 @@
+[remap]
+
+importer="svg"
+type="DPITexture"
+uid="uid://dy25danh2am23"
+path="res://.godot/imported/icon.svg-35635e7bbda4487d4b2942da1d987df8.dpitex"
+
+[deps]
+
+source_file="res://addons/dockable_container/icon.svg"
+dest_files=["res://.godot/imported/icon.svg-35635e7bbda4487d4b2942da1d987df8.dpitex"]
+
+[params]
+
+base_scale=1.0
+saturation=1.0
+color_map={}
+compress=true
diff --git a/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd b/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd
new file mode 100644
index 00000000..73d0372b
--- /dev/null
+++ b/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd
@@ -0,0 +1,22 @@
+extends EditorInspectorPlugin
+
+const LayoutEditorProperty := preload("layout_editor_property.gd")
+
+
+func _can_handle(object: Object) -> bool:
+ return object is DockableContainer
+
+
+func _parse_property(
+ _object: Object,
+ _type: Variant.Type,
+ name: String,
+ _hint: PropertyHint,
+ _hint_text: String,
+ _usage: int,
+ _wide: bool
+) -> bool:
+ if name == "layout":
+ var editor_property := LayoutEditorProperty.new()
+ add_property_editor("layout", editor_property)
+ return false
diff --git a/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd.uid b/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd.uid
new file mode 100644
index 00000000..db6dd318
--- /dev/null
+++ b/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd.uid
@@ -0,0 +1 @@
+uid://st877dcaad8x
diff --git a/addons/dockable_container/inspector_plugin/layout_editor_property.gd b/addons/dockable_container/inspector_plugin/layout_editor_property.gd
new file mode 100644
index 00000000..eb00134b
--- /dev/null
+++ b/addons/dockable_container/inspector_plugin/layout_editor_property.gd
@@ -0,0 +1,71 @@
+extends EditorProperty
+
+var _container := DockableContainer.new()
+var _hidden_menu_button := MenuButton.new()
+var _hidden_menu_popup: PopupMenu
+var _hidden_menu_list: PackedStringArray
+
+
+func _ready() -> void:
+ custom_minimum_size = Vector2(128, 256)
+
+ _hidden_menu_button.text = "Visible nodes"
+ add_child(_hidden_menu_button)
+ _hidden_menu_popup = _hidden_menu_button.get_popup()
+ _hidden_menu_popup.hide_on_checkable_item_selection = false
+ _hidden_menu_popup.about_to_popup.connect(_on_hidden_menu_popup_about_to_show)
+ _hidden_menu_popup.id_pressed.connect(_on_hidden_menu_popup_id_pressed)
+
+ _container.clone_layout_on_ready = false
+ _container.custom_minimum_size = custom_minimum_size
+
+ var value := _get_layout().clone() # The layout gets reset when selecting it without clone
+ for n in value.get_names():
+ var child := _create_child_control(n)
+ _container.add_child(child)
+ _container.set(get_edited_property(), value)
+ add_child(_container)
+ set_bottom_editor(_container)
+
+
+func _exit_tree() -> void: # Not sure if this is needed, but just to be sure
+ queue_free()
+
+
+func _update_property() -> void:
+ var value := _get_layout()
+ _container.set(get_edited_property(), value)
+
+
+func _get_layout() -> DockableLayout:
+ var original_container := get_edited_object() as DockableContainer
+ return original_container.get(get_edited_property())
+
+
+func _create_child_control(named: String) -> Label:
+ var new_control := Label.new()
+ new_control.name = named
+ new_control.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ new_control.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
+ new_control.clip_text = true
+ new_control.text = named
+ return new_control
+
+
+func _on_hidden_menu_popup_about_to_show() -> void:
+ var layout := _get_layout().clone()
+ _hidden_menu_popup.clear()
+ _hidden_menu_list = layout.get_names()
+ for i in _hidden_menu_list.size():
+ var tab_name := _hidden_menu_list[i]
+ _hidden_menu_popup.add_check_item(tab_name, i)
+ _hidden_menu_popup.set_item_checked(i, not layout.is_tab_hidden(tab_name))
+
+
+func _on_hidden_menu_popup_id_pressed(id: int) -> void:
+ var layout := _get_layout().clone()
+ var tab_name := _hidden_menu_list[id]
+ var new_hidden := not layout.is_tab_hidden(tab_name)
+ _get_layout().set_tab_hidden(tab_name, new_hidden)
+ _hidden_menu_popup.set_item_checked(id, not new_hidden)
+ emit_changed(get_edited_property(), _get_layout()) # This line may not be needed
diff --git a/addons/dockable_container/inspector_plugin/layout_editor_property.gd.uid b/addons/dockable_container/inspector_plugin/layout_editor_property.gd.uid
new file mode 100644
index 00000000..56aa173f
--- /dev/null
+++ b/addons/dockable_container/inspector_plugin/layout_editor_property.gd.uid
@@ -0,0 +1 @@
+uid://b10vbk0p47buq
diff --git a/addons/dockable_container/layout.gd b/addons/dockable_container/layout.gd
new file mode 100644
index 00000000..b02df10f
--- /dev/null
+++ b/addons/dockable_container/layout.gd
@@ -0,0 +1,260 @@
+@tool
+class_name DockableLayout
+extends Resource
+## DockableLayout Resource definition, holding the root DockableLayoutNode and hidden tabs.
+##
+## DockableLayoutSplit are binary trees with nested DockableLayoutSplit subtrees
+## and DockableLayoutPanel leaves. Both of them inherit from DockableLayoutNode to help with
+## type annotation and define common functionality.
+##
+## Hidden tabs are marked in the `hidden_tabs` Dictionary by name.
+
+enum { MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM, MARGIN_CENTER }
+
+@export var root: DockableLayoutNode = DockableLayoutPanel.new():
+ get:
+ return _root
+ set(value):
+ set_root(value)
+@export var hidden_tabs := {}:
+ get:
+ return _hidden_tabs
+ set(value):
+ if value != _hidden_tabs:
+ _hidden_tabs = value
+ changed.emit()
+## A [Dictionary] of [StringName] and [Dictionary], containing data such as position and size.
+@export var windows := {}:
+ get:
+ return _windows
+ set(value):
+ if value != _windows:
+ _windows = value
+ changed.emit()
+
+var _changed_signal_queued := false
+var _first_leaf: DockableLayoutPanel
+var _hidden_tabs: Dictionary
+var _windows: Dictionary
+var _leaf_by_node_name: Dictionary
+var _root: DockableLayoutNode = DockableLayoutPanel.new()
+
+
+func _init() -> void:
+ resource_name = "Layout"
+
+
+func set_root(value: DockableLayoutNode, should_emit_changed := true) -> void:
+ if not value:
+ value = DockableLayoutPanel.new()
+ if _root == value:
+ return
+ if _root and _root.changed.is_connected(_on_root_changed):
+ _root.changed.disconnect(_on_root_changed)
+ _root = value
+ _root.parent = null
+ _root.changed.connect(_on_root_changed)
+ if should_emit_changed:
+ _on_root_changed()
+
+
+func get_root() -> DockableLayoutNode:
+ return _root
+
+
+func clone() -> DockableLayout:
+ return duplicate(true)
+
+
+func get_names() -> PackedStringArray:
+ return _root.get_names()
+
+
+## Add missing nodes on first leaf and remove nodes outside indices from leaves.
+##
+## _leaf_by_node_name = {
+## (string keys) = respective Leaf that holds the node name,
+## }
+func update_nodes(names: PackedStringArray) -> void:
+ _leaf_by_node_name.clear()
+ _first_leaf = null
+ var empty_leaves: Array[DockableLayoutPanel] = []
+ _ensure_names_in_node(_root, names, empty_leaves) # Changes _leaf_by_node_name and empty_leaves
+ for l in empty_leaves:
+ _remove_leaf(l)
+ if not _first_leaf:
+ _first_leaf = DockableLayoutPanel.new()
+ set_root(_first_leaf)
+ for n in names:
+ if not _leaf_by_node_name.has(n):
+ _first_leaf.push_name(n)
+ _leaf_by_node_name[n] = _first_leaf
+ _on_root_changed()
+
+
+func move_node_to_leaf(node: Node, leaf: DockableLayoutPanel, relative_position: int) -> void:
+ var node_name := node.name
+ var previous_leaf: DockableLayoutPanel = _leaf_by_node_name.get(node_name)
+ if previous_leaf:
+ previous_leaf.remove_node(node)
+ if previous_leaf.is_empty():
+ _remove_leaf(previous_leaf)
+
+ leaf.insert_node(relative_position, node)
+ _leaf_by_node_name[node_name] = leaf
+ _on_root_changed()
+
+
+func get_leaf_for_node(node: Node) -> DockableLayoutPanel:
+ return _leaf_by_node_name.get(node.name)
+
+
+func split_leaf_with_node(leaf: DockableLayoutPanel, node: Node, margin: int) -> void:
+ var root_branch := leaf.parent
+ var new_leaf := DockableLayoutPanel.new()
+ var new_branch := DockableLayoutSplit.new()
+ if margin == MARGIN_LEFT or margin == MARGIN_RIGHT:
+ new_branch.direction = DockableLayoutSplit.Direction.HORIZONTAL
+ else:
+ new_branch.direction = DockableLayoutSplit.Direction.VERTICAL
+ if margin == MARGIN_LEFT or margin == MARGIN_TOP:
+ new_branch.first = new_leaf
+ new_branch.second = leaf
+ else:
+ new_branch.first = leaf
+ new_branch.second = new_leaf
+ if _root == leaf:
+ set_root(new_branch, false)
+ elif root_branch:
+ if leaf == root_branch.first:
+ root_branch.first = new_branch
+ else:
+ root_branch.second = new_branch
+
+ move_node_to_leaf(node, new_leaf, 0)
+
+
+func add_node(node: Node) -> void:
+ var node_name := node.name
+ if _leaf_by_node_name.has(node_name):
+ return
+ _first_leaf.push_name(node_name)
+ _leaf_by_node_name[node_name] = _first_leaf
+ _on_root_changed()
+
+
+func remove_node(node: Node) -> void:
+ var node_name := node.name
+ var leaf: DockableLayoutPanel = _leaf_by_node_name.get(node_name)
+ if not leaf:
+ return
+ leaf.remove_node(node)
+ _leaf_by_node_name.erase(node_name)
+ if leaf.is_empty():
+ _remove_leaf(leaf)
+ _on_root_changed()
+
+
+func rename_node(previous_name: String, new_name: String) -> void:
+ var leaf: DockableLayoutPanel = _leaf_by_node_name.get(previous_name)
+ if not leaf:
+ return
+ leaf.rename_node(previous_name, new_name)
+ _leaf_by_node_name.erase(previous_name)
+ _leaf_by_node_name[new_name] = leaf
+ _on_root_changed()
+
+
+func set_tab_hidden(name: String, hidden: bool) -> void:
+ if not _leaf_by_node_name.has(name):
+ return
+ if hidden:
+ _hidden_tabs[name] = true
+ else:
+ _hidden_tabs.erase(name)
+ _on_root_changed()
+
+
+func save_window_properties(window_name: StringName, data: Dictionary) -> void:
+ var new_windows = windows.duplicate(true)
+ if data.is_empty():
+ new_windows.erase(window_name)
+ else:
+ new_windows[window_name] = data
+ windows = new_windows
+
+
+func is_tab_hidden(name: String) -> bool:
+ return _hidden_tabs.get(name, false)
+
+
+func set_node_hidden(node: Node, hidden: bool) -> void:
+ set_tab_hidden(node.name, hidden)
+
+
+func is_node_hidden(node: Node) -> bool:
+ return is_tab_hidden(node.name)
+
+
+func _on_root_changed() -> void:
+ if _changed_signal_queued:
+ return
+ _changed_signal_queued = true
+ set_deferred("_changed_signal_queued", false)
+ emit_changed.call_deferred()
+
+
+func _ensure_names_in_node(
+ node: DockableLayoutNode, names: PackedStringArray, empty_leaves: Array[DockableLayoutPanel]
+) -> void:
+ if node is DockableLayoutPanel:
+ node.update_nodes(names, _leaf_by_node_name) # This changes _leaf_by_node_name
+ if node.is_empty():
+ empty_leaves.append(node)
+ if not _first_leaf:
+ _first_leaf = node
+ elif node is DockableLayoutSplit:
+ _ensure_names_in_node(node.first, names, empty_leaves)
+ _ensure_names_in_node(node.second, names, empty_leaves)
+ else:
+ assert(false, "Invalid Resource, should be branch or leaf, found %s" % node)
+
+
+func _remove_leaf(leaf: DockableLayoutPanel) -> void:
+ assert(leaf.is_empty(), "FIXME: trying to remove_at a leaf with nodes")
+ if _root == leaf:
+ return
+ var collapsed_branch := leaf.parent
+ assert(collapsed_branch is DockableLayoutSplit, "FIXME: leaf is not a child of branch")
+ var kept_branch: DockableLayoutNode = (
+ collapsed_branch.first if leaf == collapsed_branch.second else collapsed_branch.second
+ )
+ var root_branch := collapsed_branch.parent #HERE
+ if collapsed_branch == _root:
+ set_root(kept_branch, true)
+ elif root_branch:
+ if collapsed_branch == root_branch.first:
+ root_branch.first = kept_branch
+ else:
+ root_branch.second = kept_branch
+
+
+func _print_tree() -> void:
+ print("TREE")
+ _print_tree_step(_root, 0, 0)
+ print("")
+
+
+func _print_tree_step(tree_or_leaf: DockableLayoutNode, level: int, idx: int) -> void:
+ if tree_or_leaf is DockableLayoutPanel:
+ print(" |".repeat(level), "- (%d) = " % idx, tree_or_leaf.names)
+ elif tree_or_leaf is DockableLayoutSplit:
+ print(
+ " |".repeat(level),
+ "-+ (%d) = " % idx,
+ tree_or_leaf.direction,
+ " ",
+ tree_or_leaf.percent
+ )
+ _print_tree_step(tree_or_leaf.first, level + 1, 1)
+ _print_tree_step(tree_or_leaf.second, level + 1, 2)
diff --git a/addons/dockable_container/layout.gd.uid b/addons/dockable_container/layout.gd.uid
new file mode 100644
index 00000000..43f1ab21
--- /dev/null
+++ b/addons/dockable_container/layout.gd.uid
@@ -0,0 +1 @@
+uid://cylirj261q6ru
diff --git a/addons/dockable_container/layout_node.gd b/addons/dockable_container/layout_node.gd
new file mode 100644
index 00000000..ba3accb4
--- /dev/null
+++ b/addons/dockable_container/layout_node.gd
@@ -0,0 +1,29 @@
+@tool
+class_name DockableLayoutNode
+extends Resource
+## Base class for DockableLayout tree nodes
+
+var parent: DockableLayoutSplit:
+ get:
+ return _parent_ref.get_ref()
+ set(value):
+ _parent_ref = weakref(value)
+
+var _parent_ref := WeakRef.new()
+
+
+func emit_tree_changed() -> void:
+ var node := self
+ while node:
+ node.emit_changed()
+ node = node.parent
+
+
+## Returns whether there are any nodes
+func is_empty() -> bool:
+ return true
+
+
+## Returns all tab names in this node
+func get_names() -> PackedStringArray:
+ return PackedStringArray()
diff --git a/addons/dockable_container/layout_node.gd.uid b/addons/dockable_container/layout_node.gd.uid
new file mode 100644
index 00000000..1d581b5d
--- /dev/null
+++ b/addons/dockable_container/layout_node.gd.uid
@@ -0,0 +1 @@
+uid://v07flmpq7k2r
diff --git a/addons/dockable_container/layout_panel.gd b/addons/dockable_container/layout_panel.gd
new file mode 100644
index 00000000..e15201b1
--- /dev/null
+++ b/addons/dockable_container/layout_panel.gd
@@ -0,0 +1,89 @@
+@tool
+class_name DockableLayoutPanel
+extends DockableLayoutNode
+## DockableLayout leaf nodes, defining tabs
+
+@export var names: PackedStringArray:
+ get:
+ return get_names()
+ set(value):
+ _names = value
+ emit_tree_changed()
+@export var current_tab: int:
+ get:
+ return int(clamp(_current_tab, 0, _names.size() - 1))
+ set(value):
+ if value != _current_tab:
+ _current_tab = value
+ emit_tree_changed()
+
+var _names := PackedStringArray()
+var _current_tab := 0
+
+
+func _init() -> void:
+ resource_name = "Tabs"
+
+
+## Returns all tab names in this node
+func get_names() -> PackedStringArray:
+ return _names
+
+
+func push_name(name: String) -> void:
+ _names.append(name)
+ emit_tree_changed()
+
+
+func insert_node(position: int, node: Node) -> void:
+ _names.insert(position, node.name)
+ emit_tree_changed()
+
+
+func find_name(node_name: String) -> int:
+ for i in _names.size():
+ if _names[i] == node_name:
+ return i
+ return -1
+
+
+func find_child(node: Node) -> int:
+ return find_name(node.name)
+
+
+func remove_node(node: Node) -> void:
+ var i := find_child(node)
+ if i >= 0:
+ _names.remove_at(i)
+ emit_tree_changed()
+ else:
+ push_warning("Remove failed, node '%s' was not found" % node)
+
+
+func rename_node(previous_name: String, new_name: String) -> void:
+ var i := find_name(previous_name)
+ if i >= 0:
+ _names.set(i, new_name)
+ emit_tree_changed()
+ else:
+ push_warning("Rename failed, name '%s' was not found" % previous_name)
+
+
+## Returns whether there are any nodes
+func is_empty() -> bool:
+ return _names.is_empty()
+
+
+func update_nodes(node_names: PackedStringArray, data: Dictionary) -> void:
+ var i := 0
+ var removed_any := false
+ while i < _names.size():
+ var current := _names[i]
+ if not current in node_names or data.has(current):
+ _names.remove_at(i)
+ removed_any = true
+ else:
+ data[current] = self
+ i += 1
+ if removed_any:
+ emit_tree_changed()
diff --git a/addons/dockable_container/layout_panel.gd.uid b/addons/dockable_container/layout_panel.gd.uid
new file mode 100644
index 00000000..1edb035a
--- /dev/null
+++ b/addons/dockable_container/layout_panel.gd.uid
@@ -0,0 +1 @@
+uid://bh202gagkdkar
diff --git a/addons/dockable_container/layout_split.gd b/addons/dockable_container/layout_split.gd
new file mode 100644
index 00000000..45f1f78b
--- /dev/null
+++ b/addons/dockable_container/layout_split.gd
@@ -0,0 +1,111 @@
+@tool
+class_name DockableLayoutSplit
+extends DockableLayoutNode
+## DockableLayout binary tree nodes, defining subtrees and leaf panels
+
+enum Direction { HORIZONTAL, VERTICAL }
+
+@export var direction := Direction.HORIZONTAL:
+ get:
+ return get_direction()
+ set(value):
+ set_direction(value)
+@export_range(0, 1) var percent := 0.5:
+ get = get_percent,
+ set = set_percent
+@export var first: DockableLayoutNode = DockableLayoutPanel.new():
+ get:
+ return get_first()
+ set(value):
+ set_first(value)
+@export var second: DockableLayoutNode = DockableLayoutPanel.new():
+ get:
+ return get_second()
+ set(value):
+ set_second(value)
+
+var _direction := Direction.HORIZONTAL
+var _percent := 0.5
+var _first: DockableLayoutNode
+var _second: DockableLayoutNode
+
+
+func _init() -> void:
+ resource_name = "Split"
+
+
+func set_first(value: DockableLayoutNode) -> void:
+ if value == null:
+ _first = DockableLayoutPanel.new()
+ else:
+ _first = value
+ _first.parent = self
+ emit_tree_changed()
+
+
+func get_first() -> DockableLayoutNode:
+ return _first
+
+
+func set_second(value: DockableLayoutNode) -> void:
+ if value == null:
+ _second = DockableLayoutPanel.new()
+ else:
+ _second = value
+ emit_tree_changed()
+ _second.parent = self
+
+
+func get_second() -> DockableLayoutNode:
+ return _second
+
+
+func set_direction(value: Direction) -> void:
+ if value != _direction:
+ _direction = value
+ emit_tree_changed()
+
+
+func get_direction() -> Direction:
+ return _direction
+
+
+func set_percent(value: float) -> void:
+ var clamped_value := clampf(value, 0, 1)
+ if not is_equal_approx(_percent, clamped_value):
+ _percent = clamped_value
+ emit_tree_changed()
+
+
+func get_percent() -> float:
+ return _percent
+
+
+func get_names() -> PackedStringArray:
+ var names := _first.get_names()
+ names.append_array(_second.get_names())
+ return names
+
+
+## Returns whether there are any nodes
+func is_empty() -> bool:
+ return _first.is_empty() and _second.is_empty()
+
+
+func is_horizontal() -> bool:
+ return _direction == Direction.HORIZONTAL
+
+
+func is_vertical() -> bool:
+ return _direction == Direction.VERTICAL
+
+
+func _is_draggable() -> bool:
+ for dl: DockableLayoutNode in [first, second]:
+ if dl is not DockableLayoutPanel:
+ continue
+
+ for name in dl.get_names():
+ if name.begins_with("_"):
+ return false
+ return true
diff --git a/addons/dockable_container/layout_split.gd.uid b/addons/dockable_container/layout_split.gd.uid
new file mode 100644
index 00000000..3b8363b3
--- /dev/null
+++ b/addons/dockable_container/layout_split.gd.uid
@@ -0,0 +1 @@
+uid://dos02hjhrf1ws
diff --git a/addons/dockable_container/plugin.cfg b/addons/dockable_container/plugin.cfg
new file mode 100644
index 00000000..b3595919
--- /dev/null
+++ b/addons/dockable_container/plugin.cfg
@@ -0,0 +1,13 @@
+[plugin]
+
+name="Dockable Container"
+description="Container script that manages docking/tiling UI panels.
+
+Panels are composed of tabs that can be dragged around and dropped to split another panel or compose its tabs.
+
+Layout information is stored in Resource objects, so they can be saved/loaded from disk easily.
+
+This plugin also offers a replica of the Container layout to be edited directly in the inspector."
+author="gilzoide"
+version="1.1.2"
+script="plugin.gd"
diff --git a/addons/dockable_container/plugin.gd b/addons/dockable_container/plugin.gd
new file mode 100644
index 00000000..e93e0101
--- /dev/null
+++ b/addons/dockable_container/plugin.gd
@@ -0,0 +1,19 @@
+@tool
+extends EditorPlugin
+
+const LayoutInspectorPlugin := preload("inspector_plugin/editor_inspector_plugin.gd")
+const Icon := preload("icon.svg")
+
+var _layout_inspector_plugin: LayoutInspectorPlugin
+
+
+func _enter_tree() -> void:
+ _layout_inspector_plugin = LayoutInspectorPlugin.new()
+ add_custom_type("DockableContainer", "Container", DockableContainer, Icon)
+ add_inspector_plugin(_layout_inspector_plugin)
+
+
+func _exit_tree() -> void:
+ remove_inspector_plugin(_layout_inspector_plugin)
+ remove_custom_type("DockableContainer")
+ _layout_inspector_plugin = null
diff --git a/addons/dockable_container/plugin.gd.uid b/addons/dockable_container/plugin.gd.uid
new file mode 100644
index 00000000..ca216da1
--- /dev/null
+++ b/addons/dockable_container/plugin.gd.uid
@@ -0,0 +1 @@
+uid://cfkem7gta5wke
diff --git a/addons/dockable_container/samples/TestScene.gd b/addons/dockable_container/samples/TestScene.gd
new file mode 100644
index 00000000..f94ac972
--- /dev/null
+++ b/addons/dockable_container/samples/TestScene.gd
@@ -0,0 +1,63 @@
+extends VBoxContainer
+
+const SAVED_LAYOUT_PATH := "user://layout.tres"
+
+@onready var _container := $DockableContainers/DockableContainer as DockableContainer
+@onready var _clone_control := $HBoxContainer/ControlPrefab as ColorRect
+@onready var _checkbox_container := $HBoxContainer as HBoxContainer
+
+
+func _ready() -> void:
+ if not OS.is_userfs_persistent():
+ $HBoxContainer/SaveLayoutButton.visible = false
+ $HBoxContainer/LoadLayoutButton.visible = false
+
+ var tabs := _container.get_tabs()
+ for i in tabs.size():
+ var checkbox := CheckBox.new()
+ checkbox.text = str(i)
+ checkbox.button_pressed = not _container.is_control_hidden(tabs[i])
+ checkbox.toggled.connect(_on_CheckButton_toggled.bind(tabs[i]))
+ _checkbox_container.add_child(checkbox)
+
+
+func _on_add_pressed() -> void:
+ var control := _clone_control.duplicate()
+ control.get_node("Buttons/Rename").pressed.connect(
+ _on_control_rename_button_pressed.bind(control)
+ )
+ control.get_node("Buttons/Remove").pressed.connect(
+ _on_control_remove_button_pressed.bind(control)
+ )
+ control.color = Color(randf(), randf(), randf())
+ control.name = "Control0"
+
+ _container.add_child(control, true)
+ await _container.sort_children
+ _container.set_control_as_current_tab(control)
+
+
+func _on_save_pressed() -> void:
+ if ResourceSaver.save(_container.layout, SAVED_LAYOUT_PATH) != OK:
+ print("ERROR")
+
+
+func _on_load_pressed() -> void:
+ var res = load(SAVED_LAYOUT_PATH)
+ if res:
+ _container.set_layout(res.clone())
+ else:
+ print("Error")
+
+
+func _on_control_rename_button_pressed(control: Control) -> void:
+ control.name = StringName(str(control.name) + " =D")
+
+
+func _on_control_remove_button_pressed(control: Control) -> void:
+ control.get_parent().remove_child(control)
+ control.queue_free()
+
+
+func _on_CheckButton_toggled(button_pressed: bool, tab: Control) -> void:
+ _container.set_control_hidden(tab, not button_pressed)
diff --git a/addons/dockable_container/samples/TestScene.gd.uid b/addons/dockable_container/samples/TestScene.gd.uid
new file mode 100644
index 00000000..a8525dc2
--- /dev/null
+++ b/addons/dockable_container/samples/TestScene.gd.uid
@@ -0,0 +1 @@
+uid://57n31bygo7cy
diff --git a/addons/dockable_container/samples/TestScene.tscn b/addons/dockable_container/samples/TestScene.tscn
new file mode 100644
index 00000000..311440da
--- /dev/null
+++ b/addons/dockable_container/samples/TestScene.tscn
@@ -0,0 +1,181 @@
+[gd_scene load_steps=16 format=3 uid="uid://drlvhuchtk6if"]
+
+[ext_resource type="Script" path="res://addons/dockable_container/dockable_container.gd" id="1"]
+[ext_resource type="Script" path="res://addons/dockable_container/layout.gd" id="2"]
+[ext_resource type="Script" path="res://addons/dockable_container/samples/TestScene.gd" id="4"]
+[ext_resource type="Script" path="res://addons/dockable_container/layout_split.gd" id="4_yhgfb"]
+[ext_resource type="Script" path="res://addons/dockable_container/layout_panel.gd" id="5"]
+
+[sub_resource type="Resource" id="Resource_8aoc2"]
+resource_name = "Tabs"
+script = ExtResource("5")
+names = PackedStringArray("Control0")
+current_tab = 0
+
+[sub_resource type="Resource" id="Resource_6kjom"]
+resource_name = "Tabs"
+script = ExtResource("5")
+names = PackedStringArray("Control1", "Control2")
+current_tab = 0
+
+[sub_resource type="Resource" id="Resource_hl8y1"]
+resource_name = "Split"
+script = ExtResource("4_yhgfb")
+direction = 1
+percent = 0.5
+first = SubResource("Resource_8aoc2")
+second = SubResource("Resource_6kjom")
+
+[sub_resource type="Resource" id="Resource_ybwqe"]
+resource_name = "Layout"
+script = ExtResource("2")
+root = SubResource("Resource_hl8y1")
+hidden_tabs = {}
+windows = {}
+save_on_change = false
+
+[sub_resource type="Resource" id="Resource_ntwfj"]
+resource_name = "Tabs"
+script = ExtResource("5")
+names = PackedStringArray("Control3")
+current_tab = 0
+
+[sub_resource type="Resource" id="Resource_dmyvf"]
+resource_name = "Tabs"
+script = ExtResource("5")
+names = PackedStringArray("Control4")
+current_tab = 0
+
+[sub_resource type="Resource" id="Resource_vag66"]
+resource_name = "Split"
+script = ExtResource("4_yhgfb")
+direction = 1
+percent = 0.281
+first = SubResource("Resource_ntwfj")
+second = SubResource("Resource_dmyvf")
+
+[sub_resource type="Resource" id="Resource_4q660"]
+resource_name = "Tabs"
+script = ExtResource("5")
+names = PackedStringArray("Control5")
+current_tab = 0
+
+[sub_resource type="Resource" id="Resource_jhibs"]
+resource_name = "Split"
+script = ExtResource("4_yhgfb")
+direction = 0
+percent = 0.5
+first = SubResource("Resource_vag66")
+second = SubResource("Resource_4q660")
+
+[sub_resource type="Resource" id="Resource_xhxpg"]
+resource_name = "Layout"
+script = ExtResource("2")
+root = SubResource("Resource_jhibs")
+hidden_tabs = {}
+windows = {}
+save_on_change = false
+
+[node name="SampleScene" type="VBoxContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+script = ExtResource("4")
+
+[node name="HBoxContainer" type="HBoxContainer" parent="."]
+layout_mode = 2
+alignment = 1
+
+[node name="AddControlButton" type="Button" parent="HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 4
+text = "(+) ADD CONTROL"
+
+[node name="SaveLayoutButton" type="Button" parent="HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 4
+text = "Save Layout"
+
+[node name="LoadLayoutButton" type="Button" parent="HBoxContainer"]
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 4
+text = "Load Layout"
+
+[node name="ControlPrefab" type="ColorRect" parent="HBoxContainer"]
+visible = false
+layout_mode = 2
+color = Color(0.129412, 0.121569, 0.121569, 1)
+
+[node name="Buttons" type="VBoxContainer" parent="HBoxContainer/ControlPrefab"]
+layout_mode = 0
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+offset_left = -65.5
+offset_top = -22.0
+offset_right = 65.5
+offset_bottom = 22.0
+
+[node name="Rename" type="Button" parent="HBoxContainer/ControlPrefab/Buttons"]
+layout_mode = 2
+text = "Rename"
+
+[node name="Remove" type="Button" parent="HBoxContainer/ControlPrefab/Buttons"]
+layout_mode = 2
+text = "REMOVE"
+
+[node name="DockableContainers" type="HBoxContainer" parent="."]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="DockableContainer" type="Container" parent="DockableContainers"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource("1")
+layout = SubResource("Resource_ybwqe")
+
+[node name="Control0" type="ColorRect" parent="DockableContainers/DockableContainer"]
+layout_mode = 2
+
+[node name="Control1" type="ColorRect" parent="DockableContainers/DockableContainer"]
+layout_mode = 2
+color = Color(0.141176, 0.0745098, 0.603922, 1)
+
+[node name="Control2" type="ColorRect" parent="DockableContainers/DockableContainer"]
+visible = false
+layout_mode = 2
+color = Color(0.533333, 0.380392, 0.380392, 1)
+
+[node name="Separator" type="ColorRect" parent="DockableContainers"]
+custom_minimum_size = Vector2(50, 0)
+layout_mode = 2
+color = Color(0, 0, 0, 1)
+
+[node name="DockableContainer2" type="Container" parent="DockableContainers"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource("1")
+layout = SubResource("Resource_xhxpg")
+
+[node name="Control3" type="ColorRect" parent="DockableContainers/DockableContainer2"]
+layout_mode = 2
+color = Color(0, 1, 0.905882, 1)
+
+[node name="Control4" type="ColorRect" parent="DockableContainers/DockableContainer2"]
+layout_mode = 2
+color = Color(0, 0.698039, 0.0588235, 1)
+
+[node name="Control5" type="ColorRect" parent="DockableContainers/DockableContainer2"]
+layout_mode = 2
+color = Color(1, 0.937255, 0, 1)
+
+[connection signal="pressed" from="HBoxContainer/AddControlButton" to="." method="_on_add_pressed"]
+[connection signal="pressed" from="HBoxContainer/SaveLayoutButton" to="." method="_on_save_pressed"]
+[connection signal="pressed" from="HBoxContainer/LoadLayoutButton" to="." method="_on_load_pressed"]
diff --git a/addons/dockable_container/split_handle.gd b/addons/dockable_container/split_handle.gd
new file mode 100644
index 00000000..2dfc6163
--- /dev/null
+++ b/addons/dockable_container/split_handle.gd
@@ -0,0 +1,127 @@
+@tool
+extends Control
+
+const SPLIT_THEME_CLASS: PackedStringArray = [
+ "HSplitContainer", # SPLIT_THEME_CLASS[DockableLayoutSplit.Direction.HORIZONTAL]
+ "VSplitContainer", # SPLIT_THEME_CLASS[DockableLayoutSplit.Direction.VERTICAL]
+]
+
+const SPLIT_MOUSE_CURSOR_SHAPE: Array[Control.CursorShape] = [
+ Control.CURSOR_HSPLIT, # SPLIT_MOUSE_CURSOR_SHAPE[DockableLayoutSplit.Direction.HORIZONTAL]
+ Control.CURSOR_VSPLIT, # SPLIT_MOUSE_CURSOR_SHAPE[DockableLayoutSplit.Direction.VERTICAL]
+]
+
+var layout_split: DockableLayoutSplit
+var first_minimum_size: Vector2
+var second_minimum_size: Vector2
+
+var _parent_rect: Rect2
+var _mouse_hovering := false
+var _dragging := false
+var _draggable: bool:
+ get():
+ return layout_split._is_draggable()
+
+
+func _draw() -> void:
+ var theme_class := SPLIT_THEME_CLASS[layout_split.direction]
+ var icon := get_theme_icon("grabber", theme_class)
+ var autohide := bool(get_theme_constant("autohide", theme_class))
+ if not icon or (autohide and not _mouse_hovering):
+ return
+
+ draw_texture(icon, (size - icon.get_size()) * 0.5)
+
+
+func _gui_input(event: InputEvent) -> void:
+ if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and _draggable:
+ _dragging = event.is_pressed()
+ if event.double_click:
+ layout_split.percent = 0.5
+ elif _dragging and event is InputEventMouseMotion and _draggable:
+ if not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
+ _dragging = false
+ return
+
+ var mouse_in_parent := get_parent_control().get_local_mouse_position()
+ if layout_split.is_horizontal():
+ layout_split.percent = (
+ (mouse_in_parent.x - _parent_rect.position.x) / _parent_rect.size.x
+ )
+ else:
+ layout_split.percent = (
+ (mouse_in_parent.y - _parent_rect.position.y) / _parent_rect.size.y
+ )
+
+
+func _notification(what: int) -> void:
+ if what == NOTIFICATION_MOUSE_ENTER:
+ _mouse_hovering = true
+ set_split_cursor(true)
+ if bool(get_theme_constant("autohide", SPLIT_THEME_CLASS[layout_split.direction])):
+ queue_redraw()
+ elif what == NOTIFICATION_MOUSE_EXIT:
+ _mouse_hovering = false
+ set_split_cursor(false)
+ if bool(get_theme_constant("autohide", SPLIT_THEME_CLASS[layout_split.direction])):
+ queue_redraw()
+ elif what == NOTIFICATION_FOCUS_EXIT:
+ _dragging = false
+
+
+func get_layout_minimum_size() -> Vector2:
+ if not layout_split:
+ return Vector2.ZERO
+ var separation := get_theme_constant("separation", SPLIT_THEME_CLASS[layout_split.direction])
+ if layout_split.is_horizontal():
+ return Vector2(
+ first_minimum_size.x + separation + second_minimum_size.x,
+ maxf(first_minimum_size.y, second_minimum_size.y)
+ )
+ else:
+ return Vector2(
+ maxf(first_minimum_size.x, second_minimum_size.x),
+ first_minimum_size.y + separation + second_minimum_size.y
+ )
+
+
+func set_split_cursor(value: bool) -> void:
+ if value and _draggable:
+ mouse_default_cursor_shape = SPLIT_MOUSE_CURSOR_SHAPE[layout_split.direction]
+ else:
+ mouse_default_cursor_shape = CURSOR_ARROW
+
+
+func get_split_rects(rect: Rect2) -> Dictionary:
+ _parent_rect = rect
+ var separation := get_theme_constant("separation", SPLIT_THEME_CLASS[layout_split.direction])
+ var origin := rect.position
+ var percent := layout_split.percent
+ if layout_split.is_horizontal():
+ var split_offset := clampf(
+ rect.size.x * percent - separation * 0.5,
+ first_minimum_size.x,
+ rect.size.x - second_minimum_size.x - separation
+ )
+ var second_width := rect.size.x - split_offset - separation
+
+ return {
+ "first": Rect2(origin.x, origin.y, split_offset, rect.size.y),
+ "self": Rect2(origin.x + split_offset, origin.y, separation, rect.size.y),
+ "second":
+ Rect2(origin.x + split_offset + separation, origin.y, second_width, rect.size.y),
+ }
+ else:
+ var split_offset := clampf(
+ rect.size.y * percent - separation * 0.5,
+ first_minimum_size.y,
+ rect.size.y - second_minimum_size.y - separation
+ )
+ var second_height := rect.size.y - split_offset - separation
+
+ return {
+ "first": Rect2(origin.x, origin.y, rect.size.x, split_offset),
+ "self": Rect2(origin.x, origin.y + split_offset, rect.size.x, separation),
+ "second":
+ Rect2(origin.x, origin.y + split_offset + separation, rect.size.x, second_height),
+ }
diff --git a/addons/dockable_container/split_handle.gd.uid b/addons/dockable_container/split_handle.gd.uid
new file mode 100644
index 00000000..3bd28a98
--- /dev/null
+++ b/addons/dockable_container/split_handle.gd.uid
@@ -0,0 +1 @@
+uid://c3g628ohy6cus
diff --git a/addons/gdUnit4/GdUnitRunner.cfg b/addons/gdUnit4/GdUnitRunner.cfg
deleted file mode 100644
index b2caafaf..00000000
--- a/addons/gdUnit4/GdUnitRunner.cfg
+++ /dev/null
@@ -1,1060 +0,0 @@
-{
- "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"
- }
- ],
- "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..8a081abb 100644
--- a/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid
+++ b/addons/gdUnit4/bin/GdUnitCmdTool.gd.uid
@@ -1 +1 @@
-uid://p88p3dkvm32r
+uid://bj5p2b3mmvpv2
diff --git a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid
index 33c3e3f6..b2d5e762 100644
--- a/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid
+++ b/addons/gdUnit4/bin/GdUnitCopyLog.gd.uid
@@ -1 +1 @@
-uid://qnmevknqtkcr
+uid://du3e4ltm5exyw
diff --git a/addons/gdUnit4/plugin.cfg b/addons/gdUnit4/plugin.cfg
index 45d0af55..4b3cf178 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="5.0.5"
script="plugin.gd"
diff --git a/addons/gdUnit4/plugin.gd b/addons/gdUnit4/plugin.gd
index cefdc412..1a52ef3c 100644
--- a/addons/gdUnit4/plugin.gd
+++ b/addons/gdUnit4/plugin.gd
@@ -30,8 +30,10 @@ func _enter_tree() -> void:
var control := add_control_to_bottom_panel(_gd_console, "gdUnitConsole")
@warning_ignore("unsafe_method_access")
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")
diff --git a/addons/gdUnit4/plugin.gd.uid b/addons/gdUnit4/plugin.gd.uid
index cf0dbb04..c83a8417 100644
--- a/addons/gdUnit4/plugin.gd.uid
+++ b/addons/gdUnit4/plugin.gd.uid
@@ -1 +1 @@
-uid://bjat85epf7ofm
+uid://do7vudxbo4hhc
diff --git a/addons/gdUnit4/src/Comparator.gd.uid b/addons/gdUnit4/src/Comparator.gd.uid
index e941641d..66137723 100644
--- a/addons/gdUnit4/src/Comparator.gd.uid
+++ b/addons/gdUnit4/src/Comparator.gd.uid
@@ -1 +1 @@
-uid://cm41y6w7wthm4
+uid://fmchogf6bdf8
diff --git a/addons/gdUnit4/src/Fuzzers.gd.uid b/addons/gdUnit4/src/Fuzzers.gd.uid
index c16cd4aa..f48b919c 100644
--- a/addons/gdUnit4/src/Fuzzers.gd.uid
+++ b/addons/gdUnit4/src/Fuzzers.gd.uid
@@ -1 +1 @@
-uid://p6pp3cntrnd
+uid://dut2wofegoono
diff --git a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid
index 532347e8..ab4f79db 100644
--- a/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitArrayAssert.gd.uid
@@ -1 +1 @@
-uid://cqcsax3mfwcio
+uid://cffjqveit84v4
diff --git a/addons/gdUnit4/src/GdUnitAssert.gd.uid b/addons/gdUnit4/src/GdUnitAssert.gd.uid
index 2d716408..ecde72d7 100644
--- a/addons/gdUnit4/src/GdUnitAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitAssert.gd.uid
@@ -1 +1 @@
-uid://b2rci48lrkstq
+uid://dichuec7mryel
diff --git a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid
index 32ad46eb..42ce6ae7 100644
--- a/addons/gdUnit4/src/GdUnitAwaiter.gd.uid
+++ b/addons/gdUnit4/src/GdUnitAwaiter.gd.uid
@@ -1 +1 @@
-uid://dmw2wrfn6hq88
+uid://c0f57re4e7dk2
diff --git a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid
index e29a69d4..003f37ef 100644
--- a/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitBoolAssert.gd.uid
@@ -1 +1 @@
-uid://ba4be0xfbtnmf
+uid://dquqkxy1o024t
diff --git a/addons/gdUnit4/src/GdUnitConstants.gd.uid b/addons/gdUnit4/src/GdUnitConstants.gd.uid
index 456b802b..60811fa4 100644
--- a/addons/gdUnit4/src/GdUnitConstants.gd.uid
+++ b/addons/gdUnit4/src/GdUnitConstants.gd.uid
@@ -1 +1 @@
-uid://dsfw4040i5wdo
+uid://d066hnl6xhys
diff --git a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid
index bfac4d80..742a07a0 100644
--- a/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitDictionaryAssert.gd.uid
@@ -1 +1 @@
-uid://difkcajov8fkg
+uid://bq1gcsupvqo21
diff --git a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid
index 9d50f843..766bcf15 100644
--- a/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitFailureAssert.gd.uid
@@ -1 +1 @@
-uid://qtqlkea47sy2
+uid://c0ug13vv0w60v
diff --git a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid
index 7ee6da68..5f5f9340 100644
--- a/addons/gdUnit4/src/GdUnitFileAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitFileAssert.gd.uid
@@ -1 +1 @@
-uid://cpangqu3iagtj
+uid://ccdvwlsp8gun0
diff --git a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid
index 9c3983d7..607523bf 100644
--- a/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitFloatAssert.gd.uid
@@ -1 +1 @@
-uid://cii6rn31jx3al
+uid://bpmnnby4vuhk8
diff --git a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid
index 87e1e06b..6023a015 100644
--- a/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitFuncAssert.gd.uid
@@ -1 +1 @@
-uid://c5xibgs8ux5xy
+uid://bkt4ihw5hsa5u
diff --git a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid
index 7a59443c..b948b8d3 100644
--- a/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitGodotErrorAssert.gd.uid
@@ -1 +1 @@
-uid://co2g8c7py1k3e
+uid://dfqi0y7vrgj10
diff --git a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid
index 17e2b81e..ae2ed11d 100644
--- a/addons/gdUnit4/src/GdUnitIntAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitIntAssert.gd.uid
@@ -1 +1 @@
-uid://bpfhi1eheni4h
+uid://cue1n5kbfpf62
diff --git a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid
index e86bd11b..ac399915 100644
--- a/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitObjectAssert.gd.uid
@@ -1 +1 @@
-uid://d2d3bkxt1wcx7
+uid://c5wxapigqmk5q
diff --git a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid
index eb8143b2..f690de84 100644
--- a/addons/gdUnit4/src/GdUnitResultAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitResultAssert.gd.uid
@@ -1 +1 @@
-uid://4cgv3ss1mryp
+uid://dnip2ya5y8ycw
diff --git a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid
index 4457aaa1..513dc150 100644
--- a/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid
+++ b/addons/gdUnit4/src/GdUnitSceneRunner.gd.uid
@@ -1 +1 @@
-uid://dhdbg6m7ygvou
+uid://b3vx3wiljpn0f
diff --git a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid
index f09743f6..7d5da055 100644
--- a/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitSignalAssert.gd.uid
@@ -1 +1 @@
-uid://nqq3ony261ch
+uid://baq28tquvfpvx
diff --git a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid
index b90e3a81..03331917 100644
--- a/addons/gdUnit4/src/GdUnitStringAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitStringAssert.gd.uid
@@ -1 +1 @@
-uid://dcbgythmvh6g4
+uid://hcqrn03qu1e4
diff --git a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid
index faaa948b..a8db01ab 100644
--- a/addons/gdUnit4/src/GdUnitTestSuite.gd.uid
+++ b/addons/gdUnit4/src/GdUnitTestSuite.gd.uid
@@ -1 +1 @@
-uid://d27xn7ceg8ta1
+uid://c0yh28xahuiht
diff --git a/addons/gdUnit4/src/GdUnitTuple.gd.uid b/addons/gdUnit4/src/GdUnitTuple.gd.uid
index 9eb02219..4fcd08d7 100644
--- a/addons/gdUnit4/src/GdUnitTuple.gd.uid
+++ b/addons/gdUnit4/src/GdUnitTuple.gd.uid
@@ -1 +1 @@
-uid://4soxgauogw4g
+uid://c16ujl1btfyyb
diff --git a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid
index 9b4d3ee6..6e1921af 100644
--- a/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid
+++ b/addons/gdUnit4/src/GdUnitValueExtractor.gd.uid
@@ -1 +1 @@
-uid://dus26afrslc7c
+uid://4epm64nfbmhg
diff --git a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid
index 7222e571..bb440ddd 100644
--- a/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid
+++ b/addons/gdUnit4/src/GdUnitVectorAssert.gd.uid
@@ -1 +1 @@
-uid://61ct1w1pqjnk
+uid://bkmgw6fy1aohx
diff --git a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
index 1a3409a7..995cf47d 100644
--- a/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
+++ b/addons/gdUnit4/src/asserts/CallBackValueProvider.gd.uid
@@ -1 +1 @@
-uid://d3a4ypfxdkmru
+uid://caw2mwxk4y02a
diff --git a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
index 727b98e6..6aad89f3 100644
--- a/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
+++ b/addons/gdUnit4/src/asserts/DefaultValueProvider.gd.uid
@@ -1 +1 @@
-uid://bd0yn1rsh6307
+uid://ceuddy0hobjye
diff --git a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
index a22e8bef..2bb50832 100644
--- a/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdAssertMessages.gd.uid
@@ -1 +1 @@
-uid://cc3hdk43hh47
+uid://cce0evdm20w7c
diff --git a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
index 8660da2b..ce68dea3 100644
--- a/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdAssertReports.gd.uid
@@ -1 +1 @@
-uid://6kr4lpvi1qwq
+uid://cj58yddbde7p8
diff --git a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
index 8fa7f5a1..4bf5edd5 100644
--- a/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitArrayAssertImpl.gd.uid
@@ -1 +1 @@
-uid://e04hjp5huh74
+uid://blnx4jsud6upv
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
index 974376c9..80a27b41 100644
--- a/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertImpl.gd.uid
@@ -1 +1 @@
-uid://ciwobc7f6h46r
+uid://b8p4grxo0smkx
diff --git a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
index 009718e3..39df948c 100644
--- a/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitAssertions.gd.uid
@@ -1 +1 @@
-uid://mdemt0sdcelx
+uid://bvacky1evj2jv
diff --git a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
index 7b1d3294..6b773f41 100644
--- a/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitBoolAssertImpl.gd.uid
@@ -1 +1 @@
-uid://cuu0go0gd1ul7
+uid://bmktyrxj0k8p2
diff --git a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid
index dd8adbfa..9c1a50e7 100644
--- a/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitDictionaryAssertImpl.gd.uid
@@ -1 +1 @@
-uid://bwrof6ds0dpxt
+uid://dqw4e41njp2l8
diff --git a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid
index c0ba7bb3..a4866565 100644
--- a/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitFailureAssertImpl.gd.uid
@@ -1 +1 @@
-uid://4v5w8jahhm04
+uid://bkh5pmbre7kam
diff --git a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid
index feea0235..c0e39fc3 100644
--- a/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitFileAssertImpl.gd.uid
@@ -1 +1 @@
-uid://cmm7sbdnq6ynv
+uid://b0p0dpxafixwg
diff --git a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid
index a9047db4..f4eb1521 100644
--- a/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitFloatAssertImpl.gd.uid
@@ -1 +1 @@
-uid://ce0tctonwex1f
+uid://cfmvglkttiixi
diff --git a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid
index 05dc97c0..7e6295f9 100644
--- a/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitFuncAssertImpl.gd.uid
@@ -1 +1 @@
-uid://d0blnbdubapl2
+uid://dyj6mpqnx8fqh
diff --git a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid
index 9481101a..7553986f 100644
--- a/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitGodotErrorAssertImpl.gd.uid
@@ -1 +1 @@
-uid://csgwjvp1srw01
+uid://b28gwfs6x5d3c
diff --git a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid
index ea042aa2..d361034e 100644
--- a/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitIntAssertImpl.gd.uid
@@ -1 +1 @@
-uid://b4wyced4y202u
+uid://co0vsefhtjvly
diff --git a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid
index 2fc264a6..e12163f3 100644
--- a/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitObjectAssertImpl.gd.uid
@@ -1 +1 @@
-uid://boiiakwr5lbqu
+uid://dmg376u3xqyi2
diff --git a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid
index af6b7313..b090c457 100644
--- a/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitResultAssertImpl.gd.uid
@@ -1 +1 @@
-uid://dbe4cfms6j7tf
+uid://bg88ekxp3v4hh
diff --git a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid
index 13d71b9c..e3faa0e8 100644
--- a/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitSignalAssertImpl.gd.uid
@@ -1 +1 @@
-uid://dc0nxf3g1q6hg
+uid://yqt80k0iet10
diff --git a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid
index 454ba1dd..9ee32442 100644
--- a/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd.uid
@@ -1 +1 @@
-uid://cgc45q2bbnw6h
+uid://cnurg4dsh2j51
diff --git a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid
index 8d5800d4..6e929720 100644
--- a/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid
+++ b/addons/gdUnit4/src/asserts/GdUnitVectorAssertImpl.gd.uid
@@ -1 +1 @@
-uid://c8prlu08lq4pw
+uid://cb76021730n1g
diff --git a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid
index 44144cf3..053887b5 100644
--- a/addons/gdUnit4/src/asserts/ValueProvider.gd.uid
+++ b/addons/gdUnit4/src/asserts/ValueProvider.gd.uid
@@ -1 +1 @@
-uid://dmrnx0ievdw00
+uid://bwqax8vnkbk0t
diff --git a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid
index d711c4d4..66421f50 100644
--- a/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid
+++ b/addons/gdUnit4/src/cmd/CmdArgumentParser.gd.uid
@@ -1 +1 @@
-uid://dytw8hhfjgrfo
+uid://b4lwihq3wr0tw
diff --git a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid
index 401b3521..e49d6370 100644
--- a/addons/gdUnit4/src/cmd/CmdCommand.gd.uid
+++ b/addons/gdUnit4/src/cmd/CmdCommand.gd.uid
@@ -1 +1 @@
-uid://c3bmxrdyb2s7c
+uid://o40bgvtisayg
diff --git a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid
index e394020c..28b1f385 100644
--- a/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid
+++ b/addons/gdUnit4/src/cmd/CmdCommandHandler.gd.uid
@@ -1 +1 @@
-uid://bv18vku5wy8dh
+uid://dlkcvw2algkqo
diff --git a/addons/gdUnit4/src/cmd/CmdOption.gd.uid b/addons/gdUnit4/src/cmd/CmdOption.gd.uid
index a6e08ca7..8475e700 100644
--- a/addons/gdUnit4/src/cmd/CmdOption.gd.uid
+++ b/addons/gdUnit4/src/cmd/CmdOption.gd.uid
@@ -1 +1 @@
-uid://docx18wjoc50t
+uid://dnagtjbx1bcqu
diff --git a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid
index 5efaea1b..60be7c9d 100644
--- a/addons/gdUnit4/src/cmd/CmdOptions.gd.uid
+++ b/addons/gdUnit4/src/cmd/CmdOptions.gd.uid
@@ -1 +1 @@
-uid://d1rr3hrhxwpcx
+uid://w2jlsroiofho
diff --git a/addons/gdUnit4/src/core/GdArrayTools.gd.uid b/addons/gdUnit4/src/core/GdArrayTools.gd.uid
index d31fe723..6ae859ac 100644
--- a/addons/gdUnit4/src/core/GdArrayTools.gd.uid
+++ b/addons/gdUnit4/src/core/GdArrayTools.gd.uid
@@ -1 +1 @@
-uid://dysbpliok4jy3
+uid://d30x6br7de16j
diff --git a/addons/gdUnit4/src/core/GdDiffTool.gd.uid b/addons/gdUnit4/src/core/GdDiffTool.gd.uid
index 39495dfb..4ad8196e 100644
--- a/addons/gdUnit4/src/core/GdDiffTool.gd.uid
+++ b/addons/gdUnit4/src/core/GdDiffTool.gd.uid
@@ -1 +1 @@
-uid://b15cajmjouv4w
+uid://c0lieuv0eejrw
diff --git a/addons/gdUnit4/src/core/GdObjects.gd.uid b/addons/gdUnit4/src/core/GdObjects.gd.uid
index fb2475d9..aaa94f44 100644
--- a/addons/gdUnit4/src/core/GdObjects.gd.uid
+++ b/addons/gdUnit4/src/core/GdObjects.gd.uid
@@ -1 +1 @@
-uid://sdoq2leo0wh7
+uid://cbkqqtijh636g
diff --git a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid
index 597caf56..641370b7 100644
--- a/addons/gdUnit4/src/core/GdUnit4Version.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnit4Version.gd.uid
@@ -1 +1 @@
-uid://db2lxsj6mco0s
+uid://dkxn7forlqv0d
diff --git a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid
index 678e5c13..dd714bfd 100644
--- a/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitFileAccess.gd.uid
@@ -1 +1 @@
-uid://c3frtlg7eexve
+uid://ctippg01yi8cg
diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd b/addons/gdUnit4/src/core/GdUnitProperty.gd
index 138dd9f7..48de036b 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
diff --git a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid
index a1ea4b8c..703740de 100644
--- a/addons/gdUnit4/src/core/GdUnitProperty.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitProperty.gd.uid
@@ -1 +1 @@
-uid://bvcue10yckc55
+uid://dhuger7fnp4o5
diff --git a/addons/gdUnit4/src/core/GdUnitResult.gd.uid b/addons/gdUnit4/src/core/GdUnitResult.gd.uid
index 985e9878..1f77d27f 100644
--- a/addons/gdUnit4/src/core/GdUnitResult.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitResult.gd.uid
@@ -1 +1 @@
-uid://diw4j3mxoow71
+uid://bbsofx7xfmyb5
diff --git a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd
index 149893f2..9f36354d 100644
--- a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd
+++ b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd
@@ -70,7 +70,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..816ec711 100644
--- a/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitRunnerConfig.gd.uid
@@ -1 +1 @@
-uid://8t4ughvls7x4
+uid://chlwqcs7smmml
diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd
index c70be022..b8f1c82d 100644
--- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd
+++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd
@@ -502,7 +502,7 @@ func invoke(
arg9: Variant = NO_ARG) -> Variant:
var args: Array = GdArrayTools.filter_value([arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9], NO_ARG)
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
@@ -569,7 +569,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
diff --git a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid
index 66c5dd62..8ef4396e 100644
--- a/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSceneRunnerImpl.gd.uid
@@ -1 +1 @@
-uid://d28jctgpw3ia4
+uid://ddejpoiwafnsx
diff --git a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid
index a937b3a4..7b8ab155 100644
--- a/addons/gdUnit4/src/core/GdUnitSettings.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSettings.gd.uid
@@ -1 +1 @@
-uid://b41o5s7w6ruau
+uid://kb1fusmpl827
diff --git a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid
index 5411baae..25b42bb5 100644
--- a/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSignalAwaiter.gd.uid
@@ -1 +1 @@
-uid://vdafterb2ngh
+uid://vprbi1v5gyqb
diff --git a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid
index ca0929b6..181baa96 100644
--- a/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSignalCollector.gd.uid
@@ -1 +1 @@
-uid://cpi5b0k7xnw4j
+uid://bfxoxj4i1wdmf
diff --git a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid
index 4a7fd80d..7d8a1529 100644
--- a/addons/gdUnit4/src/core/GdUnitSignals.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSignals.gd.uid
@@ -1 +1 @@
-uid://c3gfd1at2bm81
+uid://bh6i3ln6vlbrk
diff --git a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid
index 07a94f76..5110f1f5 100644
--- a/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitSingleton.gd.uid
@@ -1 +1 @@
-uid://bc5fybsywnuya
+uid://coll4y58u7q5i
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..169edf2e 100644
--- a/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitTestResourceLoader.gd.uid
@@ -1 +1 @@
-uid://dtinkm1otfo2y
+uid://btteb7dviq513
diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid
index a2b1341e..325afc1b 100644
--- a/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitTestSuiteBuilder.gd.uid
@@ -1 +1 @@
-uid://dm1kreoovutly
+uid://sutpkyk072xx
diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd
index b5d27349..654bdc88 100644
--- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd
+++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd
@@ -224,7 +224,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
diff --git a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid
index f3d0fd6d..5a545073 100644
--- a/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd.uid
@@ -1 +1 @@
-uid://d05tmi5e02oqi
+uid://b7rd1ly2ugi07
diff --git a/addons/gdUnit4/src/core/GdUnitTools.gd.uid b/addons/gdUnit4/src/core/GdUnitTools.gd.uid
index 7a94c3ae..6549e888 100644
--- a/addons/gdUnit4/src/core/GdUnitTools.gd.uid
+++ b/addons/gdUnit4/src/core/GdUnitTools.gd.uid
@@ -1 +1 @@
-uid://bb5gyqgycvniq
+uid://coarfyhpqcnc1
diff --git a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid
index 6c3d5a06..c8605b3d 100644
--- a/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid
+++ b/addons/gdUnit4/src/core/GodotVersionFixures.gd.uid
@@ -1 +1 @@
-uid://5ihmoi7vjmd
+uid://ra53xlpn6c44
diff --git a/addons/gdUnit4/src/core/LocalTime.gd.uid b/addons/gdUnit4/src/core/LocalTime.gd.uid
index 9332291f..69637497 100644
--- a/addons/gdUnit4/src/core/LocalTime.gd.uid
+++ b/addons/gdUnit4/src/core/LocalTime.gd.uid
@@ -1 +1 @@
-uid://bkm1q7yvoxy7j
+uid://bjf3dk5qw21wx
diff --git a/addons/gdUnit4/src/core/_TestCase.gd.uid b/addons/gdUnit4/src/core/_TestCase.gd.uid
index 15288372..b8cfa927 100644
--- a/addons/gdUnit4/src/core/_TestCase.gd.uid
+++ b/addons/gdUnit4/src/core/_TestCase.gd.uid
@@ -1 +1 @@
-uid://srhv4b4s7cl6
+uid://cbkonrpx4fo1w
diff --git a/addons/gdUnit4/src/core/assets/touch-button.png.import b/addons/gdUnit4/src/core/assets/touch-button.png.import
index a84ce13b..5c918411 100644
--- a/addons/gdUnit4/src/core/assets/touch-button.png.import
+++ b/addons/gdUnit4/src/core/assets/touch-button.png.import
@@ -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..7f9c7deb 100644
--- a/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid
+++ b/addons/gdUnit4/src/core/attributes/TestCaseAttribute.gd.uid
@@ -1 +1 @@
-uid://dgxgnjr51711k
+uid://doey67tyoy6xb
diff --git a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid
index 41fca155..7c26cc9f 100644
--- a/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid
+++ b/addons/gdUnit4/src/core/command/GdUnitCommand.gd.uid
@@ -1 +1 @@
-uid://b5x0oc5ngf8lj
+uid://de56aharilx1p
diff --git a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid
index 8c138b6d..43b91fc5 100644
--- a/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid
+++ b/addons/gdUnit4/src/core/command/GdUnitCommandHandler.gd.uid
@@ -1 +1 @@
-uid://c0sifu2vquucy
+uid://b5tm2bvrkhtbd
diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid
index af3594b3..5c9b76eb 100644
--- a/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid
+++ b/addons/gdUnit4/src/core/command/GdUnitShortcut.gd.uid
@@ -1 +1 @@
-uid://do4egyy4scgfs
+uid://dch3r18obmu2h
diff --git a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid
index b0fbd4ee..24372c27 100644
--- a/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid
+++ b/addons/gdUnit4/src/core/command/GdUnitShortcutAction.gd.uid
@@ -1 +1 @@
-uid://bt74gepjg7ekn
+uid://buyllgjg2vyux
diff --git a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid
index 713df158..2eba042f 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid
+++ b/addons/gdUnit4/src/core/discovery/GdUnitGUID.gd.uid
@@ -1 +1 @@
-uid://d0151xs4n3iyb
+uid://dwssr1bh4i1aw
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..da1e037b 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestCase.gd.uid
@@ -1 +1 @@
-uid://xluthcvn10cq
+uid://di77insyh6j75
diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd
index f4103787..4f9111d0 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd
@@ -151,6 +151,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..46e1b8dd 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverGuard.gd.uid
@@ -1 +1 @@
-uid://bjxpngc0i437j
+uid://c4o1eyxqtr85k
diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid
index 06658467..475228ea 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverSink.gd.uid
@@ -1 +1 @@
-uid://di3w8b6us6l0p
+uid://6lq18dxktabs
diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd
index f7a672b6..996dd95c 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd
@@ -11,6 +11,10 @@ static func run() -> Array[GdUnitTestCase]:
var t:= Thread.new()
@warning_ignore("return_value_discarded")
t.start(func () -> Array[GdUnitTestCase]:
+ # Loading previous test session
+ var runner_config := GdUnitRunnerConfig.new()
+ runner_config.load_config()
+ var recovered_tests := runner_config.test_cases()
var test_suite_directories :PackedStringArray = GdUnitCommandHandler.scan_all_test_directories(GdUnitSettings.test_root_folder())
var scanner := GdUnitTestSuiteScanner.new()
@@ -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("Recovery last test session successfully, %d tests restored." % recovered_tests.size(), true)
return collected_tests
)
# wait unblocked to the tread is finished
@@ -40,6 +48,46 @@ 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("Recovery 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("Recovery 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
@@ -51,9 +99,10 @@ static func console_log_discover_results(tests: Array[GdUnitTestCase]) -> void:
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,7 @@ 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)
diff --git a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid
index 981c3f9b..60028632 100644
--- a/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid
+++ b/addons/gdUnit4/src/core/discovery/GdUnitTestDiscoverer.gd.uid
@@ -1 +1 @@
-uid://dkh40yvtnqoxw
+uid://qubknuaxgms1
diff --git a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid
index 549a202f..e0f4f841 100644
--- a/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid
+++ b/addons/gdUnit4/src/core/event/GdUnitEvent.gd.uid
@@ -1 +1 @@
-uid://0bl5sfv34mjl
+uid://mi4dinjy4nb8
diff --git a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid
index 919c791c..e0fb0946 100644
--- a/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid
+++ b/addons/gdUnit4/src/core/event/GdUnitEventInit.gd.uid
@@ -1 +1 @@
-uid://bc6lj1xsoonhx
+uid://durnex2gkjsea
diff --git a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid
index db9b4a23..d31c5831 100644
--- a/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid
+++ b/addons/gdUnit4/src/core/event/GdUnitEventStop.gd.uid
@@ -1 +1 @@
-uid://bsj6l1y8mebur
+uid://bontcauq3i2y3
diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid
index 141a50b2..79bb2b1b 100644
--- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid
+++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverEnd.gd.uid
@@ -1 +1 @@
-uid://bpepiow1roltr
+uid://i7i8rjh3m30m
diff --git a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid
index 0f87c131..da5e9a03 100644
--- a/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid
+++ b/addons/gdUnit4/src/core/event/GdUnitEventTestDiscoverStart.gd.uid
@@ -1 +1 @@
-uid://cvawxva8c1imy
+uid://coy02lpj7lgra
diff --git a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid
index 20171dfc..eb4e8cef 100644
--- a/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid
+++ b/addons/gdUnit4/src/core/execution/GdUnitExecutionContext.gd.uid
@@ -1 +1 @@
-uid://d3efv548pfjn3
+uid://cybn5vv8g6ij4
diff --git a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid
index 272d4ebb..4f6c1f83 100644
--- a/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid
+++ b/addons/gdUnit4/src/core/execution/GdUnitMemoryObserver.gd.uid
@@ -1 +1 @@
-uid://befegid5bg08s
+uid://bbiwtyyvfpv74
diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid
index 009974b7..e8942d0f 100644
--- a/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid
+++ b/addons/gdUnit4/src/core/execution/GdUnitTestReportCollector.gd.uid
@@ -1 +1 @@
-uid://r6m4c3af0jvf
+uid://546waj3gx7jv
diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd
index 1e1c08f9..b97f6fcd 100644
--- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd
+++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd
@@ -23,9 +23,9 @@ func execute(test_suite :GdUnitTestSuite) -> void:
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():
diff --git a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid
index a893e9b8..dc1bfb07 100644
--- a/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid
+++ b/addons/gdUnit4/src/core/execution/GdUnitTestSuiteExecutor.gd.uid
@@ -1 +1 @@
-uid://byu4stkw4742q
+uid://cpopn8eaay2cn
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseAfterStage.gd.uid
index 11b9385b..9ba20046 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://cvta7ko1501fd
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseBeforeStage.gd.uid
index 9e433536..0783bdc4 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://ddgonfohcnc71
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestCaseExecutionStage.gd.uid
index d6cbf4bb..ee180cf6 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://bvc28jl2py5uv
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteAfterStage.gd.uid
index 5d7ec4bd..c9a2c8be 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://c65ts615srhu4
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteBeforeStage.gd.uid
index a1153692..5942fb12 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://3o45uqg6xq32
diff --git a/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/GdUnitTestSuiteExecutionStage.gd.uid
index a65d54cb..35d6581d 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://d1uxwd33mjg3t
diff --git a/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid b/addons/gdUnit4/src/core/execution/stages/IGdUnitExecutionStage.gd.uid
index 5b7733a3..b46b611a 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://dddrb70m1i27q
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..847a4336 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://bw02toxonv7i5
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..996e12ac 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://cw0d366wx8i3n
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..dbe711b0 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://ijkm5vlud50
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..4cd87db7 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://h1vt5ehmfpc4
diff --git a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid
index 54e24d96..def2ea99 100644
--- a/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdClassDescriptor.gd.uid
@@ -1 +1 @@
-uid://cd7lic1hrb5vf
+uid://tfw5f2av7ke7
diff --git a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd
index 202926ea..de8c29a6 100644
--- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd
+++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd
@@ -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..ed3167f3 100644
--- a/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdDefaultValueDecoder.gd.uid
@@ -1 +1 @@
-uid://bvcgb8ww4rpy1
+uid://ovt8lkvlngkq
diff --git a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd
index 087cf408..1cc27dbf 100644
--- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd
+++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd
@@ -57,7 +57,7 @@ func set_value(value: String) -> void:
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:
@@ -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..30d9ff2c 100644
--- a/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdFunctionArgument.gd.uid
@@ -1 +1 @@
-uid://c5iskcxtyerbi
+uid://dnmyxq3ftdweh
diff --git a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid
index 7edf93d8..ef68bd89 100644
--- a/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdFunctionDescriptor.gd.uid
@@ -1 +1 @@
-uid://bbbvxjnaas2n8
+uid://cwmh0qkwy4lsv
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..ae174881 100644
--- a/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdFunctionParameterSetResolver.gd.uid
@@ -1 +1 @@
-uid://bk704y2xw4nyd
+uid://cr7giifccl6bx
diff --git a/addons/gdUnit4/src/core/parse/GdScriptParser.gd b/addons/gdUnit4/src/core/parse/GdScriptParser.gd
index 83789361..7f8c7031 100644
--- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd
+++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd
@@ -74,6 +74,7 @@ var _regex_clazz_name := GdUnitTools.to_regex("(class) ([a-zA-Z0-9_]+) (extends[
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:
@@ -650,14 +651,17 @@ 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()
+ # skip empty lines
+ if 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..5580f706 100644
--- a/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdScriptParser.gd.uid
@@ -1 +1 @@
-uid://b78j43ljfuk77
+uid://dkoi16up7my6t
diff --git a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid
index a45cbb43..a9351687 100644
--- a/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdUnitExpressionRunner.gd.uid
@@ -1 +1 @@
-uid://b1yoqtvbi13bp
+uid://db0x6bk6p662j
diff --git a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid
index aad421fd..a338cc7d 100644
--- a/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid
+++ b/addons/gdUnit4/src/core/parse/GdUnitTestParameterSetResolver.gd.uid
@@ -1 +1 @@
-uid://cxrcrn0fwe7u6
+uid://dqtbrkqywyioi
diff --git a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid
index 97b6e99d..3440b552 100644
--- a/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid
+++ b/addons/gdUnit4/src/core/report/GdUnitReport.gd.uid
@@ -1 +1 @@
-uid://bvq0loao62flo
+uid://2uqm256ou7l4
diff --git a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid
index 43b27cef..cc91c2a9 100644
--- a/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid
+++ b/addons/gdUnit4/src/core/runners/GdUnitBaseTestRunner.gd.uid
@@ -1 +1 @@
-uid://d067xlq7k011y
+uid://diaekfrhgif7n
diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid
index 6bd3a81e..5859a2d3 100644
--- a/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid
+++ b/addons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gd.uid
@@ -1 +1 @@
-uid://bj5xoyqhuebbg
+uid://bbmp0coocwb6p
diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid
index aff91a66..22b2fcb0 100644
--- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid
+++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.gd.uid
@@ -1 +1 @@
-uid://ck1aurdyu3fdc
+uid://dfybw0apor6w6
diff --git a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn
index 1da430e4..afffc171 100644
--- a/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn
+++ b/addons/gdUnit4/src/core/runners/GdUnitTestRunner.tscn
@@ -1,7 +1,7 @@
[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")
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..8bdf1f42 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://0mughm34cuxb
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..04df5344 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://u7tl5obo6ss7
diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid
index 28f25fcc..66e8f059 100644
--- a/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid
+++ b/addons/gdUnit4/src/core/thread/GdUnitThreadContext.gd.uid
@@ -1 +1 @@
-uid://12h8llwtn2r5
+uid://bllwdumm80bl1
diff --git a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid
index 58f9b6cf..e5586a7c 100644
--- a/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid
+++ b/addons/gdUnit4/src/core/thread/GdUnitThreadManager.gd.uid
@@ -1 +1 @@
-uid://cwxjlrkeyjqso
+uid://b8nbf5wokdtiy
diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid
index d3fbc18d..3ecb2f58 100644
--- a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid
+++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid
@@ -1 +1 @@
-uid://dd0l0xtkj75j
+uid://ofhpc2ovxde4
diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid
index e5af79ed..880d1ed8 100644
--- a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid
+++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid
@@ -1 +1 @@
-uid://b68mtydadk8xp
+uid://ccsi7syb3wtgy
diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid
index 02a36e64..dc9ce006 100644
--- a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid
+++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid
@@ -1 +1 @@
-uid://53rfaenrwp4p
+uid://bfxdwhov4a0iv
diff --git a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs
index af8320f4..d62d3da3 100644
--- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs
+++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApi.cs
@@ -1,204 +1,176 @@
namespace gdUnit4.addons.gdUnit4.src.dotnet;
+#if GDUNIT4NET_API_V5
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
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
+public partial class GdUnit4CSharpApi : GdUnit4NetApiGodotBridge
+{
+ [Signal]
+ public delegate void ExecutionCompletedEventHandler();
+
+ private CancellationTokenSource? executionCts;
+
+ public override void _Notification(int what)
+ {
+ if (what != NotificationPredelete)
+ return;
+ executionCts?.Dispose();
+ executionCts = null;
+ }
+
+ public static bool IsApiLoaded()
+ => true;
+
+ public static Array DiscoverTests(CSharpScript sourceScript)
+ {
+ try
+ {
+ // Get the list of test case descriptors from the API
+ var testCaseDescriptors = DiscoverTestsFromScript(sourceScript);
+ // Convert each TestCaseDescriptor to a Dictionary
+ return testCaseDescriptors
+ .Select(descriptor => new Dictionary
+ {
+ ["guid"] = descriptor.Id.ToString(),
+ ["managed_type"] = descriptor.ManagedType,
+ ["test_name"] = descriptor.ManagedMethod,
+ ["source_file"] = sourceScript.ResourcePath,
+ ["line_number"] = descriptor.LineNumber,
+ ["attribute_index"] = descriptor.AttributeIndex,
+ ["require_godot_runtime"] = descriptor.RequireRunningGodotEngine,
+ ["code_file_path"] = descriptor.CodeFilePath ?? "",
+ ["simple_name"] = descriptor.SimpleName,
+ ["fully_qualified_name"] = descriptor.FullyQualifiedName,
+ ["assembly_location"] = descriptor.AssemblyPath
+ })
+ .Aggregate(new Array(), (array, dict) =>
+ {
+ array.Add(dict);
+ return array;
+ });
+ }
+ catch (Exception e)
+ {
+ GD.PrintErr($"Error discovering tests: {e.Message}\n{e.StackTrace}");
+ return new Array();
+ }
+ }
+
+ public void ExecuteAsync(Array tests, Callable listener)
+ {
+ try
+ {
+ // Cancel any ongoing execution
+ executionCts?.Cancel();
+ executionCts?.Dispose();
+
+ // Create new cancellation token source
+ executionCts = new CancellationTokenSource();
+
+ var testSuiteNodes = new List { BuildTestSuiteNodeFrom(tests) };
+ ExecuteAsync(testSuiteNodes, listener, executionCts.Token)
+ .GetAwaiter()
+ .OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted));
+ }
+ catch (Exception e)
+ {
+ GD.PrintErr($"Error executing tests: {e.Message}\n{e.StackTrace}");
+ Task.Run(() => { }).GetAwaiter().OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted));
+ }
+ }
+
+ public void CancelExecution()
+ {
+ try
+ {
+ executionCts?.Cancel();
+ }
+ catch (Exception e)
+ {
+ GD.PrintErr($"Error cancelling execution: {e.Message}");
+ }
+ }
+
+ // 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)
+ {
+ if (tests.Count == 0)
+ throw new InvalidOperationException("Cant build 'TestSuiteNode' from an empty test set.");
+
+ // Create a suite ID
+ var suiteId = Guid.NewGuid();
+ var firstTest = tests[0];
+ var managedType = firstTest["managed_type"].AsString();
+ var assemblyLocation = firstTest["assembly_location"].AsString();
+ var sourceFile = firstTest["source_file"].AsString();
+
+ // 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()
+ }
+ )
+ .ToList();
+
+ return new TestSuiteNode
+ {
+ Id = suiteId,
+ ParentId = Guid.Empty,
+ ManagedType = managedType,
+ AssemblyPath = assemblyLocation,
+ SourceFile = sourceFile,
+ Tests = testCaseNodes
+ };
+ }
+}
+#else
+using Godot;
+using Godot.Collections;
+
public partial class GdUnit4CSharpApi : RefCounted
{
- [Signal]
- public delegate void ExecutionCompletedEventHandler();
-
- private static readonly object LockObject = new();
-
- private static Type? apiType;
- private static Assembly? gdUnit4Api;
- 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);
-
- public static Array DiscoverTests(CSharpScript sourceScript)
- {
- try
- {
- // Get the list of test case descriptors from the API
- var testCaseDescriptors = InvokeApiMethod>("DiscoverTestsFromScript", sourceScript)!;
- // Convert each TestCaseDescriptor to a Dictionary
- return testCaseDescriptors
- .Select(descriptor => new Dictionary
- {
- ["guid"] = descriptor.Id.ToString(),
- ["managed_type"] = descriptor.ManagedType,
- ["test_name"] = descriptor.ManagedMethod,
- ["source_file"] = sourceScript.ResourcePath,
- ["line_number"] = descriptor.LineNumber,
- ["attribute_index"] = descriptor.AttributeIndex,
- ["require_godot_runtime"] = descriptor.RequireRunningGodotEngine,
- ["code_file_path"] = descriptor.CodeFilePath ?? "",
- ["simple_name"] = descriptor.SimpleName,
- ["fully_qualified_name"] = descriptor.FullyQualifiedName,
- ["assembly_location"] = descriptor.AssemblyPath
- })
- .Aggregate(new Array(), (array, dict) =>
- {
- array.Add(dict);
- return array;
- });
- }
- catch (Exception e)
- {
- GD.PrintErr($"Error discovering tests: {e.Message}\n{e.StackTrace}");
- return new Array();
- }
- }
-
- public void ExecuteAsync(Array tests, Callable listener)
- {
- try
- {
- // Cancel any ongoing execution
- executionCts?.Cancel();
- executionCts?.Dispose();
-
- // Create new cancellation token source
- executionCts = new CancellationTokenSource();
-
- var testSuiteNodes = new List { BuildTestSuiteNodeFrom(tests) };
- InvokeApiMethod("ExecuteAsync", testSuiteNodes, listener, executionCts.Token)?
- .GetAwaiter()
- .OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted));
- }
- catch (Exception e)
- {
- GD.PrintErr($"Error executing tests: {e.Message}\n{e.StackTrace}");
- Task.Run(() => { }).GetAwaiter().OnCompleted(() => EmitSignal(SignalName.ExecutionCompleted));
- }
- }
-
- public void CancelExecution()
- {
- try
- {
- executionCts?.Cancel();
- }
- catch (Exception e)
- {
- 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)
- {
- if (tests.Count == 0)
- throw new InvalidOperationException("Cant build 'TestSuiteNode' from an empty test set.");
-
- // Create a suite ID
- var suiteId = Guid.NewGuid();
- var firstTest = tests[0];
- var managedType = firstTest["managed_type"].AsString();
- var assemblyLocation = firstTest["assembly_location"].AsString();
- var sourceFile = firstTest["source_file"].AsString();
-
- // 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()
- }
- )
- .ToList();
-
- return new TestSuiteNode
- {
- Id = suiteId,
- ParentId = Guid.Empty,
- ManagedType = managedType,
- AssemblyPath = assemblyLocation,
- SourceFile = sourceFile,
- Tests = testCaseNodes
- };
- }
+ [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..199570c6 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.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..d46ee2c1 100644
--- a/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid
+++ b/addons/gdUnit4/src/dotnet/GdUnit4CSharpApiLoader.gd.uid
@@ -1 +1 @@
-uid://em6wu2kmyv05
+uid://clbrjt518h411
diff --git a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid
index 9c937dca..44ccbcc6 100644
--- a/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid
+++ b/addons/gdUnit4/src/doubler/CallableDoubler.gd.uid
@@ -1 +1 @@
-uid://c44li8m22q2g
+uid://bekuscrnacsu8
diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd
index 892e65c8..090b71df 100644
--- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd
+++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd
@@ -200,8 +200,12 @@ 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())
+ # For Variant types we need to enforce the type in the signature
+ if arg.type() == GdObjects.TYPE_VARIANT:
+ collect.push_back("%s_:%s=%s" % [arg.name(), GdObjects.type_as_string(arg.type()), arg.value_as_string()])
+ else:
+ @warning_ignore("return_value_discarded")
+ collect.push_back("%s_=%s" % [arg.name(), arg.value_as_string()])
else:
@warning_ignore("return_value_discarded")
collect.push_back(arg.name() + "_")
diff --git a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid
index e0c3e24e..158d6f6a 100644
--- a/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid
+++ b/addons/gdUnit4/src/doubler/GdFunctionDoubler.gd.uid
@@ -1 +1 @@
-uid://c7snbahh8hwm3
+uid://hdyhj2fbcegc
diff --git a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid
index c85f07a2..7b2ae3e0 100644
--- a/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid
+++ b/addons/gdUnit4/src/doubler/GdUnitClassDoubler.gd.uid
@@ -1 +1 @@
-uid://by7gx6owx6r0u
+uid://dliu4g7unhwj5
diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid
index 742a23df..8b646db9 100644
--- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid
+++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractions.gd.uid
@@ -1 +1 @@
-uid://bx7ng06gj6xt2
+uid://ct4khumjijdfm
diff --git a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid
index 3db11b0b..80928c20 100644
--- a/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid
+++ b/addons/gdUnit4/src/doubler/GdUnitObjectInteractionsVerifier.gd.uid
@@ -1 +1 @@
-uid://7ni6p5fmj303
+uid://peakocc268o5
diff --git a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid
index 2503e518..66656317 100644
--- a/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid
+++ b/addons/gdUnit4/src/extractors/GdUnitFuncValueExtractor.gd.uid
@@ -1 +1 @@
-uid://bnykspgpnndrk
+uid://drpfbnkhhiue
diff --git a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid
index 9b5ac982..ba66d779 100644
--- a/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/FloatFuzzer.gd.uid
@@ -1 +1 @@
-uid://d05keunlyb13g
+uid://b7y4t0ub35p13
diff --git a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid
index 1d1bc1b1..e72dd2c9 100644
--- a/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/Fuzzer.gd.uid
@@ -1 +1 @@
-uid://dn4s1yxhf58rv
+uid://e337t4bar8cp
diff --git a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid
index 73bd8501..cf79c329 100644
--- a/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/IntFuzzer.gd.uid
@@ -1 +1 @@
-uid://bvyh3bnhntg8f
+uid://vtek3ibtchre
diff --git a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid
index d45396a5..398c6405 100644
--- a/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/StringFuzzer.gd.uid
@@ -1 +1 @@
-uid://cmghw5dtcdrxl
+uid://dq400wh5q0ilk
diff --git a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid
index 36643e8c..42a623da 100644
--- a/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/Vector2Fuzzer.gd.uid
@@ -1 +1 @@
-uid://c0nafcj411138
+uid://bg3nrrvm61ptw
diff --git a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid
index cb55b26b..1975a70e 100644
--- a/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid
+++ b/addons/gdUnit4/src/fuzzers/Vector3Fuzzer.gd.uid
@@ -1 +1 @@
-uid://bdw37s7i5ddgy
+uid://b8dicnlqjqvv1
diff --git a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid
index 418aba02..6bd02e24 100644
--- a/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/AnyArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://8wtpiv1qvpg6
+uid://clgomxdt2lcgv
diff --git a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid
index 54dcb2d0..c2eee052 100644
--- a/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/AnyBuildInTypeArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://bpvbfh6rscg6b
+uid://bg18q33ici5x3
diff --git a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid
index c75956c7..98124612 100644
--- a/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/AnyClazzArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://berqup1jifqvo
+uid://dvmgtradtmap1
diff --git a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid
index 12a810d8..0918179f 100644
--- a/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/ChainedArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://bxfddo1mxd7yk
+uid://bf7cly10vkif1
diff --git a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid
index 4066bdc8..3c9dbb33 100644
--- a/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/EqualsArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://dptx538y0iv5
+uid://b5g3fu6ia18v3
diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid
index da99d3a3..f12b045b 100644
--- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid
+++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatcher.gd.uid
@@ -1 +1 @@
-uid://lcfje3tsypup
+uid://ca01bqnc2dfi5
diff --git a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid
index f0d7dc53..db251d07 100644
--- a/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid
+++ b/addons/gdUnit4/src/matchers/GdUnitArgumentMatchers.gd.uid
@@ -1 +1 @@
-uid://c8voejjm5titg
+uid://k5if1ve7sf4f
diff --git a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid
index 9c3e5478..1676f1ee 100644
--- a/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid
+++ b/addons/gdUnit4/src/mocking/GdUnitMock.gd.uid
@@ -1 +1 @@
-uid://dgc1qam4o7i0i
+uid://snhg1g74hjp4
diff --git a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid
index f40ca653..2ba58f24 100644
--- a/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid
+++ b/addons/gdUnit4/src/mocking/GdUnitMockBuilder.gd.uid
@@ -1 +1 @@
-uid://dq56630tdb00j
+uid://c0shsnsrusbyd
diff --git a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid
index 7e82b1c1..7e3da141 100644
--- a/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid
+++ b/addons/gdUnit4/src/mocking/GdUnitMockFunctionDoubler.gd.uid
@@ -1 +1 @@
-uid://jm8onp8m4k25
+uid://dbftl6vpbaswy
diff --git a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid
index 75e24f16..7d27ffc4 100644
--- a/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid
+++ b/addons/gdUnit4/src/mocking/GdUnitMockImpl.gd.uid
@@ -1 +1 @@
-uid://derlwqu7gm3qj
+uid://bqq7tk4hvva8f
diff --git a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid
index 1a235d0d..ca7a1423 100644
--- a/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid
+++ b/addons/gdUnit4/src/monitor/ErrorLogEntry.gd.uid
@@ -1 +1 @@
-uid://dkiel0qlk1gk2
+uid://daopfdh4fej8n
diff --git a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid
index a5a4a1fa..44588a08 100644
--- a/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid
+++ b/addons/gdUnit4/src/monitor/GdUnitMonitor.gd.uid
@@ -1 +1 @@
-uid://cgjdtqp2vmlvn
+uid://dw7kiptih6weg
diff --git a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid
index 18e6fab6..79a1439e 100644
--- a/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid
+++ b/addons/gdUnit4/src/monitor/GdUnitOrphanNodesMonitor.gd.uid
@@ -1 +1 @@
-uid://c05r34gpp50to
+uid://dhlaa1gqwg0rm
diff --git a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd
index 2ea2234e..b0737051 100644
--- a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd
+++ b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd
@@ -52,6 +52,20 @@ func erase_log_entry(entry: ErrorLogEntry) -> void:
_entries.erase(entry)
+func collect_full_logs() -> PackedStringArray:
+ await (Engine.get_main_loop() as SceneTree).process_frame
+ await (Engine.get_main_loop() as SceneTree).physics_frame
+
+ 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())
+
+ return records
+
+
func _collect_log_entries(force_collect_reports: bool) -> Array[ErrorLogEntry]:
var file := FileAccess.open(_godot_log_file, FileAccess.READ)
file.seek(_eof)
diff --git a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid
index 99621185..b31d376f 100644
--- a/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid
+++ b/addons/gdUnit4/src/monitor/GodotGdErrorMonitor.gd.uid
@@ -1 +1 @@
-uid://1i2srtcp8q5
+uid://dcuvgoaxonxbc
diff --git a/addons/gdUnit4/src/network/GdUnitServer.gd.uid b/addons/gdUnit4/src/network/GdUnitServer.gd.uid
index 49b1cb8e..fe80d01f 100644
--- a/addons/gdUnit4/src/network/GdUnitServer.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitServer.gd.uid
@@ -1 +1 @@
-uid://d2b4atsva3n6r
+uid://cdmq5d5672rdx
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..1f7d5624 100644
--- a/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitServerConstants.gd.uid
@@ -1 +1 @@
-uid://jjy8r2hwxssd
+uid://cit33bc2qquij
diff --git a/addons/gdUnit4/src/network/GdUnitTask.gd.uid b/addons/gdUnit4/src/network/GdUnitTask.gd.uid
index b9e21fa8..3ea3fe16 100644
--- a/addons/gdUnit4/src/network/GdUnitTask.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitTask.gd.uid
@@ -1 +1 @@
-uid://xe5jb81h1hfm
+uid://ccwk7neqtr2sn
diff --git a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid
index 6b9a73fe..118d5f3d 100644
--- a/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitTcpClient.gd.uid
@@ -1 +1 @@
-uid://cdqknt40422ej
+uid://c38gh6mfpnt83
diff --git a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid
index 272805ba..17912849 100644
--- a/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitTcpNode.gd.uid
@@ -1 +1 @@
-uid://cbkf0u7jrre8m
+uid://cjtx84t86jf1g
diff --git a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid
index e3d48020..8cee9f96 100644
--- a/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid
+++ b/addons/gdUnit4/src/network/GdUnitTcpServer.gd.uid
@@ -1 +1 @@
-uid://b5rg08xa3h6x3
+uid://cauxpc1sb4ptk
diff --git a/addons/gdUnit4/src/network/rpc/RPC.gd.uid b/addons/gdUnit4/src/network/rpc/RPC.gd.uid
index 7fc771c3..17163402 100644
--- a/addons/gdUnit4/src/network/rpc/RPC.gd.uid
+++ b/addons/gdUnit4/src/network/rpc/RPC.gd.uid
@@ -1 +1 @@
-uid://b78q0d0k2xhls
+uid://cedvqoiapts6d
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid
index 74ee5a9c..fb232793 100644
--- a/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid
+++ b/addons/gdUnit4/src/network/rpc/RPCClientConnect.gd.uid
@@ -1 +1 @@
-uid://dkro11eh12cxm
+uid://bwo4g7r6dlqm
diff --git a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid
index e096f60e..0bbae8a2 100644
--- a/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid
+++ b/addons/gdUnit4/src/network/rpc/RPCClientDisconnect.gd.uid
@@ -1 +1 @@
-uid://chdib4feehlsb
+uid://bt5xyiti7u805
diff --git a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid
index 23b84604..a7efa394 100644
--- a/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid
+++ b/addons/gdUnit4/src/network/rpc/RPCGdUnitEvent.gd.uid
@@ -1 +1 @@
-uid://bd5vwo3ck8v4c
+uid://cijkq1m75qmmv
diff --git a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid
index 8f3132ec..61f5bd4f 100644
--- a/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid
+++ b/addons/gdUnit4/src/network/rpc/RPCMessage.gd.uid
@@ -1 +1 @@
-uid://dengpjk16on7n
+uid://binsc1d6mcj5i
diff --git a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid
index 15b89628..de31a85a 100644
--- a/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid
+++ b/addons/gdUnit4/src/reporters/GdUnitConsoleTestReporter.gd.uid
@@ -1 +1 @@
-uid://duffxt287c2xv
+uid://yf1lwslmsr1p
diff --git a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid
index 1e086a98..f27ff085 100644
--- a/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid
+++ b/addons/gdUnit4/src/reporters/GdUnitHtmlTestReporter.gd.uid
@@ -1 +1 @@
-uid://ckpa3m7t165g2
+uid://b8r8buvfmxkc1
diff --git a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid
index df62c4d9..7478658c 100644
--- a/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid
+++ b/addons/gdUnit4/src/reporters/GdUnitTestReporter.gd.uid
@@ -1 +1 @@
-uid://cqyx7s50kvgit
+uid://bgihlovlje028
diff --git a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid b/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid
index 08b83e47..b6b68cac 100644
--- a/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid
+++ b/addons/gdUnit4/src/reporters/JUnitXmlReport.gd.uid
@@ -1 +1 @@
-uid://ckt5bbakcyu1m
+uid://fyna6rb2ko53
diff --git a/addons/gdUnit4/src/reporters/XmlElement.gd.uid b/addons/gdUnit4/src/reporters/XmlElement.gd.uid
index 670bd0ad..1a0d1894 100644
--- a/addons/gdUnit4/src/reporters/XmlElement.gd.uid
+++ b/addons/gdUnit4/src/reporters/XmlElement.gd.uid
@@ -1 +1 @@
-uid://cq27dcqhpeglp
+uid://dr0dtmu570ddy
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid
index c80e9b0f..5d41d20e 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitByPathReport.gd.uid
@@ -1 +1 @@
-uid://yo22m618tcs3
+uid://bxtk8ptaee4sy
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid
index e3e8a692..054298b6 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlPatterns.gd.uid
@@ -1 +1 @@
-uid://bd6iibim1mjbg
+uid://c7sqdlct08bsw
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid
index f01d91b3..e835ca32 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitHtmlReport.gd.uid
@@ -1 +1 @@
-uid://dlwag4dpcgbo8
+uid://dc5qieesw2531
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid
index 1be2ad00..389904a5 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitReportSummary.gd.uid
@@ -1 +1 @@
-uid://bmdjjeedfuqnf
+uid://dy757y4ltjjil
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid
index 8dab443f..1cf32fac 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitTestCaseReport.gd.uid
@@ -1 +1 @@
-uid://coymnbc7qpl3o
+uid://6amw43v1lk5r
diff --git a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid
index 0974ce65..6d3c1c09 100644
--- a/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid
+++ b/addons/gdUnit4/src/reporters/html/GdUnitTestSuiteReport.gd.uid
@@ -1 +1 @@
-uid://cvl6b5i5uhnci
+uid://dn118sseqeyl2
diff --git a/addons/gdUnit4/src/reporters/html/template/.gdignore b/addons/gdUnit4/src/reporters/html/template/.gdignore
new file mode 100644
index 00000000..e69de29b
diff --git a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid
index d8975049..200d36b4 100644
--- a/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid
+++ b/addons/gdUnit4/src/spy/GdUnitSpyBuilder.gd.uid
@@ -1 +1 @@
-uid://dw53ujxolkr5m
+uid://ppqfk5innaal
diff --git a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid
index d7803326..714bc89d 100644
--- a/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid
+++ b/addons/gdUnit4/src/spy/GdUnitSpyFunctionDoubler.gd.uid
@@ -1 +1 @@
-uid://drnkfb6u0ggpu
+uid://w22nuqoq7i4c
diff --git a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid
index 2ac39b47..4b9e7fa0 100644
--- a/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid
+++ b/addons/gdUnit4/src/spy/GdUnitSpyImpl.gd.uid
@@ -1 +1 @@
-uid://cytpqfrmm0pt3
+uid://dq2ct33cofsyl
diff --git a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid
index 3d53d9db..1aa118ca 100644
--- a/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid
+++ b/addons/gdUnit4/src/ui/GdUnitConsole.gd.uid
@@ -1 +1 @@
-uid://bc2216ij7fvk1
+uid://2i7agu7i0w68
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..939406ef 100644
--- a/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid
+++ b/addons/gdUnit4/src/ui/GdUnitFonts.gd.uid
@@ -1 +1 @@
-uid://dgf16dlm5ms6k
+uid://cao8sc3cwog8q
diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid
index 9bc99846..c907808b 100644
--- a/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid
+++ b/addons/gdUnit4/src/ui/GdUnitInspector.gd.uid
@@ -1 +1 @@
-uid://u4ddpnglc1kf
+uid://vothtltt41qt
diff --git a/addons/gdUnit4/src/ui/GdUnitInspector.tscn b/addons/gdUnit4/src/ui/GdUnitInspector.tscn
index 9b5a55fa..54ee9193 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
diff --git a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid
index 9e51e2d6..105cd856 100644
--- a/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid
+++ b/addons/gdUnit4/src/ui/GdUnitInspectorTreeConstants.gd.uid
@@ -1 +1 @@
-uid://s3jesnpl5w4
+uid://djkcwschqiupb
diff --git a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid
index 68288f5f..5578a48b 100644
--- a/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid
+++ b/addons/gdUnit4/src/ui/GdUnitUiTools.gd.uid
@@ -1 +1 @@
-uid://3w6n0vrjwmo8
+uid://htntluoxnikn
diff --git a/addons/gdUnit4/src/ui/ScriptEditorControls.gd b/addons/gdUnit4/src/ui/ScriptEditorControls.gd
index 5e07fe15..6f3590b2 100644
--- a/addons/gdUnit4/src/ui/ScriptEditorControls.gd
+++ b/addons/gdUnit4/src/ui/ScriptEditorControls.gd
@@ -54,9 +54,9 @@ static func save_an_open_script(script_path: String, close:=false) -> bool:
# select the script in the editor
EditorInterface.edit_script(open_script, 0);
# save and close
- editor_popup.id_pressed.emit(FILE_SAVE)
+ editor_popup.id_pressed.emit(Field.FILE_SAVE)
if close:
- editor_popup.id_pressed.emit(FILE_CLOSE)
+ editor_popup.id_pressed.emit(Field.FILE_CLOSE)
return true
return false
@@ -64,7 +64,7 @@ static func save_an_open_script(script_path: String, close:=false) -> bool:
# Saves all opened script
static func save_all_open_script() -> void:
if Engine.is_editor_hint():
- _menu_popup().id_pressed.emit(FILE_SAVE_ALL)
+ _menu_popup().id_pressed.emit(Field.FILE_SAVE_ALL)
static func close_open_editor_scripts() -> void:
diff --git a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid
index e1923cbc..a249f619 100644
--- a/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid
+++ b/addons/gdUnit4/src/ui/ScriptEditorControls.gd.uid
@@ -1 +1 @@
-uid://li3m7sxmxbog
+uid://cy3jltba1mub4
diff --git a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid
index 67d7dd23..af64f29e 100644
--- a/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid
+++ b/addons/gdUnit4/src/ui/menu/EditorFileSystemContextMenuHandler.gd.uid
@@ -1 +1 @@
-uid://7fd7477usa3w
+uid://bh6aoa1oac8pp
diff --git a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid
index 832ee47e..5a6c0bc5 100644
--- a/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid
+++ b/addons/gdUnit4/src/ui/menu/GdUnitContextMenuItem.gd.uid
@@ -1 +1 @@
-uid://bsdgpryghi78m
+uid://3p1xmguf0a4b
diff --git a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid
index 7ba54248..d8224efa 100644
--- a/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid
+++ b/addons/gdUnit4/src/ui/menu/ScriptEditorContextMenuHandler.gd.uid
@@ -1 +1 @@
-uid://dix5adosojsc
+uid://bo1xnst1353h5
diff --git a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid
index 908d8e01..c26b6d4a 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid
+++ b/addons/gdUnit4/src/ui/parts/InspectorMonitor.gd.uid
@@ -1 +1 @@
-uid://crw1egthht6dn
+uid://dji3ntx24jxpc
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.uid b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid
index ed4e4d1d..2f120448 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid
+++ b/addons/gdUnit4/src/ui/parts/InspectorProgressBar.gd.uid
@@ -1 +1 @@
-uid://b5sffcmr4tagj
+uid://b45snkt7caquf
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..f2623aeb 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd
+++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd
@@ -18,8 +18,8 @@ signal tree_view_mode_changed(flat :bool)
@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
diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid
index 5580160e..6bd8c139 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid
+++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.gd.uid
@@ -1 +1 @@
-uid://dvrxigojhylsy
+uid://o7pbyqyihbvb
diff --git a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn
index 182338b8..c6848272 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn
+++ b/addons/gdUnit4/src/ui/parts/InspectorStatusBar.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=32 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 = {
diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd
index 3407048d..e4d99e44 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd
+++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd
@@ -5,6 +5,8 @@ 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
@onready var _tool_button: Button = %tool
@@ -14,7 +16,6 @@ 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,
@@ -23,11 +24,16 @@ const SETTINGS_SHORTCUT_MAPPING := {
}
-@warning_ignore("return_value_discarded")
func _ready() -> void:
+ var inspector :InspectorTreeMainPanel = 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)
+ run_pressed.connect(inspector._on_run_pressed)
+
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)
@@ -37,7 +43,6 @@ func _ready() -> void:
init_shortcuts(command_handler)
-
func init_buttons() -> void:
_button_run_overall.icon = GdUnitUiTools.get_run_overall_icon()
_button_run_overall.visible = GdUnitSettings.is_inspector_toolbar_button_show()
@@ -46,6 +51,9 @@ func init_buttons() -> void:
_button_stop.icon = GdUnitUiTools.get_icon("Stop")
_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:
@@ -58,6 +66,12 @@ func init_shortcuts(command_handler: GdUnitCommandHandler) -> void:
GdUnitSignals.instance().gdunit_settings_changed.connect(_on_settings_changed.bind(command_handler))
+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_runoverall_pressed(debug:=false) -> void:
run_overall_pressed.emit(debug)
@@ -79,8 +93,6 @@ func _on_gdunit_runner_start() -> void:
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
diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid
index 2addee17..951f7463 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid
+++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.gd.uid
@@ -1 +1 @@
-uid://d311g1tcmdbtv
+uid://jwd8m4833smh
diff --git a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn
index 210fa9e3..5d75da02 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn
+++ b/addons/gdUnit4/src/ui/parts/InspectorToolBar.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=22 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 = {
diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd
index ac383025..fce16f98 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd
+++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd
@@ -3,6 +3,8 @@ extends VSplitContainer
## Will be emitted when the test index counter is changed
signal test_counters_changed(index: int, total: int, state: GdUnitInspectorTreeConstants.STATE)
+signal tree_item_selected(item: TreeItem)
+
const CONTEXT_MENU_RUN_ID = 0
const CONTEXT_MENU_DEBUG_ID = 1
@@ -61,8 +63,10 @@ const STATE = GdUnitInspectorTreeConstants.STATE
var _tree_root: TreeItem
+var _current_selected_item: TreeItem = null
var _item_hash := Dictionary()
var _current_tree_view_mode := GdUnitSettings.get_inspector_tree_view_mode()
+var _run_test_recovery := true
func _build_cache_key(resource_path: String, test_name: String) -> Array:
@@ -195,6 +199,11 @@ 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"))
@@ -215,6 +224,8 @@ func _ready() -> void:
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 +237,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)
@@ -255,6 +267,7 @@ func cleanup_tree() -> void:
return
_free_recursive()
_tree.clear()
+ _current_selected_item = null
func _free_recursive(items:=_tree_root.get_children()) -> void:
@@ -263,12 +276,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 +302,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)
-func sort_items_by_execution_time(a: TreeItem, b: TreeItem) -> bool:
+ return comparison < 0 if ascending else comparison > 0
+
+
+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 +348,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
@@ -530,11 +568,12 @@ func set_state_running(item: TreeItem) -> void:
if parent != _tree_root:
set_state_running(parent)
# force scrolling to current test case
- @warning_ignore("return_value_discarded")
- select_item(item)
+ _tree.scroll_to_item(item, true)
func set_state_succeded(item: TreeItem) -> void:
+ 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)
@@ -728,7 +767,7 @@ func show_failed_report(selected_item: TreeItem) -> void:
func update_test_suite(event: GdUnitEvent) -> void:
var item := _find_tree_item_by_path(extract_resource_path(event), event.suite_name())
if not item:
- push_error("[GdUnitInspector:731] Internal Error: Can't find tree item for\n %s" % event)
+ push_error("[InspectorTreeMainPanel.gd:753] Internal Error: Can't find tree item for\n %s" % event)
return
if event.type() == GdUnitEvent.TESTSUITE_BEFORE:
set_state_running(item)
@@ -742,7 +781,7 @@ func update_test_suite(event: GdUnitEvent) -> void:
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)
@@ -766,10 +805,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))
+ item.set_meta(META_TEST_CASE, GdUnitTestCase.from(test.suite_resource_path, 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)
@@ -1077,7 +1116,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
@@ -1124,6 +1163,8 @@ func _on_Tree_item_selected() -> void:
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()
+ tree_item_selected.emit(_current_selected_item)
# Opens the test suite
@@ -1153,7 +1194,7 @@ func _on_Tree_item_activated() -> void:
# external signal receiver
################################################################################
func _on_gdunit_runner_start() -> void:
- reset_tree_state(_tree_root)
+ reset_tree_state(_current_selected_item)
_context_menu.set_item_disabled(CONTEXT_MENU_RUN_ID, true)
_context_menu.set_item_disabled(CONTEXT_MENU_DEBUG_ID, true)
clear_reports()
@@ -1179,6 +1220,7 @@ 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")
@@ -1188,6 +1230,7 @@ func _on_gdunit_event(event: GdUnitEvent) -> void:
GdUnitEvent.STOP:
sort_tree_items(_tree_root)
+ select_item(_current_selected_item)
#_dump_tree_as_json("tree_example")
GdUnitEvent.TESTCASE_BEFORE:
@@ -1219,7 +1262,7 @@ 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..356d0f16 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid
+++ b/addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd.uid
@@ -1 +1 @@
-uid://2io2vddkuoqf
+uid://b1wycqsf5vf04
diff --git a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn
index 7d7af9b4..a4247706 100644
--- a/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn
+++ b/addons/gdUnit4/src/ui/parts/InspectorTreePanel.tscn
@@ -1,6 +1,6 @@
[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"]
+[ext_resource type="Script" path="res://addons/gdUnit4/src/ui/parts/InspectorTreeMainPanel.gd" id="1"]
[sub_resource type="Image" id="Image_466oo"]
data = {
diff --git a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid
index 8b2dd786..b123c8f2 100644
--- a/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid
+++ b/addons/gdUnit4/src/ui/settings/GdUnitInputCapture.gd.uid
@@ -1 +1 @@
-uid://djjujnonaiy34
+uid://c3unwpwya10b5
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..49da37f8 100644
--- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid
+++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.gd.uid
@@ -1 +1 @@
-uid://bbmloatmyvou5
+uid://bf71hux0t1te6
diff --git a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn
index 96f1c0b0..ba732ad1 100644
--- a/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn
+++ b/addons/gdUnit4/src/ui/settings/GdUnitSettingsDialog.tscn
@@ -1,11 +1,11 @@
[gd_scene load_steps=8 format=3 uid="uid://dwgat6j2u77g4"]
-[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/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
diff --git a/addons/gdUnit4/src/ui/settings/logo.png.import b/addons/gdUnit4/src/ui/settings/logo.png.import
index c2a071e8..2cb878c0 100644
--- a/addons/gdUnit4/src/ui/settings/logo.png.import
+++ b/addons/gdUnit4/src/ui/settings/logo.png.import
@@ -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..7b1034e6 100644
--- a/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid
+++ b/addons/gdUnit4/src/ui/templates/TestSuiteTemplate.gd.uid
@@ -1 +1 @@
-uid://b8ypatydap4v6
+uid://bcnywkpktlnqy
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..0d04a4ef 100644
--- a/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid
+++ b/addons/gdUnit4/src/update/GdMarkDownReader.gd.uid
@@ -1 +1 @@
-uid://c0pbiv6wn5iiu
+uid://dj3esct7kbp36
diff --git a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid
index 76bf4f50..111ab472 100644
--- a/addons/gdUnit4/src/update/GdUnitPatch.gd.uid
+++ b/addons/gdUnit4/src/update/GdUnitPatch.gd.uid
@@ -1 +1 @@
-uid://yfl8yfxw73e1
+uid://bxiyaaxrv5obm
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..80781f6c 100644
--- a/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid
+++ b/addons/gdUnit4/src/update/GdUnitPatcher.gd.uid
@@ -1 +1 @@
-uid://cdcl6oy7t8r2a
+uid://cy5bcbbj383x1
diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd b/addons/gdUnit4/src/update/GdUnitUpdate.gd
index 1b492fe8..ef878151 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")
@@ -91,6 +93,9 @@ func run_update() -> void:
else:
copy_directory(tmp_path, "res://")
+ await update_progress("Patch invalid UID's")
+ await patch_uids()
+
await update_progress("New GdUnit version successfully installed, Restarting Godot please wait.")
await get_tree().create_timer(3).timeout
enable_gdUnit()
@@ -99,6 +104,60 @@ func run_update() -> void:
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)
diff --git a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid
index ebd02b68..6167660c 100644
--- a/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid
+++ b/addons/gdUnit4/src/update/GdUnitUpdate.gd.uid
@@ -1 +1 @@
-uid://yq8po6gm4kqs
+uid://b4820gymnwsmc
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..5cf7da1e 100644
--- a/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid
+++ b/addons/gdUnit4/src/update/GdUnitUpdateClient.gd.uid
@@ -1 +1 @@
-uid://2lrcgo0tmnlo
+uid://dhi05b5jk225i
diff --git a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid
index 4b7e5900..d401f804 100644
--- a/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid
+++ b/addons/gdUnit4/src/update/GdUnitUpdateNotify.gd.uid
@@ -1 +1 @@
-uid://cje1afd65l33e
+uid://w4jy3sjetpin
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..0054a84f 100644
--- a/addons/gdUnit4/src/update/assets/border_bottom.png.import
+++ b/addons/gdUnit4/src/update/assets/border_bottom.png.import
@@ -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..0b748c72 100644
--- a/addons/gdUnit4/src/update/assets/border_top.png.import
+++ b/addons/gdUnit4/src/update/assets/border_top.png.import
@@ -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..fa76ec88 100644
--- a/addons/gdUnit4/src/update/assets/dot1.png.import
+++ b/addons/gdUnit4/src/update/assets/dot1.png.import
@@ -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..de06952e 100644
--- a/addons/gdUnit4/src/update/assets/dot2.png.import
+++ b/addons/gdUnit4/src/update/assets/dot2.png.import
@@ -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..06f69325 100644
--- a/addons/gdUnit4/src/update/assets/embedded.png.import
+++ b/addons/gdUnit4/src/update/assets/embedded.png.import
@@ -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..8755fd87 100644
--- a/addons/gdUnit4/src/update/assets/horizontal-line2.png.import
+++ b/addons/gdUnit4/src/update/assets/horizontal-line2.png.import
@@ -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..35dac993 100644
--- a/addons/monologue/assets/Node.svg.import
+++ b/addons/monologue/assets/Node.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/addons/monologue/assets/RichTextLabel.svg.import b/addons/monologue/assets/RichTextLabel.svg.import
index 41e94928..942e4352 100644
--- a/addons/monologue/assets/RichTextLabel.svg.import
+++ b/addons/monologue/assets/RichTextLabel.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/autoloads/constants.gd b/autoloads/constants.gd
index 3ce56ef1..eb926f14 100644
--- a/autoloads/constants.gd
+++ b/autoloads/constants.gd
@@ -2,24 +2,24 @@ 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")
+#"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"
diff --git a/autoloads/storyline_manager.gd b/autoloads/storyline_manager.gd
new file mode 100644
index 00000000..75642550
--- /dev/null
+++ b/autoloads/storyline_manager.gd
@@ -0,0 +1,59 @@
+extends Node
+
+signal storyline_created
+signal storyline_closed
+signal storyline_changed
+signal storyline_switched
+
+var _documents: Dictionary = {}
+var _active_document_id: String
+
+
+func create_storyline(fname: String = "unnamed_storyline") -> StorylineDocument:
+ var doc: StorylineDocument = StorylineDocument.new(fname)
+ doc.is_dirty = true
+ doc.content_changed.connect(_on_document_content_changed)
+ _documents.set(doc.id, doc)
+ set_active_storyline(doc.id)
+ storyline_created.emit()
+ if _documents.size() <= 1:
+ storyline_switched.emit()
+ return doc
+
+
+func close_storyline(id: String) -> void:
+ var doc = _documents.get(id, null)
+ if doc == null:
+ return
+ if doc.is_dirty:
+ # Save changes ?
+ return
+ _documents.erase(id)
+
+ if _active_document_id == id:
+ var remaining = _documents.keys()
+ if remaining.size() > 0:
+ _active_document_id = remaining[0]
+ else:
+ _active_document_id = ""
+ storyline_closed.emit()
+
+
+func set_active_storyline(id: String) -> void:
+ _active_document_id = id
+
+
+func get_storyline(id: String) -> StorylineDocument:
+ return _documents.get(id, null)
+
+
+func get_storyline_ids() -> Array:
+ return _documents.keys()
+
+
+func get_active_storyline() -> StorylineDocument:
+ return _documents.get(_active_document_id)
+
+
+func _on_document_content_changed() -> void:
+ storyline_changed.emit()
diff --git a/autoloads/storyline_manager.gd.uid b/autoloads/storyline_manager.gd.uid
new file mode 100644
index 00000000..ed977378
--- /dev/null
+++ b/autoloads/storyline_manager.gd.uid
@@ -0,0 +1 @@
+uid://dabxmafwjrhkv
diff --git a/autoloads/theme_manager.gd b/autoloads/theme_manager.gd
new file mode 100644
index 00000000..25aa3df6
--- /dev/null
+++ b/autoloads/theme_manager.gd
@@ -0,0 +1,65 @@
+extends Node
+## Manages theme generation, saving, and application for the Monologue application
+
+var current_theme: Theme
+var current_settings: ThemeSettings
+var is_light_theme: bool = false
+
+
+func _ready() -> void:
+ generate_and_apply_theme(false)
+
+
+## Generate a theme from a settings and apply it
+func generate_and_apply_theme(light: bool = false) -> void:
+ is_light_theme = light
+
+ if is_light_theme:
+ current_settings = ThemeSettingsLight.new()
+ else:
+ current_settings = ThemeSettingsDark.new()
+
+ current_theme = ThemeBuilder.build_theme(current_settings)
+
+ if not current_theme:
+ push_error("Failed to generate theme")
+ return
+
+ apply_theme_to_tree(current_theme)
+ print("Theme generated and applied: %s" % ("Light" if light else "Dark"))
+
+
+## Save the current theme to disk
+func save_current_theme() -> void:
+ if not current_theme:
+ push_error("No theme to save")
+ return
+
+ var theme_path = current_settings.get_path()
+ var err = ResourceSaver.save(current_theme, theme_path)
+
+ if err == OK:
+ print("Theme saved to: %s" % theme_path)
+ else:
+ push_error("Failed to save theme to: %s (error %d)" % [theme_path, err])
+
+
+## Apply theme to all Control nodes in the scene tree
+func apply_theme_to_tree(theme: Theme) -> void:
+ var root: Window = get_tree().root
+ root.set_theme(theme)
+
+
+## Switch between light and dark themes
+func toggle_theme() -> void:
+ generate_and_apply_theme(not is_light_theme)
+
+
+## Get the current theme
+func get_current_theme() -> Theme:
+ return current_theme
+
+
+## Get the current settings
+func get_current_settings() -> RefCounted:
+ return current_settings
diff --git a/autoloads/theme_manager.gd.uid b/autoloads/theme_manager.gd.uid
new file mode 100644
index 00000000..5087d8ac
--- /dev/null
+++ b/autoloads/theme_manager.gd.uid
@@ -0,0 +1 @@
+uid://b0c887r1lq57j
diff --git a/autoloads/undo_redo_service.gd b/autoloads/undo_redo_service.gd
new file mode 100644
index 00000000..1db57981
--- /dev/null
+++ b/autoloads/undo_redo_service.gd
@@ -0,0 +1,36 @@
+# UndoRedoService
+extends Node
+
+const MAX_STEPS: int = 100
+
+var contexts: Dictionary = {}
+var active_context_id: String = ""
+
+
+func create_context(context_id: String) -> HistoryHandler:
+ if contexts.has(context_id):
+ return contexts[context_id]
+
+ var undo_redo: HistoryHandler = HistoryHandler.new()
+ undo_redo.max_steps = MAX_STEPS
+ contexts[context_id] = undo_redo
+ return undo_redo
+
+
+func get_context(context_id: String) -> HistoryHandler:
+ return contexts.get(context_id, null)
+
+
+func destroy_context(context_id: String) -> void:
+ if not contexts.has(context_id):
+ return
+
+ var undo_redo: HistoryHandler = contexts[context_id]
+ undo_redo.clear_history()
+ contexts.erase(undo_redo)
+
+
+func get_active_context() -> HistoryHandler:
+ if active_context_id.is_empty():
+ return null
+ return get_context(active_context_id)
diff --git a/autoloads/undo_redo_service.gd.uid b/autoloads/undo_redo_service.gd.uid
new file mode 100644
index 00000000..a7d4ddb5
--- /dev/null
+++ b/autoloads/undo_redo_service.gd.uid
@@ -0,0 +1 @@
+uid://cl3x7l44eayh3
diff --git a/autoloads/util.gd b/autoloads/util.gd
index 3aa06a58..861b75f2 100644
--- a/autoloads/util.gd
+++ b/autoloads/util.gd
@@ -49,6 +49,10 @@ func to_key_name(snake_case_name: String, delimiter: String = "") -> String:
for word 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".
+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.
diff --git a/bilUDX.png.import b/bilUDX.png.import
index b45c9584..3cb032c9 100644
--- a/bilUDX.png.import
+++ b/bilUDX.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/bilUDX.png-4b799bae1c669628ee420ba80d3eba36.c
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/common/assets/logo/logo_256x256.png.import b/common/assets/logo/logo_256x256.png.import
index 94ba4b54..12412049 100644
--- a/common/assets/logo/logo_256x256.png.import
+++ b/common/assets/logo/logo_256x256.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo_256x256.png-c64139f54d35de8927bbaa3e9e2c
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/common/assets/logo/logo_256x256.svg.import b/common/assets/logo/logo_256x256.svg.import
index 3cd5ed55..4c711d6c 100644
--- a/common/assets/logo/logo_256x256.svg.import
+++ b/common/assets/logo/logo_256x256.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://exl7lwdbgxr2"
-path="res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.dpitex"
[deps]
source_file="res://common/assets/logo/logo_256x256.svg"
-dest_files=["res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.ctex"]
+dest_files=["res://.godot/imported/logo_256x256.svg-af415b1c6dd0fa173899415407767412.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={}
+compress=true
diff --git a/common/assets/logo/logo_32x32.png.import b/common/assets/logo/logo_32x32.png.import
index ab1f6931..58d4c850 100644
--- a/common/assets/logo/logo_32x32.png.import
+++ b/common/assets/logo/logo_32x32.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo_32x32.png-a9ec36e08c728751a55ec37b054696
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/common/assets/logo/logo_32x32.svg.import b/common/assets/logo/logo_32x32.svg.import
index 1ed2486e..3c2ce479 100644
--- a/common/assets/logo/logo_32x32.svg.import
+++ b/common/assets/logo/logo_32x32.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://b35w6abcbrbvw"
-path="res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.dpitex"
[deps]
source_file="res://common/assets/logo/logo_32x32.svg"
-dest_files=["res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.ctex"]
+dest_files=["res://.godot/imported/logo_32x32.svg-13a21cb5dc383e97ea1060616438d51c.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={}
+compress=true
diff --git a/common/assets/logo/logo_512x512.png.import b/common/assets/logo/logo_512x512.png.import
index 2700cb6e..99c047db 100644
--- a/common/assets/logo/logo_512x512.png.import
+++ b/common/assets/logo/logo_512x512.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo_512x512.png-dac42d53bf3ffaa08235f733c584
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/common/assets/logo/logo_512x512.svg.import b/common/assets/logo/logo_512x512.svg.import
index f3e3fe43..cec06eb2 100644
--- a/common/assets/logo/logo_512x512.svg.import
+++ b/common/assets/logo/logo_512x512.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://d3icwpvi8vr6x"
-path="res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.dpitex"
[deps]
source_file="res://common/assets/logo/logo_512x512.svg"
-dest_files=["res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.ctex"]
+dest_files=["res://.godot/imported/logo_512x512.svg-d52f6f4fda3afc8cd27c75d996e2ed6b.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={}
+compress=true
diff --git a/common/assets/logo/logo_72x72.png.import b/common/assets/logo/logo_72x72.png.import
index 6ea0d2b0..ca6d1efd 100644
--- a/common/assets/logo/logo_72x72.png.import
+++ b/common/assets/logo/logo_72x72.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/logo_72x72.png-053db1a5547f3fbe3dc44285da1ce4
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/common/assets/logo/logo_72x72.svg.import b/common/assets/logo/logo_72x72.svg.import
index b9b55c67..23a3977f 100644
--- a/common/assets/logo/logo_72x72.svg.import
+++ b/common/assets/logo/logo_72x72.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://bn75u1jdkiwu3"
-path="res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.dpitex"
[deps]
source_file="res://common/assets/logo/logo_72x72.svg"
-dest_files=["res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.ctex"]
+dest_files=["res://.godot/imported/logo_72x72.svg-73f50997eb81123373b48dd50423d36e.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={}
+compress=true
diff --git a/common/connection_manager.gd b/common/connection_manager.gd
new file mode 100644
index 00000000..f8cb2608
--- /dev/null
+++ b/common/connection_manager.gd
@@ -0,0 +1,352 @@
+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
+
+
+## Updates property connection tracking when a connection is made (using property names)
+## All parameters are node_ids, not scene node names.
+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
+
+ var from_prop: Property = from_node.get_property(from_property_name)
+ var to_prop: Property = to_node.get_property(to_property_name)
+
+ if not from_prop or not to_prop:
+ push_warning("Cannot register connection: property not found")
+ return
+
+ # Track the connection in both properties using property names
+ from_prop.add_connection_to(to_node_id, to_property_name)
+ to_prop.add_connection_from(from_node_id, from_property_name)
+
+
+## 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 = _get_node_view_by_id(from_node_id)
+ var to_node = _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 = _get_property_at_port(from_node, from_port)
+ var to_prop = _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.
+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 = _get_node_view_by_id(from_node_id)
+ var to_node = _get_node_view_by_id(to_node_id)
+
+ if not from_node or not to_node:
+ return
+
+ var from_prop = from_node.get_property(from_property_name)
+ var to_prop = to_node.get_property(to_property_name)
+
+ if not from_prop or not to_prop:
+ return
+
+ # Remove the connection from both properties using property names
+ from_prop.connected_to = from_prop.connected_to.filter(
+ func(c): return not (c["node_id"] == to_node_id and c["property_name"] == to_property_name)
+ )
+ to_prop.connected_from = to_prop.connected_from.filter(
+ func(c):
+ return not (c["node_id"] == from_node_id and c["property_name"] == from_property_name)
+ )
+
+
+## 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 = _get_node_view_by_id(from_node_id)
+ var to_node = _get_node_view_by_id(to_node_id)
+
+ if not from_node or not to_node:
+ return
+
+ var from_prop = _get_property_at_port(from_node, from_port)
+ var to_prop = _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 = {} # 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 in prop.connected_to:
+ var key = (
+ "%s.%s->%s.%s" % [node_id, prop.name, conn["node_id"], conn["property_name"]]
+ )
+ if key not in processed_connections:
+ connections.append(
+ {
+ "from_node_id": node_id,
+ "from_property": prop.name,
+ "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 = 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 = 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(connection["node_id"])
+
+ return null
+
+
+## Validates all connections are still valid (nodes and properties exist)
+func validate_connections() -> bool:
+ var all_valid = true
+ for node: InspectableNode in _storyline.nodes:
+ for prop: Property in node.get_properties():
+ # Check outgoing connections
+ for conn in prop.connected_to:
+ var target_node = _get_node_view_by_id(conn["node_id"])
+ if not target_node or not target_node.get_property(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 in prop.connected_from:
+ var source_node = _get_node_view_by_id(conn["node_id"])
+ if not source_node or not source_node.get_property(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():
+ for i in range(prop.connected_to.size()):
+ var conn: Dictionary = prop.connected_to[i]
+ if conn.get("node_id", "") == old_id:
+ conn["node_id"] = new_id
+ prop.connected_to[i] = conn
+ for i in range(prop.connected_from.size()):
+ var conn_in: Dictionary = prop.connected_from[i]
+ if conn_in.get("node_id", "") == old_id:
+ conn_in["node_id"] = new_id
+ prop.connected_from[i] = conn_in
+
+ # Update suspended connection snapshots
+ var keys := _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 := "%s::%s" % [new_id, parts[1]]
+ var snapshot: Dictionary = _suspended_connections[k]
+ # Update inner arrays
+ for dir in ["incoming", "outgoing"]:
+ if snapshot.has(dir):
+ var arr: Array = snapshot[dir]
+ for i in range(arr.size()):
+ var c: Dictionary = arr[i]
+ if c.get("node_id", "") == old_id:
+ c["node_id"] = new_id
+ arr[i] = c
+ snapshot[dir] = arr
+ _suspended_connections.erase(k)
+ _suspended_connections[new_key] = snapshot
+
+
+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 := _get_node_view_by_id(node_id)
+ if not node:
+ return
+
+ var prop := node.get_property(property_name)
+ if not prop:
+ return
+
+ var connections: Array[Dictionary] = prop.get(connection_array_name).duplicate(true)
+ if connections.is_empty():
+ return
+
+ _suspended_connections[key][direction] = connections
+
+ # Unregister each connection
+ for conn_dict 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 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 properties = node.get_properties()
+ var visible_props: Array[Property] = []
+
+ # Build list of visible properties in graph (matching graph display logic)
+ for prop: Property in properties:
+ if not prop.get_settings_value("visible_in_graph", true):
+ continue
+ var has_input = prop.get_settings_value("exposed", false)
+ var has_output = prop.get_settings_value("export", false)
+ if not has_input and not has_output:
+ continue
+
+ # Main property goes first
+ if prop.get_settings_value("is_main_property"):
+ visible_props.push_front(prop)
+ else:
+ visible_props.append(prop)
+
+ 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/graph_node_row.gd b/common/graph_node_row.gd
new file mode 100644
index 00000000..5abfad4d
--- /dev/null
+++ b/common/graph_node_row.gd
@@ -0,0 +1,23 @@
+class_name GraphNodeRow
+
+var _key: String = ""
+var _type: String = ""
+var _enable_left_port: bool = false
+var _enable_right_port: bool = false
+
+
+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
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/inspectable_node.gd b/common/inspectable_node.gd
new file mode 100644
index 00000000..3b8fdf35
--- /dev/null
+++ b/common/inspectable_node.gd
@@ -0,0 +1,166 @@
+@abstract
+class_name InspectableNode extends InspectableObject
+
+const ID_LENGTH: int = 6
+
+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"
+ )
+ define_property(
+ "id",
+ IDGen.generate(ID_LENGTH),
+ "text",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": true,
+ "flat": true,
+ },
+ "Special:Header"
+ )
+ # Keep all connection references in sync when the id changes
+ var _id_prop := 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",
+ Vector2.ZERO,
+ "vector2",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": false,
+ "editable": 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 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(old_value)
+ var new_id := String(new_value)
+ if old_id == new_id:
+ return
+
+ # Update all connection references via connection manager if available
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ if storyline:
+ # Try to find a graph_edit and its connection manager
+ var graph_edit := graph_view.get_parent() if is_instance_valid(graph_view) else null
+ if graph_edit and graph_edit is MonologueGraphEdit and graph_edit.connection_manager:
+ graph_edit.connection_manager.rename_node_id(old_id, new_id)
+ else:
+ # Fallback: direct update across storyline
+ for node: InspectableNode in storyline.nodes:
+ for prop: Property in node.get_properties():
+ # Update outgoing connections
+ for i in range(prop.connected_to.size()):
+ var conn: Dictionary = prop.connected_to[i]
+ if conn.get("node_id", "") == old_id:
+ conn["node_id"] = new_id
+ prop.connected_to[i] = conn
+ # Update incoming connections
+ for i in range(prop.connected_from.size()):
+ var conn_in: Dictionary = prop.connected_from[i]
+ if conn_in.get("node_id", "") == old_id:
+ conn_in["node_id"] = new_id
+ prop.connected_from[i] = conn_in
+
+ # 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 := 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 += Vector2(30, 30)
+
+ return duplicated
+
+
+@abstract func get_color() -> Color
+@abstract func get_icon() -> Texture2D
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..ac039d0a
--- /dev/null
+++ b/common/inspectable_object.gd
@@ -0,0 +1,148 @@
+@abstract
+class_name InspectableObject extends Resource
+
+var _properties: Dictionary[String, Property] = {}
+var _observers: Array[Callable] = []
+var history: CommandManager
+var settings: Dictionary = {}
+
+
+func _init(command_manager: CommandManager = null) -> void:
+ if not command_manager:
+ push_warning("InspectableObject does not have a command manager.")
+ history = command_manager
+
+ 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, _new): _notify_change(pname))
+
+
+func add_observer(callable: Callable) -> void:
+ if callable in _observers:
+ push_warning("Observer is already registered.")
+ return
+
+ _observers.append(callable)
+
+
+func remove_observer(callable: Callable) -> void:
+ _observers.erase(callable)
+
+
+func _notify_change(pname: String) -> void:
+ for observer: Callable in _observers:
+ if not observer or not observer.is_valid() or observer.get_object() == null:
+ _observers.erase(observer)
+ continue
+ observer.call(self, pname)
+
+
+func get_properties() -> Array[Property]:
+ var properties: Array[Property] = []
+ for pname 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:
+ return get_property(pname).get_value()
+
+
+func get_property_settings_value(pname: String, skey: String) -> Variant:
+ var property: Property = get_property(pname)
+ return property.settings.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 pvalue == old_value:
+ return
+
+ var command: PropertyChangeCommand = PropertyChangeCommand.new(self, pname, old_value, pvalue)
+ history.execute(command)
+
+ _notify_change(pname)
+
+
+func set_property_settings_value(pname: String, skey: Variant, 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 _to_dict() -> Dictionary:
+ var dict: Dictionary = {"$type": get_type()}
+ for property: Property in get_properties():
+ dict[property.name] = property._to_dict()
+
+ return dict
+
+
+# Do not trigger undo/redo
+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 {}
+
+
+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
+@abstract func _on_property_changed(pname: String, old_value: Variant, new_value: Variant) -> 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/inspectable_storyline_object.gd b/common/inspectable_storyline_object.gd
new file mode 100644
index 00000000..db84e46e
--- /dev/null
+++ b/common/inspectable_storyline_object.gd
@@ -0,0 +1,9 @@
+@abstract
+class_name InspectableStorylineObject extends InspectableObject
+
+
+func _init(command_manager: CommandManager = null) -> void:
+ super._init(command_manager)
+
+
+@abstract func build_graph_preview() -> Array[Control]
diff --git a/common/inspectable_storyline_object.gd.uid b/common/inspectable_storyline_object.gd.uid
new file mode 100644
index 00000000..6015b309
--- /dev/null
+++ b/common/inspectable_storyline_object.gd.uid
@@ -0,0 +1 @@
+uid://c4pgnm3s0fk7m
diff --git a/common/layout_manager.gd b/common/layout_manager.gd
new file mode 100644
index 00000000..85732a3e
--- /dev/null
+++ b/common/layout_manager.gd
@@ -0,0 +1,191 @@
+class_name LayoutManager
+
+
+static func create_layout(
+ schema: Dictionary,
+ item: ListItemObject,
+ list_field: ListField,
+ layout_name: String = "default",
+) -> Control:
+ var layout_config = _get_layout_config(schema, layout_name)
+
+ var vbox = VBoxContainer.new()
+ var fields = _get_fields_to_display(schema, layout_config)
+ var properties = schema.get("properties", {})
+ var index = list_field.get_item_index(item)
+
+ for field_name in fields:
+ if not properties.has(field_name):
+ continue
+
+ var field_container = _create_field_container(
+ field_name, properties[field_name], list_field, item
+ )
+
+ vbox.add_child(field_container)
+
+ _create_item_header(vbox, index, list_field, item, layout_config)
+
+ return vbox
+
+
+static func _get_layout_config(schema: Dictionary, layout_name: String) -> Dictionary:
+ var layouts = schema.get("layouts", {})
+ return layouts.get(layout_name, layouts.get("default", {}))
+
+
+static func _create_field_container(
+ field_name: String, field_config: Dictionary, _list_field: ListField, item: ListItemObject
+) -> Control:
+ var container: VBoxContainer = VBoxContainer.new()
+
+ var title_container: HBoxContainer = _create_title_container(field_name, field_config)
+ container.add_child(title_container)
+
+ var field: Field = _create_and_configure_field(field_name, field_config, item)
+ container.add_child(field)
+
+ var property: Property = item.get_property(field_name)
+ if property:
+ var merged_settings: Dictionary = field_config.duplicate()
+ merged_settings.merge(property.get_settings(), true)
+ field.settings = merged_settings
+
+ _bind_field_to_property.call_deferred(field, field_name, item)
+
+ var is_editable: bool = not (field_config.get("protect", false) and item.is_protected())
+ field.set_editable.call_deferred(is_editable)
+
+ return container
+
+
+static func _create_title_container(field_name: String, field_config: Dictionary) -> HBoxContainer:
+ var title_container = HBoxContainer.new()
+ title_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+ var label = Label.new()
+ label.text = Util.to_readable_name(field_name)
+ label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+ if field_config.has("tooltip"):
+ label.tooltip_text = field_config["tooltip"]
+
+ title_container.add_child(label)
+ return title_container
+
+
+static func _create_and_configure_field(
+ _field_name: String, field_config: Dictionary, _item: ListItemObject
+) -> Field:
+ var field_type: String = field_config.get("type")
+ var field: Field = FieldBucket.safe_create_field(field_type)
+
+ if field is Field:
+ field.settings = field_config.duplicate()
+ field.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+ return field
+
+
+static func _bind_field_to_property(field: Field, field_name: String, item: ListItemObject) -> void:
+ var property = item.get_property(field_name)
+ if not property:
+ push_warning("Property '%s' not found in item" % field_name)
+ return
+
+ if field.is_node_ready():
+ property.bind_field(field, item)
+ return
+ field.ready.connect(func(): property.bind_field(field, item), CONNECT_ONE_SHOT)
+
+
+static func _get_fields_to_display(schema: Dictionary, layout_config: Dictionary) -> Array:
+ var properties = schema.get("properties", {})
+ return layout_config.get("fields", properties.keys())
+
+
+static func _create_item_header(
+ content: Control,
+ index: int,
+ list_field: ListField,
+ item: ListItemObject,
+ layout_config: Dictionary
+) -> Control:
+ var header = _get_or_create_header_container(content)
+ var actions = layout_config.get("actions", ["delete"])
+
+ _add_action_buttons(header, actions, index, list_field, item)
+
+ return header
+
+
+static func _get_or_create_header_container(content: Control) -> HBoxContainer:
+ if content.get_child_count() == 0:
+ return HBoxContainer.new()
+
+ var first_child = content.get_child(0)
+ if not first_child is BoxContainer or first_child.get_child_count() == 0:
+ return HBoxContainer.new()
+
+ var header_candidate = first_child.get_child(0)
+ if header_candidate is HBoxContainer:
+ return header_candidate
+
+ return HBoxContainer.new()
+
+
+static func _add_action_buttons(
+ header: HBoxContainer, actions: Array, index: int, list_field: ListField, item: ListItemObject
+) -> void:
+ if "edit" in actions:
+ _add_button(
+ header,
+ index,
+ list_field,
+ "edit",
+ "Edit item",
+ preload("res://ui/assets/icons/pen.svg"),
+ )
+
+ if "duplicate" in actions and not item.is_protected():
+ _add_button(
+ header,
+ index,
+ list_field,
+ "duplicate",
+ "Duplicate item",
+ preload("res://ui/assets/icons/copy.png"),
+ )
+
+ if "delete" in actions and not item.is_protected():
+ _add_button(
+ header,
+ index,
+ list_field,
+ "delete",
+ "Delete item",
+ preload("res://ui/assets/icons/trash.svg"),
+ )
+
+
+static func _add_button(
+ header: HBoxContainer,
+ index: int,
+ list_field: ListField,
+ action_name: String,
+ tooltip: String,
+ icon: Texture2D
+) -> void:
+ if not list_field.has_method("_on_%s_item" % action_name):
+ return
+
+ var button = Button.new()
+ button.icon = icon
+ button.tooltip_text = tooltip
+ button.pressed.connect(list_field.call.bind("_on_%s_item" % action_name, index))
+ header.add_child(button)
+
+
+static func _resolve_dynamic_enum(_path: String, _data: Dictionary) -> Array:
+ # TODO: Implement full resolution for dynamic enums
+ return []
diff --git a/common/layout_manager.gd.uid b/common/layout_manager.gd.uid
new file mode 100644
index 00000000..5ccc685f
--- /dev/null
+++ b/common/layout_manager.gd.uid
@@ -0,0 +1 @@
+uid://mm6khj33mtil
diff --git a/common/layouts/character_edit/assets/triangle_pointer.svg.import b/common/layouts/character_edit/assets/triangle_pointer.svg.import
index 5d5ecb14..573aa9dc 100644
--- a/common/layouts/character_edit/assets/triangle_pointer.svg.import
+++ b/common/layouts/character_edit/assets/triangle_pointer.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://mjb34j4suabp"
-path="res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.dpitex"
[deps]
source_file="res://common/layouts/character_edit/assets/triangle_pointer.svg"
-dest_files=["res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.ctex"]
+dest_files=["res://.godot/imported/triangle_pointer.svg-1a5e452f0eac195ecf23f23e96dbf3b7.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={}
+compress=true
diff --git a/common/layouts/character_edit/character_edit.gd b/common/layouts/character_edit/character_edit.gd
index 87f59a03..e1e52152 100644
--- a/common/layouts/character_edit/character_edit.gd
+++ b/common/layouts/character_edit/character_edit.gd
@@ -1,70 +1,70 @@
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.
+#
+#var nicknames := OldProperty.new(Field.LINE)
+#var display_name := OldProperty.new(Field.LINE)
+#var description := OldProperty.new(Field.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.value[index])
+#show()
+#else:
+#close()
+#
+#
+#func close() -> void:
+## also triggered when 'Done' button is pressed
+#if graph_edit:
+#graph_edit.characters.value[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.tscn b/common/layouts/character_edit/character_edit.tscn
index 1894c18a..0f70202b 100644
--- a/common/layouts/character_edit/character_edit.tscn
+++ b/common/layouts/character_edit/character_edit.tscn
@@ -194,7 +194,7 @@ stretch = true
disable_3d = true
transparent_bg = true
handle_input_locally = false
-size = Vector2i(2, 2)
+size = Vector2i(366, 251)
size_2d_override_stretch = true
render_target_update_mode = 4
diff --git a/common/layouts/character_edit/character_edit_section.gd b/common/layouts/character_edit/character_edit_section.gd
index 9d06c5c2..e169f2b8 100644
--- a/common/layouts/character_edit/character_edit_section.gd
+++ b/common/layouts/character_edit/character_edit_section.gd
@@ -49,30 +49,30 @@ func set_index(index: int) -> void:
section.character_index = index
-func _from_dict(dict: Dictionary) -> void:
+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)
+ #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
+ #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/portrait_list_section.gd b/common/layouts/character_edit/portrait_list_section.gd
index 4974edb3..437a4038 100644
--- a/common/layouts/character_edit/portrait_list_section.gd
+++ b/common/layouts/character_edit/portrait_list_section.gd
@@ -1,171 +1,171 @@
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)
+#
+#signal portrait_selected
+#
+#const DEFAULT_PORTRAIT_NAME = "new portrait %s"
+#
+#var portraits := OldProperty.new(Field.LIST, {}, [])
+#var default_portrait := OldProperty.new(Field.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.value[character_index]["Character"]
+ #portrait_settings_section._from_dict(character_dict)
diff --git a/common/layouts/character_edit/portrait_settings_section.gd b/common/layouts/character_edit/portrait_settings_section.gd
index 075278cb..c7d752df 100644
--- a/common/layouts/character_edit/portrait_settings_section.gd
+++ b/common/layouts/character_edit/portrait_settings_section.gd
@@ -1,116 +1,114 @@
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()
+#
+#@warning_ignore("unused_signal")
+#signal changed
+#
+#var portrait_type := OldProperty.new(Field.DROPDOWN, {}, "Image", "PortraitType")
+#var image_path := OldProperty.new(Field.FILE, {"filters": FilePicker.IMAGE}, "", "ImagePath")
+#var offset := OldProperty.new(Field.VECTOR, {}, [0, 0], "Offset")
+#var mirror := OldProperty.new(Field.TOGGLE, {}, false, "Mirror")
+#var one_shot := OldProperty.new(Field.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/timeline_section.gd b/common/layouts/character_edit/timeline_section.gd
index bde6dede..9adf2ee6 100644
--- a/common/layouts/character_edit/timeline_section.gd
+++ b/common/layouts/character_edit/timeline_section.gd
@@ -3,7 +3,7 @@ class_name TimelineSection extends PortraitEditSection
@warning_ignore("unused_signal")
signal changed
-var animation := Property.new(MonologueGraphNode.TIMELINE, {}, {})
+#var animation := OldProperty.new(Field.TIMELINE, {}, {})
var id: String
var base_path: String:
@@ -12,12 +12,12 @@ var base_path: String:
func _ready() -> void:
super._ready()
- animation.setters["preview_section"] = %PreviewSection
+ #animation.setters["preview_section"] = %PreviewSection
func _set_base_path(val: String) -> void:
base_path = val
- animation.setters["base_path"] = val
+ #animation.setters["base_path"] = val
func _from_dict(dict: Dictionary = {}) -> void:
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/editor/editor_dimmer.gd b/common/layouts/editor/editor_dimmer.gd
new file mode 100644
index 00000000..988aa902
--- /dev/null
+++ b/common/layouts/editor/editor_dimmer.gd
@@ -0,0 +1,36 @@
+extends ColorRect
+
+var _focus_nodes: Array = []
+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:
+ if focus_node and not focus_node in _focus_nodes:
+ _focus_nodes.append(focus_node)
+
+ request_count = max(1, request_count + 1)
+ show()
+
+
+func _on_hide_dimmer(focus_node: Node = null) -> void:
+ if focus_node and focus_node in _focus_nodes:
+ focus_node.hide()
+ _focus_nodes.erase(focus_node)
+
+ request_count = max(0, request_count - 1)
+ if request_count == 0:
+ hide()
+
+
+func _on_gui_input(event: InputEvent) -> void:
+ if event is InputEventMouseButton and event.is_pressed():
+ for node in _focus_nodes:
+ node.hide()
+ request_count = 0
+ hide()
diff --git a/common/layouts/dimmer/dimmer.gd.uid b/common/layouts/editor/editor_dimmer.gd.uid
similarity index 100%
rename from common/layouts/dimmer/dimmer.gd.uid
rename to common/layouts/editor/editor_dimmer.gd.uid
diff --git a/common/layouts/editor/editor_list_section.gd b/common/layouts/editor/editor_list_section.gd
new file mode 100644
index 00000000..79777556
--- /dev/null
+++ b/common/layouts/editor/editor_list_section.gd
@@ -0,0 +1,89 @@
+extends PanelContainer
+
+@export var section_icon: Texture2D
+
+var _property: Property
+var _list_field: ListField
+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 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 ListField:
+ _property.bind_field(_list_field, _property_owner)
+ vbox.add_child(_list_field)
+
+
+func _on_add_button_pressed() -> void:
+ if not (_property and _property_owner):
+ return
+
+ var data_schema = _property.get_settings_value("data_schema", {})
+ var schema_properties: Dictionary = data_schema.get("properties", {})
+ var item_initial_data: Dictionary = {}
+ for prop_name in schema_properties.keys():
+ var prop_config = schema_properties[prop_name]
+
+ if prop_config.get("editor_only", false):
+ continue
+
+ item_initial_data[prop_name] = prop_config.get("default", "")
+ if prop_config.get("default") is Callable:
+ item_initial_data[prop_name] = item_initial_data[prop_name].call()
+
+ var item_object = ListItemObject.new(
+ data_schema, item_initial_data, _list_field._command_manager, schema_properties
+ )
+ item_object.list_field = _list_field
+ item_object.make_all_values_unique()
+
+ var new_item_list: Array = _property.get_value().duplicate(true)
+ new_item_list.append(item_object._to_dict())
+ _property_owner.set_property_value(_property.name, new_item_list)
+
+
+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._list_items
+
+ for item: ListItemObject in item_list:
+ var hide_item: bool = true
+ var item_idx: int = item_list.find(item)
+ 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
+ if hide_item:
+ _list_field.hide_item(item_idx)
diff --git a/common/layouts/editor/editor_list_section.gd.uid b/common/layouts/editor/editor_list_section.gd.uid
new file mode 100644
index 00000000..40a85d71
--- /dev/null
+++ b/common/layouts/editor/editor_list_section.gd.uid
@@ -0,0 +1 @@
+uid://yglbu25x1rsy
diff --git a/common/layouts/editor/editor_list_section.tscn b/common/layouts/editor/editor_list_section.tscn
new file mode 100644
index 00000000..a221d269
--- /dev/null
+++ b/common/layouts/editor/editor_list_section.tscn
@@ -0,0 +1,38 @@
+[gd_scene load_steps=3 format=3 uid="uid://mfdu320oy6ex"]
+
+[ext_resource type="Script" uid="uid://yglbu25x1rsy" path="res://common/layouts/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"]
+
+[node name="ListSection" type="PanelContainer"]
+custom_minimum_size = Vector2(250, 0)
+offset_right = 250.0
+offset_bottom = 47.0
+script = ExtResource("1_coa1v")
+
+[node name="VBox" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="ToolBar" type="HBoxContainer" parent="VBox"]
+layout_mode = 2
+
+[node name="SearchLine" type="LineEdit" parent="VBox/ToolBar"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+placeholder_text = "Filter"
+
+[node name="AddButton" type="Button" parent="VBox/ToolBar"]
+layout_mode = 2
+icon = ExtResource("3_j4mj2")
+
+[node name="ScrollContainer" type="ScrollContainer" parent="VBox"]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="VBox" type="VBoxContainer" parent="VBox/ScrollContainer"]
+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/layouts/editor/graph.tscn b/common/layouts/editor/graph.tscn
new file mode 100644
index 00000000..bf89e6d5
--- /dev/null
+++ b/common/layouts/editor/graph.tscn
@@ -0,0 +1,84 @@
+[gd_scene load_steps=11 format=3 uid="uid://c8525nhvwt1y0"]
+
+[ext_resource type="Script" uid="uid://nxistnt1yhxc" path="res://scenes/main/graph_container.gd" id="1_nykfi"]
+[ext_resource type="Script" uid="uid://bmku341x5gaoe" path="res://scenes/main/add_node_button.gd" id="2_4vw8t"]
+[ext_resource type="Texture2D" uid="uid://bfmsxfn26cvfn" path="res://ui/assets/icons/character.svg" id="3_fbi2i"]
+[ext_resource type="Texture2D" uid="uid://b46sqb5g0spae" path="res://ui/assets/icons/variables.svg" id="4_4hcgi"]
+[ext_resource type="PackedScene" uid="uid://cb3se7h7akt47" path="res://common/layouts/language_switcher/language_switcher.tscn" id="5_l617j"]
+[ext_resource type="Texture2D" uid="uid://d4fesqfd2v8fd" path="res://ui/assets/icons/settings.svg" id="6_p1o4h"]
+[ext_resource type="Texture2D" uid="uid://dd6wdpndndufl" path="res://ui/assets/icons/sparkles.svg" id="7_lhc65"]
+[ext_resource type="Texture2D" uid="uid://b272tbdmvxj20" path="res://ui/assets/icons/play.svg" id="8_7526h"]
+[ext_resource type="PackedScene" uid="uid://qdgl8co6qy6" path="res://common/layouts/graph_edit/monologue_graph_edit.tscn" id="9_4vw8t"]
+
+[sub_resource type="GDScript" id="GDScript_lenro"]
+script/source = "extends Button
+
+
+func _on_pressed() -> void:
+ GlobalSignal.emit(\"test_trigger\")
+"
+
+[node name="Graph" type="PanelContainer"]
+
+[node name="GraphContainer" type="VBoxContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_vertical = 3
+mouse_filter = 2
+script = ExtResource("1_nykfi")
+
+[node name="ToolBar" type="HBoxContainer" parent="GraphContainer"]
+layout_mode = 2
+
+[node name="AddNodeBtn" type="Button" parent="GraphContainer/ToolBar"]
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+text = "Add a node..."
+script = ExtResource("2_4vw8t")
+
+[node name="ButtonCharacters" type="Button" parent="GraphContainer/ToolBar"]
+visible = false
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+icon = ExtResource("3_fbi2i")
+icon_alignment = 1
+
+[node name="ButtonVariables" type="Button" parent="GraphContainer/ToolBar"]
+visible = false
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+icon = ExtResource("4_4hcgi")
+icon_alignment = 1
+
+[node name="LanguageSwitcher" parent="GraphContainer/ToolBar" instance=ExtResource("5_l617j")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+disabled = true
+
+[node name="ButtonSettings" type="Button" parent="GraphContainer/ToolBar"]
+visible = false
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+icon = ExtResource("6_p1o4h")
+icon_alignment = 1
+
+[node name="ButtonSparkle" type="Button" parent="GraphContainer/ToolBar"]
+visible = false
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+icon = ExtResource("7_lhc65")
+
+[node name="RunButton" type="Button" parent="GraphContainer/ToolBar"]
+layout_mode = 2
+theme_type_variation = &"Button_Flat"
+icon = ExtResource("8_7526h")
+script = SubResource("GDScript_lenro")
+
+[node name="GraphEdit" parent="GraphContainer" instance=ExtResource("9_4vw8t")]
+unique_name_in_owner = true
+layout_mode = 2
+
+[connection signal="pressed" from="GraphContainer/ToolBar/AddNodeBtn" to="GraphContainer/ToolBar/AddNodeBtn" method="_on_pressed"]
+[connection signal="pressed" from="GraphContainer/ToolBar/RunButton" to="GraphContainer/ToolBar/RunButton" method="_on_pressed"]
+[connection signal="node_view_selected" from="GraphContainer/GraphEdit" to="GraphContainer" method="_on_graph_edit_node_view_selected"]
diff --git a/common/layouts/editor/inspector/expose_button.tscn b/common/layouts/editor/inspector/expose_button.tscn
new file mode 100644
index 00000000..e3eba3f0
--- /dev/null
+++ b/common/layouts/editor/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/layouts/editor/inspector/inspector_category_container.gd b/common/layouts/editor/inspector/inspector_category_container.gd
new file mode 100644
index 00000000..b00c0f54
--- /dev/null
+++ b/common/layouts/editor/inspector/inspector_category_container.gd
@@ -0,0 +1,23 @@
+extends FoldableContainer
+
+@onready var _vbox: VBoxContainer = %VBox
+
+
+func add_control(
+ node: Node,
+ force_readable_name: bool = false,
+ internal: InternalMode = InternalMode.INTERNAL_MODE_DISABLED
+) -> void:
+ if not is_node_ready():
+ # FIXME: Do weird stuff
+ 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/layouts/editor/inspector/inspector_category_container.gd.uid b/common/layouts/editor/inspector/inspector_category_container.gd.uid
new file mode 100644
index 00000000..df6a747c
--- /dev/null
+++ b/common/layouts/editor/inspector/inspector_category_container.gd.uid
@@ -0,0 +1 @@
+uid://djpi62hkqu8r6
diff --git a/common/layouts/editor/inspector/inspector_category_container.tscn b/common/layouts/editor/inspector/inspector_category_container.tscn
new file mode 100644
index 00000000..8ced7dcc
--- /dev/null
+++ b/common/layouts/editor/inspector/inspector_category_container.tscn
@@ -0,0 +1,13 @@
+[gd_scene load_steps=2 format=3 uid="uid://bvf68w7xrfrom"]
+
+[ext_resource type="Script" uid="uid://djpi62hkqu8r6" path="res://common/layouts/editor/inspector/inspector_category_container.gd" id="1_d6x5l"]
+
+[node name="FoldableContainer" type="FoldableContainer"]
+offset_right = 24.0
+offset_bottom = 32.0
+title_alignment = 1
+script = ExtResource("1_d6x5l")
+
+[node name="VBox" type="VBoxContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
diff --git a/common/layouts/editor/inspector/inspector_panel.gd b/common/layouts/editor/inspector/inspector_panel.gd
new file mode 100644
index 00000000..ee133afd
--- /dev/null
+++ b/common/layouts/editor/inspector/inspector_panel.gd
@@ -0,0 +1,325 @@
+class_name InspectorPanel extends PanelContainer
+
+var current_object: InspectableObject
+var _special_fields: Array[Control] = []
+var _category_states: Dictionary = {}
+var _pending_expand_category: String = ""
+
+@onready var header_container: VBoxContainer = %Header
+@onready var property_container: VBoxContainer = %Fields
+@onready var inspector_category_container: PackedScene = preload("uid://bvf68w7xrfrom")
+@onready var expose_button: PackedScene = preload("uid://2ehh7rdn6yg6")
+
+
+func _ready() -> void:
+ GlobalSignal.add_listener("inspector_property_changed", _on_external_property_changed)
+
+
+func _exit_tree() -> void:
+ GlobalSignal.remove_listener("inspector_property_changed", _on_external_property_changed)
+
+
+func inspect(object: InspectableObject) -> void:
+ var is_new_object := current_object != object
+ if current_object and current_object != object:
+ if current_object is InspectableNode and is_instance_valid(current_object.graph_view):
+ current_object.graph_view.selected = false
+ current_object.remove_observer(on_property_changed)
+ elif current_object:
+ current_object.remove_observer(on_property_changed)
+
+ current_object = object
+ if is_new_object:
+ _category_states.clear()
+ rebuild()
+
+ if current_object:
+ current_object.add_observer(on_property_changed)
+
+
+func rebuild() -> void:
+ # Store the current focus owner before rebuilding
+ var focus_owner = get_viewport().gui_get_focus_owner()
+ var focused_property_name: String = ""
+
+ # Try to identify which property had focus
+ if focus_owner and focus_owner.is_inside_tree():
+ var node = focus_owner
+ while node:
+ if node.get_parent() == property_container:
+ # Found the property container, try to extract property name
+ for child in property_container.get_children():
+ if child == node or child.is_ancestor_of(focus_owner):
+ # Try to find the property name from the label
+ var labels = []
+ _find_labels(child, labels)
+ if labels.size() > 0:
+ focused_property_name = labels[0].text
+ break
+ break
+ node = node.get_parent()
+
+ _cache_category_states()
+ for prop: Control in property_container.get_children():
+ prop.queue_free()
+
+ for field: Control in _special_fields:
+ field.queue_free()
+ _special_fields.clear()
+
+ if !current_object:
+ var label: Label = Label.new()
+ label.text = "No node selected"
+ property_container.add_child(label)
+ _pending_expand_category = ""
+ return
+
+ var properties: Array[Property] = current_object.get_properties()
+ var categories: Dictionary = _group_by_category(properties)
+ for category_name: String in categories.keys():
+ var props = 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)
+
+ post_build()
+
+ # Restore focus if we had a focused property
+ if not focused_property_name.is_empty():
+ await get_tree().process_frame
+ _restore_focus_to_property(focused_property_name)
+
+ _pending_expand_category = ""
+
+
+func _find_labels(node: Node, labels: Array) -> void:
+ if node is Label:
+ labels.append(node)
+ for child in node.get_children():
+ _find_labels(child, labels)
+
+
+func _restore_focus_to_property(property_display_name: String) -> void:
+ # Find the property with matching display name and restore focus to its field
+ for container in property_container.get_children():
+ var labels = []
+ _find_labels(container, labels)
+ for label in labels:
+ if label.text == property_display_name:
+ # Found the property, now find its field and focus it
+ var fields = []
+ _find_focusable_fields(container, fields)
+ if fields.size() > 0:
+ fields[0].grab_focus()
+ return
+
+
+func _find_focusable_fields(node: Node, fields: Array) -> void:
+ if node is LineEdit or node is TextEdit or node is OptionButton:
+ fields.append(node)
+ for child in node.get_children():
+ _find_focusable_fields(child, fields)
+
+
+func _group_by_category(properties: Array) -> Dictionary:
+ var groups: Dictionary = {}
+ for prop 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) -> void:
+ if not property_container.is_node_ready():
+ await property_container.ready
+
+ var container: FoldableContainer = inspector_category_container.instantiate()
+ container.title = category_name
+ property_container.add_child(container)
+ _apply_category_state(container, category_name)
+
+ for property: Property in properties:
+ var property_editor: Control = _create_property_editor(property)
+ if property_editor:
+ container.add_control(property_editor)
+
+
+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 index: int = properties.find(property)
+ var property_editor: Control = _create_property_editor(property)
+ if not property_editor:
+ continue
+
+ _special_fields.append(property_editor)
+
+ if container.get_child_count() >= index:
+ var sub_container: Control = container.get_child(index - 1)
+ sub_container.add_child(property_editor)
+ sub_container.move_child(property_editor, 0)
+ continue
+ container.add_child(property_editor)
+
+
+func _create_property_editor(property: Property) -> Control:
+ if (
+ not property.get_settings_value("editable", true)
+ and not property.get_settings_value("exposed", false)
+ and not property.get_settings_value("export", false)
+ ):
+ return
+
+ var p_container: PanelContainer = PanelContainer.new()
+ var p_hbox: HBoxContainer = HBoxContainer.new()
+ var p_vbox: VBoxContainer = VBoxContainer.new()
+ var p_expose_button: TextureButton = expose_button.instantiate()
+ var p_label: Label = Label.new()
+
+ p_container.theme_type_variation = "FieldContainer"
+
+ p_label.text = property.get_display_name()
+ p_hbox.add_child(p_expose_button)
+ p_hbox.add_child(p_label)
+
+ p_vbox.add_child(p_hbox)
+
+ if property.get_settings_value("flat"):
+ p_container.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
+ p_hbox.hide()
+
+ var p_field: Control
+ if property.is_intput_connected(): # Add inspect connected node button if property is connected
+ 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)
+ if p_field is Field:
+ property.call_deferred("bind_field", p_field, current_object)
+
+ p_vbox.add_child(p_field)
+ p_container.add_child(p_vbox)
+ if property.get_settings_value("expand", true):
+ p_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+
+ p_expose_button.disabled = not property.get_settings_value("exposable", false)
+ p_expose_button.button_pressed = property.get_settings_value("exposed", false)
+ p_expose_button.toggled.connect(
+ _on_property_expose_state_changed.bind(current_object, property.name)
+ )
+
+ # TODO: Make field read-only if property is not editable or is connected
+ #if p_field and p_field.has_method("set_editable"):
+ #var is_editable = (
+ #property.get_settings_value("editable", true) and not property.is_intput_connected()
+ #)
+ #p_field.set_editable(is_editable)
+
+ return p_container
+
+
+func _cache_category_states() -> void:
+ for child: Control in property_container.get_children():
+ if child is FoldableContainer:
+ _category_states[child.title] = child.folded
+
+
+func _apply_category_state(container: FoldableContainer, category_name: String) -> void:
+ var stored_state = _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 post_build() -> void:
+ for child: Control in property_container.get_children():
+ if child is FoldableContainer and child.is_empty():
+ child.queue_free()
+
+ _cache_category_states()
+
+
+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 := node.graph_view.get_parent()
+ if not (graph_edit is GraphEdit):
+ return
+
+ if not graph_edit.connection_manager:
+ return
+
+ # Get the connected node from the connection manager
+ var connected_node = graph_edit.connection_manager.get_connected_node(node, property.name)
+
+ if connected_node and connected_node.graph_view:
+ GlobalSignal.emit("request_node_inspection", [connected_node, connected_node.storyline_id])
+
+
+func on_property_changed(obj: InspectableObject, _property_name: String) -> void:
+ if not obj:
+ return
+
+ rebuild()
+
+
+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
+
+ _pending_expand_category = property.get_category()
+
+ if obj == current_object:
+ rebuild()
+ return
+
+ inspect(obj)
diff --git a/common/layouts/side_panel/side_panel.gd.uid b/common/layouts/editor/inspector/inspector_panel.gd.uid
similarity index 100%
rename from common/layouts/side_panel/side_panel.gd.uid
rename to common/layouts/editor/inspector/inspector_panel.gd.uid
diff --git a/common/layouts/side_panel/side_panel.tscn b/common/layouts/editor/inspector/inspector_panel.tscn
similarity index 51%
rename from common/layouts/side_panel/side_panel.tscn
rename to common/layouts/editor/inspector/inspector_panel.tscn
index b39ff815..ef17fca8 100644
--- a/common/layouts/side_panel/side_panel.tscn
+++ b/common/layouts/editor/inspector/inspector_panel.tscn
@@ -1,12 +1,12 @@
[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="Script" uid="uid://dtf4ge38njewp" path="res://common/layouts/editor/inspector/inspector_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"]
+[node name="InspectorPanel" type="PanelContainer"]
offset_right = 158.0
offset_bottom = 121.0
-theme_type_variation = &"EditorSidePanel"
+theme_type_variation = &"InspectorPanel"
script = ExtResource("1_haagr")
[node name="VBox" type="VBoxContainer" parent="."]
@@ -14,34 +14,25 @@ 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"]
+[node name="Header" type="VBoxContainer" parent="VBox"]
+unique_name_in_owner = true
layout_mode = 2
-[node name="TopBox" type="HBoxContainer" parent="VBox/PanelContainer/VBox"]
-unique_name_in_owner = true
+[node name="FirstField" type="HBoxContainer" parent="VBox/Header"]
layout_mode = 2
theme_type_variation = &"HBoxContainer_Small"
+alignment = 2
-[node name="RFHButton" type="Button" parent="VBox/PanelContainer/VBox/TopBox"]
-custom_minimum_size = Vector2(34, 29)
+[node name="RFHButton" type="Button" parent="VBox/Header/FirstField"]
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
+follow_focus = true
[node name="Fields" type="VBoxContainer" parent="VBox/Scroller"]
unique_name_in_owner = true
@@ -49,4 +40,4 @@ 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"]
+[connection signal="pressed" from="VBox/Header/FirstField/RFHButton" to="." method="_on_rfh_button_pressed"]
diff --git a/common/layouts/expanded_text_edit/expanded_text_edit_container.gd b/common/layouts/expanded_text_edit/expanded_text_edit_container.gd
index 3bdfe18e..12a6e91a 100644
--- a/common/layouts/expanded_text_edit/expanded_text_edit_container.gd
+++ b/common/layouts/expanded_text_edit/expanded_text_edit_container.gd
@@ -23,7 +23,7 @@ func _on_button_pressed() -> void:
func _on_text_edit_text_changed() -> void:
little_text_edit.text = text_edit.text
- little_text_edit.text_changed.emit()
+ little_text_edit.text_changed.emit(text_edit.text)
func _on_visibility_changed() -> void:
diff --git a/common/layouts/graph_edit/custom_graph_edit.gd b/common/layouts/graph_edit/custom_graph_edit.gd
new file mode 100644
index 00000000..02ae2ae8
--- /dev/null
+++ b/common/layouts/graph_edit/custom_graph_edit.gd
@@ -0,0 +1,57 @@
+## Represents the graph area which creates and connects MonologueGraphNodes.
+class_name CustomGraphEdit extends GraphEdit
+
+const SCROLLBAR_OVERRIDE_KEYS := ["grabber", "scroll"]
+
+var connecting_mode: bool
+var mouse_hovering: bool = false
+
+
+func _ready() -> void:
+ _hide_default_scrollbars()
+
+
+func _hide_default_scrollbars() -> void:
+ for child in get_children(true):
+ if child is GraphNode:
+ continue
+ for subchild in child.get_children(true):
+ if subchild is ScrollBar:
+ for key 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 = (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:
+ if mouse_hovering:
+ var cursor_drag := Input.is_action_pressed("Spacebar")
+ var cursor_hand_closed := 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
+ ):
+ GlobalSignal.emit("show_languages", [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/layouts/graph_edit/custom_graph_edit.gd.uid b/common/layouts/graph_edit/custom_graph_edit.gd.uid
new file mode 100644
index 00000000..4401c67f
--- /dev/null
+++ b/common/layouts/graph_edit/custom_graph_edit.gd.uid
@@ -0,0 +1 @@
+uid://dfy1ptnvfis7m
diff --git a/common/layouts/graph_edit/graph_node_view_factory.gd b/common/layouts/graph_edit/graph_node_view_factory.gd
new file mode 100644
index 00000000..d5ce534b
--- /dev/null
+++ b/common/layouts/graph_edit/graph_node_view_factory.gd
@@ -0,0 +1,155 @@
+class_name GraphNodeViewFactory extends RefCounted
+
+const SLOT_IN_TEXTURE := preload("res://ui/assets/icons/slot_in.svg")
+const SLOT_OUT_TEXTURE := preload("res://ui/assets/icons/slot_out.svg")
+
+
+static func build(node: InspectableNode) -> GraphNode:
+ var graph_node := 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(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 new_sb: StyleBoxFlat = base_sb.duplicate()
+ var new_bg_color = Color(node_color, 0.35)
+ var new_border_color = Color(node_color, 0.35)
+ new_bg_color = base_sb.bg_color.blend(new_bg_color)
+ new_border_color = base_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 := _build_rows(node)
+ for idx in rows.size():
+ var row: GraphNodeRow = rows[idx]
+ var container := HBoxContainer.new()
+ container.mouse_filter = Control.MOUSE_FILTER_PASS
+ container.theme_type_variation = "GraphNodeViewRownHBox"
+
+ var key_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.new()
+ value_label.mouse_filter = Control.MOUSE_FILTER_PASS
+ if row.get_type():
+ value_label.text = "[%s]" % row.get_type()
+
+ var field_metadata := 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)
+
+ 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())
+ # Use node id as the graph node name to make connections rely on ids only
+ var id_prop := node.get_property("id")
+ graph_node.name = String(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():
+ var enable_left: bool = bool(prop.get_settings_value("exposed", false))
+ var enable_right: bool = bool(prop.get_settings_value("export", false))
+ if (
+ not prop.get_settings_value("visible_in_graph", true)
+ and not (enable_left or enable_right)
+ ):
+ continue
+
+ var label := (
+ prop.get_display_name() if prop.get_settings_value("is_main_property") else prop.name
+ )
+ var row := GraphNodeRow.new(label, prop.type, enable_left, enable_right)
+ if prop.get_settings_value("is_main_property"):
+ rows.push_front(row)
+ continue
+ rows.append(row)
+
+ return rows
+
+
+static func _derive_node_name(node: InspectableNode) -> String:
+ # Fallback to id (no type prefix) if needed
+ var id_value := ""
+ var id_property := node.get_property("id")
+ if id_property:
+ id_value = String(id_property.get_value())
+ if id_value.is_empty():
+ id_value = IDGen.generate(5)
+ return id_value
diff --git a/common/layouts/graph_edit/graph_node_view_factory.gd.uid b/common/layouts/graph_edit/graph_node_view_factory.gd.uid
new file mode 100644
index 00000000..e9449977
--- /dev/null
+++ b/common/layouts/graph_edit/graph_node_view_factory.gd.uid
@@ -0,0 +1 @@
+uid://d3r62bie5le05
diff --git a/common/layouts/graph_edit/monologue_graph_edit.gd b/common/layouts/graph_edit/monologue_graph_edit.gd
index 2a24a655..9f8cf126 100644
--- a/common/layouts/graph_edit/monologue_graph_edit.gd
+++ b/common/layouts/graph_edit/monologue_graph_edit.gd
@@ -1,422 +1,405 @@
-## Represents the graph area which creates and connects MonologueGraphNodes.
-class_name MonologueGraphEdit extends GraphEdit
+class_name MonologueGraphEdit extends CustomGraphEdit
+signal node_view_selected(node: InspectableNode)
-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
+var storyline_id: String
+var connection_manager: ConnectionManager
+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:
- var auto_arrange_button = get_menu_hbox().get_children().back()
- auto_arrange_button.connect("pressed", _on_auto_arrange_nodes)
+ super._ready()
+ StorylineManager.storyline_switched.connect(_on_storyline_switched)
- center_offset.call_deferred()
- # Hide scroll bar
- for child in get_children(true):
- if child is GraphNode:
- continue
+func refresh() -> void:
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ if not storyline:
+ return
- for subchild in child.get_children(true):
- if subchild is not ScrollBar:
- continue
+ # Initialize connection manager if not already done
+ if not connection_manager:
+ connection_manager = ConnectionManager.new(storyline)
- for sb_name in ["grabber", "scroll"]:
- subchild.add_theme_stylebox_override(sb_name, StyleBoxEmpty.new())
+ # Clear node map and connections
+ _node_map.clear()
+ _pending_positions.clear()
+ clear_connections()
+ for child: GraphElement in get_all_graph_nodes():
+ child.queue_free()
-func _on_add_btn() -> void:
- GlobalSignal.emit("select_new_node")
+ for node: InspectableNode in storyline.nodes:
+ add_graph_node_view(node)
+ # Reconnect all tracked connections after rebuilding nodes
+ _reconnect_all_slots()
-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 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 _input(event: InputEvent) -> void:
- moving_mode = (
- Input.is_action_pressed("Select")
- and event is InputEventMouseMotion
- and not selected_nodes.is_empty()
- )
+func add_graph_node_view(node: InspectableNode) -> void:
+ var graph_node: GraphNode = GraphNodeViewFactory.build(node)
-func _gui_input(_event: InputEvent) -> void:
- if not mouse_hovering:
- return
+ # 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))
- var cursor_drag: bool = false
- var cursor_hand_closed: bool = false
+ # Add to scene and set initial position
+ add_child(graph_node)
+ if not _on_inspectable_node_property_changed in node._observers:
+ node.add_observer(_on_inspectable_node_property_changed)
- if Input.is_action_pressed("Spacebar"):
- cursor_drag = true
- if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
- cursor_hand_closed = true
+ # Apply initial position from property
+ _sync_position_from_property(node)
- 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)
+## Called when InspectableNode property changes (undo/redo, programmatic)
+func _on_inspectable_node_property_changed(node: InspectableNode, property_name: String) -> void:
+ if property_name == "position":
+ if not _is_applying_position:
+ _sync_position_from_property(node)
else:
- DisplayServer.cursor_set_custom_image(Cursor.arrow)
+ refresh_node(node)
-## 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)
+## 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
- var node_scene = Constants.NODE_SCENES.get(node_type)
- var new_node = node_scene.instantiate()
+ var position_property := node.get_property("position")
+ var desired_position: Vector2 = (
+ position_property.get_value() if position_property else Vector2.ZERO
+ )
- # created_nodes include auxilliary nodes from new_node, such as BridgeOut
- var created_nodes = new_node.add_to(self)
+ if node.graph_view.position_offset == desired_position:
+ return
- # 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
+ _is_applying_position = true
+ node.graph_view.position_offset = desired_position
+ _is_applying_position = false
+ _pending_positions.erase(node.graph_view)
+ _pending_positions.erase(node.graph_view)
- 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
+## 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 clear():
- for node in get_nodes():
- node.queue_free()
- clear_connections()
+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)
-## 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 _on_node_deselected(graph_node: Node) -> void:
+ _selected_nodes[graph_node] = false
-func get_root_node() -> RootNode:
- for node in get_nodes():
- if node is RootNode:
- return node
- return null
+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)
-## 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
+ if from_port_type != to_port_type:
+ return
+ # Get property names at the port indices
+ var from_property_name = get_property_name_at_port(String(from_view_name), from_port, true)
+ var to_property_name = get_property_name_at_port(String(to_view_name), to_port, false)
-func is_unsaved() -> bool:
- return version != undo_redo.get_version()
+ 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 = StorylineManager.get_storyline(storyline_id)
+ var command: NodeConnectionCommand = NodeConnectionCommand.new(
+ self,
+ from_node.get_property_value("id"),
+ to_node.get_property_value("id"),
+ from_property_name,
+ to_property_name
+ )
+ storyline.history.execute(command)
-## 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
+func _has_connection_at_slot(node_name: StringName, port_index: int, is_output: bool) -> bool:
+ var node_key := "from_node" if is_output else "to_node"
+ var port_key := "from_port" if is_output else "to_port"
+ var target_name := String(node_name)
- 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
+ for connection: Dictionary in get_connection_list():
+ if String(connection.get(node_key, "")) != target_name:
+ continue
+ if int(connection.get(port_key, -1)) == port_index:
+ return true
- picker.flush()
-
- for node in nodes:
- node.position_offset = offset
+ return false
- post_node_offset.call_deferred(nodes)
- return to_names
+func get_all_graph_nodes() -> Array:
+ return get_children().filter(func(child) -> bool: return child is GraphNode)
-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):
+
+## Reconnect all slots based on tracked connections in connection_manager
+func _reconnect_all_slots() -> void:
+ if not connection_manager:
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
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ var all_connections = connection_manager.get_all_connections()
+ for conn in all_connections:
+ var from_node_id = conn["from_node_id"]
+ var from_property = conn["from_property"]
+ var to_node_id = conn["to_node_id"]
+ var to_property = conn["to_property"]
-## 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)
+ var from_node: InspectableNode = storyline.get_node(from_node_id)
+ var to_node: InspectableNode = storyline.get_node(to_node_id)
+ var from_view_name: String = from_node.graph_view.name
+ var to_view_name: String = to_node.graph_view.name
- else:
- disconnect_node(from_node, from_port, to_node, to_port)
+ # Get port indices for the properties
+ var from_port = get_port_index_for_property(from_view_name, from_property)
+ var to_port = get_port_index_for_property(to_view_name, to_property)
+
+ # 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)
- 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)
+
+## Get visible properties in the same order as displayed in graph
+func _get_visible_properties(node: InspectableNode) -> Array[Property]:
+ var visible_props: Array[Property] = []
+ var properties = node.get_properties()
+
+ for prop: Property in properties:
+ if not prop.get_settings_value("visible_in_graph", true):
+ continue
+ var exposed = prop.get_settings_value("exposed", false) or false
+ var export = prop.get_settings_value("export", false) or false
+
+ # Skip if no ports
+ if not exposed and not export:
+ continue
+
+ # Main property goes first
+ if prop.get_settings_value("is_main_property"):
+ visible_props.push_front(prop)
else:
- graph_node.update_next_id(from_port, null)
+ visible_props.append(prop)
+
+ return visible_props
-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()
+## Get the port index for a specific property by name
+func get_port_index_for_property(node_name: String, property_name: String) -> int:
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ for node: InspectableNode in storyline.nodes:
+ if node.graph_view.name == node_name:
+ var visible_props = _get_visible_properties(node)
-## Checks and ensure graph is ready before triggering undo.
-func trigger_undo() -> void:
- if not connecting_mode:
- undo_redo.undo()
+ # Find the property by name and return its index
+ for i in range(visible_props.size()):
+ if visible_props[i].name == property_name:
+ return i
+ break
+ return -1
-## Checks and ensure graph is ready before triggering redo.
-func trigger_redo() -> void:
- if not connecting_mode:
- undo_redo.redo()
+func get_node_from_view_name(graph_view_name: String) -> InspectableNode:
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
-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
+ for node: InspectableNode in storyline.nodes:
+ if node.graph_view.name == graph_view_name:
+ return node
+ return null
-func update_version() -> void:
- version = undo_redo.get_version()
+## Get property name at a specific port index
+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 count := 0
+ for prop: Property in node.get_properties():
+ var has_port: bool = (
+ prop.get_settings_value("export", false)
+ if is_output
+ else prop.get_settings_value("exposed", false)
+ )
+ if not has_port:
+ continue
+ if count == port_index:
+ return prop.name
+ count += 1
+ return ""
+
+
+func _on_end_node_move() -> void:
+ if _pending_positions.is_empty():
+ return
+
+ _is_applying_position = true
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ var history: CommandManager = storyline.history if storyline else null
+ if not history:
+ _is_applying_position = false
+ _pending_positions.clear()
+ return
-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()
+ for graph_node in _pending_positions.keys():
+ if not is_instance_valid(graph_node):
+ continue
+ var node: InspectableNode = _node_map.get(graph_node)
+ if not node:
+ continue
-func _on_child_entered_tree(node: Node) -> void:
- if node is MonologueGraphNode:
- if node is RootNode:
- return
+ var target_position: Vector2 = _pending_positions[graph_node]
+ var position_property := node.get_property("position")
+ if not position_property:
+ continue
- if not node.show_close_button:
- return
+ var current_position: Vector2 = position_property.get_value()
+ if current_position == target_position:
+ continue
- var node_header = node.get_children(true)[0]
- var close_button: TextureButton = close_button_scene.instantiate()
+ var command := PropertyChangeCommand.new(
+ node, "position", current_position, target_position
+ )
+ history.execute(command, UndoRedo.MERGE_DISABLE)
- 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)
+ _is_applying_position = false
+ _pending_positions.clear()
- close_button.connect("pressed", close_callback)
- node_header.add_child(close_button)
+func _on_disconnection_request(
+ from_view_name: StringName, from_port: int, to_view_name: StringName, to_port: int
+) -> void:
+ if not connection_manager:
+ return
-func _on_connection_drag_started(_from_node, _from_port, _is_output) -> void:
- connecting_mode = true
+ 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
-func _on_connection_drag_ended() -> void:
- connecting_mode = false
+ # Get property names at the port indices
+ var from_property_name = get_property_name_at_port(String(from_view_name), from_port, true)
+ var to_property_name = 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
-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()
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ var command: NodeConnectionCommand = NodeConnectionCommand.new(
+ self,
+ from_node.get_property_value("id"),
+ to_node.get_property_value("id"),
+ from_property_name,
+ to_property_name
+ )
+ storyline.history.execute(command)
-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_storyline_switched() -> void:
+ var storyline: StorylineDocument = StorylineManager.get_active_storyline()
+ if not storyline.node_added.is_connected(refresh):
+ storyline.node_added.connect(refresh)
+ if not storyline.node_removed.is_connected(refresh):
+ storyline.node_removed.connect(refresh)
+ refresh()
-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_copy_nodes_request() -> void:
+ if _selected_nodes.size() <= 0:
+ return
+ _copied_nodes.clear()
-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])
+ for node in get_children():
+ if node is GraphNode and _selected_nodes.get(node, false):
+ var duplicated: InspectableNode = _node_map.get(node).duplicate(true)
+ _copied_nodes.append(duplicated)
-func _on_node_selected(node) -> void:
- if node is MonologueGraphNode:
- selected_nodes.append(node)
+func _on_paste_nodes_request() -> void:
+ # TODO: Move nodes based on the cursor position
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ var command: AddNodesCommand = AddNodesCommand.new(storyline_id, _copied_nodes)
+ storyline.history.execute(command)
-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 _on_cut_nodes_request() -> void:
+ if _selected_nodes.size() <= 0:
+ return
+ _copied_nodes.clear()
+ var nodes_to_delete: Array[InspectableNode] = []
-func get_nodes() -> Array[MonologueGraphNode]:
- var list: Array[MonologueGraphNode] = []
for node in get_children():
- if node is MonologueGraphNode:
- list.append(node)
- return list
+ if node is GraphNode and _selected_nodes.get(node, false):
+ var duplicated: InspectableNode = _node_map.get(node).duplicate(true)
+ _copied_nodes.append(duplicated)
+ nodes_to_delete.append(_node_map.get(node))
+
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ var command: DeleteNodesCommand = DeleteNodesCommand.new(storyline_id, nodes_to_delete)
+ storyline.history.execute(command)
-func _on_mouse_entered() -> void:
- mouse_hovering = true
+func _on_delete_nodes_request(graph_nodes: Array[StringName]) -> void:
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ 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)
-func _on_mouse_exited() -> void:
- DisplayServer.cursor_set_custom_image(null)
- mouse_hovering = false
+ refresh()
diff --git a/common/layouts/graph_edit/monologue_graph_edit.tscn b/common/layouts/graph_edit/monologue_graph_edit.tscn
index 6cb08519..4d7633ca 100644
--- a/common/layouts/graph_edit/monologue_graph_edit.tscn
+++ b/common/layouts/graph_edit/monologue_graph_edit.tscn
@@ -10,13 +10,13 @@ grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
+show_grid = false
grid_pattern = 1
snapping_enabled = false
snapping_distance = 30
right_disconnects = true
-zoom = 0.6
zoom_min = 0.1
-zoom_max = 1.2
+zoom_max = 2.0
minimap_enabled = false
show_menu = false
show_zoom_buttons = false
@@ -25,14 +25,17 @@ 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="copy_nodes_request" from="." to="." method="_on_copy_nodes_request"]
+[connection signal="cut_nodes_request" from="." to="." method="_on_cut_nodes_request"]
+[connection signal="delete_nodes_request" from="." to="." method="_on_delete_nodes_request"]
[connection signal="disconnection_request" from="." to="." method="_on_disconnection_request"]
+[connection signal="duplicate_nodes_request" from="." to="." method="_on_duplicate_nodes_request"]
+[connection signal="end_node_move" from="." to="." method="_on_end_node_move"]
[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"]
+[connection signal="paste_nodes_request" from="." to="." method="_on_paste_nodes_request"]
diff --git a/common/layouts/language_switcher/language_switcher.tscn b/common/layouts/language_switcher/language_switcher.tscn
index cc85123d..e878e6bc 100644
--- a/common/layouts/language_switcher/language_switcher.tscn
+++ b/common/layouts/language_switcher/language_switcher.tscn
@@ -25,14 +25,15 @@ grow_vertical = 2
mouse_filter = 2
[node name="PanelContainer" type="PanelContainer" parent="Dropdown"]
+z_index = 2
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
+offset_left = -3.0
+offset_top = 42.0
+offset_right = 3.0
+offset_bottom = 202.0
grow_horizontal = 2
grow_vertical = 2
theme_type_variation = &"OuterPanel"
diff --git a/common/layouts/search_bar/search_bar.gd b/common/layouts/search_bar/search_bar.gd
index 62ff5b2e..9e49172f 100644
--- a/common/layouts/search_bar/search_bar.gd
+++ b/common/layouts/search_bar/search_bar.gd
@@ -2,25 +2,24 @@ 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_line_edit_text_changed(_new_text: String) -> void:
+ pass
+ #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:
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/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..0d715198
--- /dev/null
+++ b/common/monologue_indexer.gd
@@ -0,0 +1,8 @@
+@abstract class_name MonologueIndexer
+
+enum ObjectType { NODE, FIELD }
+
+@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..b4e1f110
--- /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 = [
+ "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 = [
+ "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 = [
+ "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/node_bucket.gd b/common/nodes/node_bucket.gd
new file mode 100644
index 00000000..3fa368ba
--- /dev/null
+++ b/common/nodes/node_bucket.gd
@@ -0,0 +1,123 @@
+extends Node
+
+const DEFAULT_NODES_LOCATION := "res://nodes/"
+
+var _descriptors: Dictionary = {}
+var _is_initialized: bool = false
+
+
+func register_descriptor(descriptor) -> void:
+ if descriptor == null:
+ push_warning("Attempted to register a null GraphNodeDescriptor.")
+ return
+ if descriptor.name.is_empty():
+ push_warning("GraphNodeDescriptor missing name.")
+ return
+ if _descriptors.has(descriptor.name):
+ push_warning("GraphNodeDescriptor '%s' already registered." % descriptor.name)
+ return
+ _descriptors[descriptor.name] = descriptor
+
+
+func get_descriptor(descriptor_name: String):
+ _ensure_initialized()
+ return _descriptors.get(descriptor_name)
+
+
+func list_descriptors() -> Array:
+ _ensure_initialized()
+ var values: Array = _descriptors.values()
+ values.sort_custom(_sort_descriptors)
+ return values
+
+
+func list_metadata() -> Array[Dictionary]:
+ return list_descriptors().map(func(desc) -> Dictionary: return desc.to_metadata())
+
+
+func get_categories() -> PackedStringArray:
+ _ensure_initialized()
+ var categories: PackedStringArray = []
+ for descriptor in _descriptors.values():
+ if descriptor.category in categories:
+ continue
+ categories.append(descriptor.category)
+ categories.sort()
+ return categories
+
+
+func get_descriptors_by_category(category: String) -> Array:
+ _ensure_initialized()
+ var result: Array = []
+ for descriptor in _descriptors.values():
+ if descriptor.category == category:
+ result.append(descriptor)
+ result.sort_custom(_sort_descriptors)
+ return result
+
+
+func create_node(descriptor_name: String, history: CommandManager) -> InspectableNode:
+ var descriptor = get_descriptor(descriptor_name)
+ if descriptor:
+ return descriptor.instantiate_node(history)
+ push_warning("No node descriptor registered for '%s'." % descriptor_name)
+ return null
+
+
+func refresh_registry() -> void:
+ _is_initialized = false
+ _descriptors.clear()
+ _ensure_initialized()
+
+
+func _ensure_initialized() -> void:
+ if _is_initialized:
+ return
+ _search_nodes()
+ _is_initialized = true
+
+
+func _search_nodes() -> void:
+ var directories: Array = DirAccess.get_directories_at(DEFAULT_NODES_LOCATION)
+ for dir_path: String in directories:
+ var index_path := DEFAULT_NODES_LOCATION.path_join(dir_path).path_join("index.gd")
+ if not FileAccess.file_exists(index_path):
+ continue
+ var index_script: Script = load(index_path)
+ if index_script == null:
+ push_warning("Failed to load node indexer at %s" % index_path)
+ continue
+ var indexer = index_script.new()
+ var descriptor = _descriptor_from_indexer(indexer)
+ if descriptor:
+ register_descriptor(descriptor)
+
+
+func _descriptor_from_indexer(indexer):
+ if not indexer:
+ return null
+ if indexer.has_method("get_metadata"):
+ var metadata: Dictionary = indexer.call("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 = metadata.get("script")
+ if raw_script is Script:
+ script_resource = raw_script
+ elif raw_script is String and not raw_script.is_empty():
+ script_resource = load(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)
+ return null
+
+
+static func _sort_descriptors(a, b) -> bool:
+ return a.display_name.naturalnocasecmp_to(b.display_name) < 0
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..3ea2f993
--- /dev/null
+++ b/common/nodes/node_descriptor.gd
@@ -0,0 +1,58 @@
+class_name GraphNodeDescriptor extends RefCounted
+
+var name: String
+var node_script: Script
+var display_name: String
+var category: String = "General"
+var icon: Texture2D
+var color: Color = Color.WHITE
+var tags: Array[String] = []
+var description: String = ""
+
+
+func _init(p_name: String = "", p_script: Script = null, metadata: Dictionary = {}) -> void:
+ name = p_name
+ node_script = p_script
+ display_name = metadata.get("display_name", Util.to_readable_name(name))
+ category = metadata.get("category", category)
+ description = metadata.get("description", "")
+
+ var raw_icon = metadata.get("icon")
+ if raw_icon is Texture2D:
+ icon = raw_icon
+ elif raw_icon is String and not raw_icon.is_empty():
+ var loaded_icon = load(raw_icon)
+ if loaded_icon is Texture2D:
+ icon = loaded_icon
+
+ var raw_color = metadata.get("color")
+ if raw_color is Color:
+ color = raw_color
+ elif raw_color is String and not raw_color.is_empty():
+ color = Color(raw_color)
+
+ if metadata.has("tags") and metadata["tags"] is Array:
+ tags = metadata["tags"].duplicate(true)
+
+
+func instantiate_node(history: CommandManager) -> InspectableNode:
+ if node_script == null:
+ push_warning("Descriptor '%s' missing script for instantiation." % name)
+ return null
+ var instance = node_script.new(history)
+ if instance is InspectableNode:
+ return instance
+ push_error("Descriptor '%s' does not create an InspectableNode." % name)
+ return null
+
+
+func to_metadata() -> Dictionary:
+ return {
+ "name": name,
+ "display_name": display_name,
+ "category": category,
+ "description": description,
+ "color": color,
+ "icon": icon,
+ "tags": tags.duplicate(true)
+ }
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/schemas.gd b/common/schemas.gd
new file mode 100644
index 00000000..9ef400bf
--- /dev/null
+++ b/common/schemas.gd
@@ -0,0 +1,256 @@
+class_name Schemas
+
+static var VARIABLE: Dictionary = {
+ "title": "Variable",
+ "type": "object",
+ "properties":
+ {
+ "name":
+ {
+ "type": "text",
+ "default": "new variable",
+ "required": true,
+ "unique": true,
+ "validation": {"min_length": 1}
+ },
+ "type":
+ {
+ "type": "dropdown",
+ "options": ["bool", "string", "int", "float"],
+ "default": "string",
+ "required": true
+ },
+ "value":
+ {
+ "type": "dynamic",
+ "case_property": "type",
+ "cases":
+ {
+ "bool": {"type": "bool", "default": false},
+ "string": {"type": "text", "default": "", "coerce": "string"},
+ "int": {"type": "number", "default": 0, "coerce": "int"},
+ "float": {"type": "number", "default": 0.0, "coerce": "float"}
+ }
+ },
+ "description":
+ {
+ "type": "textarea",
+ "default": "",
+ "rows": 3,
+ }
+ },
+ "layouts":
+ {
+ "default":
+ {
+ "fields": ["name", "type", "value", "description"],
+ },
+ "list_item":
+ {
+ "fields": ["name", "type", "value", "description"],
+ "actions": ["duplicate", "delete"],
+ }
+ }
+}
+
+static var CHARACTER: Dictionary = {
+ "title": "Character",
+ "type": "object",
+ "properties":
+ {
+ "id":
+ {
+ "type": "text",
+ "default": IDGen.generate,
+ "unique": true,
+ },
+ "protected":
+ {
+ "type": "bool",
+ "default": false,
+ },
+ "name":
+ {
+ "type": "text",
+ "default": NameGenerator.generate,
+ "required": true,
+ "unique": true,
+ "protect": true,
+ "validation": {"min_length": 1},
+ },
+ "display_name":
+ {
+ "type": "text",
+ "default": "",
+ },
+ "nicknames":
+ {
+ "type": "text",
+ "default": "",
+ },
+ "description":
+ {
+ "type": "textarea",
+ "default": "",
+ "rows": 4,
+ },
+ "portraits":
+ {
+ "type": "list",
+ "default": [],
+ "minItems": 1,
+ "data_schema": PORTRAIT,
+ },
+ "default_portrait": {"type": "string", "default": "", "validation": {}}
+ },
+ "layouts":
+ {
+ "default":
+ {
+ "fields": ["name", "description"],
+ },
+ "list_item":
+ {
+ "fields": ["name", "description"],
+ "actions": ["edit", "duplicate", "delete"],
+ }
+ }
+}
+
+static var PORTRAIT: Dictionary = {
+ "title": "Portrait",
+ "type": "object",
+ "properties":
+ {
+ "name":
+ {
+ "type": "text",
+ "default": "new portrait",
+ "required": true,
+ "unique": true,
+ },
+ "protected":
+ {
+ "type": "bool",
+ "default": false,
+ "tooltip": "Prevent deletion",
+ },
+ "type":
+ {
+ "type": "dropdown",
+ "options": ["static", "animated"],
+ "default": "static",
+ "required": true,
+ },
+ "mirror":
+ {
+ "type": "bool",
+ "default": false,
+ "tooltip": "Mirror the image horizontally",
+ },
+ "offset":
+ {
+ "type": "vector2",
+ "default": Vector2.ZERO,
+ },
+ "image_path":
+ {
+ "type": "path",
+ "default": "",
+ "filter": ["*.png", "*.jpg", "*.jpeg"],
+ "required": true,
+ "condition": {"property": "type", "equals": "static"}
+ },
+ "timeline":
+ {
+ "type": "timeline",
+ "default": null,
+ "required": true,
+ "condition": {"property": "type", "equals": "animated"}
+ }
+ },
+ "layouts":
+ {
+ "default":
+ {
+ "fields": ["name", "type", "image_path"],
+ },
+ "compact":
+ {
+ "fields": ["name", "type", "image_path"],
+ }
+ }
+}
+
+static var ITEM: Dictionary = {
+ "title": "Portrait",
+ "type": "object",
+ "properties":
+ {
+ "id":
+ {
+ "type": "text",
+ "default": IDGen.generate,
+ "unique": true,
+ },
+ "name":
+ {
+ "type": "text",
+ "default": "new item",
+ "required": true,
+ "unique": true,
+ },
+ "description":
+ {
+ "type": "textarea",
+ "default": "",
+ "rows": 4,
+ },
+ },
+ "layouts":
+ {
+ "default":
+ {
+ "fields": ["name", "description"],
+ },
+ "list_item":
+ {
+ "fields": ["name", "description"],
+ "actions": ["edit", "duplicate", "delete"],
+ }
+ }
+}
+
+# TODO: Location backgrounds with variants
+static var LOCATION: Dictionary = {
+ "title": "Portrait",
+ "type": "object",
+ "properties":
+ {
+ "name":
+ {
+ "type": "text",
+ "default": "new location",
+ "required": true,
+ "unique": true,
+ },
+ "description":
+ {
+ "type": "textarea",
+ "default": "",
+ "rows": 4,
+ },
+ },
+ "layouts":
+ {
+ "default":
+ {
+ "fields": ["name", "description"],
+ },
+ "list_item":
+ {
+ "fields": ["name", "description"],
+ "actions": ["edit", "delete"],
+ }
+ }
+}
diff --git a/common/schemas.gd.uid b/common/schemas.gd.uid
new file mode 100644
index 00000000..016a09fc
--- /dev/null
+++ b/common/schemas.gd.uid
@@ -0,0 +1 @@
+uid://ctfwsw13u3127
diff --git a/common/storyline.gd b/common/storyline.gd
new file mode 100644
index 00000000..9a9edf32
--- /dev/null
+++ b/common/storyline.gd
@@ -0,0 +1,180 @@
+class_name StorylineDocument extends InspectableStorylineObject
+
+signal node_added
+signal node_removed
+signal content_changed
+signal undo_redo_changed
+
+var id: String = IDGen.generate()
+var name: String = ""
+var nodes: Array[InspectableNode] = []
+var file_path: String = ""
+var is_dirty: bool = false
+
+
+func _init(sname: String, sfile_path: String = "") -> void:
+ name = sname
+ file_path = sfile_path
+
+ var command_manager = CommandManager.new()
+ command_manager.command_executed.connect(_on_command_executed)
+ command_manager.undone.connect(_on_undo)
+ command_manager.redone.connect(_on_redo)
+
+ 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 " % node.id)
+ return
+
+ nodes.erase(node)
+ node_removed.emit()
+
+
+func create_node(node_type: String) -> InspectableNode:
+ var node = NodeBucket.create_node(node_type, history)
+ _register_node(node)
+ return node
+
+
+func get_node(node_id: String) -> InspectableNode:
+ for node: InspectableNode in nodes:
+ if node.get_property_value("id") == node_id:
+ return node
+ return null
+
+
+func initialize_properties() -> void:
+ var default_narrator: Dictionary = (
+ ListItemObject.new(Schemas.CHARACTER, {"name": "Narrator", "protected": true})._to_dict()
+ )
+
+ define_property(
+ "characters",
+ [default_narrator],
+ "list",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": true,
+ "editable": true,
+ "data_schema": Schemas.CHARACTER,
+ "layout": "list_item"
+ },
+ )
+
+ define_property(
+ "variables",
+ [],
+ "list",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": true,
+ "editable": true,
+ "data_schema": Schemas.VARIABLE,
+ "layout": "list_item"
+ }
+ )
+
+ define_property(
+ "items",
+ [],
+ "list",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": true,
+ "editable": true,
+ "data_schema": Schemas.ITEM,
+ "layout": "list_item"
+ }
+ )
+
+ define_property(
+ "locations",
+ [],
+ "list",
+ {
+ "visible_in_graph": false,
+ "visible_in_inspector": true,
+ "editable": true,
+ "data_schema": Schemas.LOCATION,
+ "layout": "list_item"
+ }
+ )
+
+
+func get_type() -> String:
+ return "storyline"
+
+
+func get_settings() -> Dictionary:
+ return {}
+
+
+func _on_property_changed(_pname: String, _old_value: Variant, _new_value: Variant) -> void:
+ is_dirty = true
+ content_changed.emit()
+
+
+func build_graph_preview() -> Array[Control]:
+ return []
+
+
+func _on_command_executed():
+ is_dirty = true
+ content_changed.emit()
+ undo_redo_changed.emit()
+
+
+func _on_undo():
+ content_changed.emit()
+ undo_redo_changed.emit()
+
+
+func _on_redo():
+ is_dirty = true
+ content_changed.emit()
+ undo_redo_changed.emit()
+
+
+func _create_default_nodes() -> void:
+ var defaults := ["root", "sentence", "text"]
+ for node_type: String in defaults:
+ var node = NodeBucket.create_node(node_type, history)
+ _register_node(node)
+
+
+func _register_node(node: InspectableNode) -> void:
+ if not node:
+ return
+ if not node in nodes:
+ nodes.append(node)
+ node.storyline_id = id
+ node.add_observer(_on_node_property_changed)
+ node_added.emit()
+
+
+func _on_node_property_changed(_node: InspectableNode, _property: String) -> void:
+ # Maybe useless
+ is_dirty = true
+
+
+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()
+ dict["nodes"].append(node._to_dict())
+
+ dict["root_node_id"] = root_node_id
+
+ return dict
diff --git a/common/storyline.gd.uid b/common/storyline.gd.uid
new file mode 100644
index 00000000..33257a8d
--- /dev/null
+++ b/common/storyline.gd.uid
@@ -0,0 +1 @@
+uid://cv5yppp8eqjja
diff --git a/common/ui/buttons/close_button.tscn b/common/ui/buttons/close_button.tscn
index cfbcc8d2..83938832 100644
--- a/common/ui/buttons/close_button.tscn
+++ b/common/ui/buttons/close_button.tscn
@@ -1,11 +1,15 @@
-[gd_scene load_steps=2 format=3 uid="uid://dspmmme0jspdx"]
+[gd_scene load_steps=3 format=3 uid="uid://dspmmme0jspdx"]
-[ext_resource type="Texture2D" uid="uid://c7vdr4e0mxst6" path="res://ui/assets/icons/close_icon.png" id="1_jkx7d"]
+[ext_resource type="Texture2D" uid="uid://kk5em170gn2b" path="res://ui/assets/icons/ui_close.svg" id="1_ai8vd"]
-[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
+[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/buttons/delete_button.tscn b/common/ui/buttons/delete_button.tscn
index e2a40158..2b52e5bc 100644
--- a/common/ui/buttons/delete_button.tscn
+++ b/common/ui/buttons/delete_button.tscn
@@ -3,10 +3,11 @@
[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
+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/editor_properties/color/color.gd b/common/ui/editor_properties/color/color.gd
new file mode 100644
index 00000000..88a1a79d
--- /dev/null
+++ b/common/ui/editor_properties/color/color.gd
@@ -0,0 +1,71 @@
+extends Field
+
+# Sweetie 16 Palette by GrafxKid
+const COLORS = [
+ "#000000",
+ "#b13e53",
+ "#ef7d57",
+ "#ffcd75",
+ "#a7f070",
+ "#38b764",
+ "#257179",
+ "#3b5dc9",
+ "#41a6f6",
+ "#73eff7",
+ "#94b0c2",
+ "#566c86"
+]
+
+@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 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:
+ color = Color(value)
+ _update_preview()
+
+
+func get_value() -> Variant:
+ return color.to_html()
+
+
+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_changed(color_string)
+ emit_value_committed(color_string)
+ emit_preview_changed()
+
+
+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()
diff --git a/common/ui/editor_properties/color/color.gd.uid b/common/ui/editor_properties/color/color.gd.uid
new file mode 100644
index 00000000..4611c73b
--- /dev/null
+++ b/common/ui/editor_properties/color/color.gd.uid
@@ -0,0 +1 @@
+uid://cwkwnc22br2nn
diff --git a/common/ui/editor_properties/color/color.tscn b/common/ui/editor_properties/color/color.tscn
new file mode 100644
index 00000000..a00ca7cd
--- /dev/null
+++ b/common/ui/editor_properties/color/color.tscn
@@ -0,0 +1,58 @@
+[gd_scene load_steps=3 format=3 uid="uid://dywxnyncdf55t"]
+
+[ext_resource type="Script" uid="uid://cwkwnc22br2nn" path="res://common/ui/editor_properties/color/color.gd" id="1_fkepi"]
+
+[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_fkepi"]
+
+[node name="Color" type="VBoxContainer"]
+anchors_preset = -1
+anchor_right = 0.011000001
+anchor_bottom = 0.010000001
+offset_right = 0.5999985
+size_flags_horizontal = 3
+size_flags_vertical = 3
+script = ExtResource("1_fkepi")
+
+[node name="HBox" type="HBoxContainer" parent="."]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="ColorPickerButton" type="Button" parent="HBox"]
+clip_contents = true
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="MarginContainer" type="MarginContainer" parent="HBox/ColorPickerButton"]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 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="ColorPreview" type="ColorRect" parent="HBox/ColorPickerButton/MarginContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+mouse_filter = 2
+
+[node name="ColorPopup" type="PopupPanel" parent="HBox/ColorPickerButton"]
+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="HBox/ColorPickerButton/ColorPopup"]
+offset_right = 8.0
+offset_bottom = 8.0
+
+[node name="ColorSelector" type="GridContainer" parent="HBox/ColorPickerButton/ColorPopup/PanelContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+columns = 4
+
+[connection signal="resized" from="." to="." method="_on_resized"]
+[connection signal="pressed" from="HBox/ColorPickerButton" to="." method="_on_color_picker_button_pressed"]
diff --git a/common/ui/editor_properties/color/color_swatch.gd b/common/ui/editor_properties/color/color_swatch.gd
new file mode 100644
index 00000000..766e2792
--- /dev/null
+++ b/common/ui/editor_properties/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/ui/editor_properties/color/color_swatch.gd.uid b/common/ui/editor_properties/color/color_swatch.gd.uid
new file mode 100644
index 00000000..46c1e9f6
--- /dev/null
+++ b/common/ui/editor_properties/color/color_swatch.gd.uid
@@ -0,0 +1 @@
+uid://c5p813dybhgwt
diff --git a/common/ui/editor_properties/color/color_swatch.tscn b/common/ui/editor_properties/color/color_swatch.tscn
new file mode 100644
index 00000000..0af7b3ad
--- /dev/null
+++ b/common/ui/editor_properties/color/color_swatch.tscn
@@ -0,0 +1,18 @@
+[gd_scene load_steps=2 format=3 uid="uid://psbrljdmbtg6"]
+
+[ext_resource type="Script" uid="uid://c5p813dybhgwt" path="res://common/ui/editor_properties/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/ui/editor_properties/color/index.gd b/common/ui/editor_properties/color/index.gd
new file mode 100644
index 00000000..16e4905b
--- /dev/null
+++ b/common/ui/editor_properties/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")}
diff --git a/common/ui/editor_properties/color/index.gd.uid b/common/ui/editor_properties/color/index.gd.uid
new file mode 100644
index 00000000..f214138a
--- /dev/null
+++ b/common/ui/editor_properties/color/index.gd.uid
@@ -0,0 +1 @@
+uid://7bxfucw58wy0
diff --git a/common/ui/editor_properties/context/index.gd b/common/ui/editor_properties/context/index.gd
new file mode 100644
index 00000000..c3d26b26
--- /dev/null
+++ b/common/ui/editor_properties/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/ui/editor_properties/context/index.gd.uid b/common/ui/editor_properties/context/index.gd.uid
new file mode 100644
index 00000000..4e460bcb
--- /dev/null
+++ b/common/ui/editor_properties/context/index.gd.uid
@@ -0,0 +1 @@
+uid://cg62bw4hys8fb
diff --git a/common/ui/editor_properties/dropdown/dropdown_field.gd b/common/ui/editor_properties/dropdown/dropdown_field.gd
new file mode 100644
index 00000000..d110bedc
--- /dev/null
+++ b/common/ui/editor_properties/dropdown/dropdown_field.gd
@@ -0,0 +1,197 @@
+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 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 = 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()
+ _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 = _get_storyline()
+ if storyline:
+ storyline.add_observer(_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(_obj: InspectableObject, 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
+ _populate_options()
+ _set_value_to_existing()
+
+
+func _on_storyline_command_executed() -> void:
+ _populate_options()
+ _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 in options:
+ option_button.add_item(str(option))
+
+ _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 ""
+ set_value(default_value)
+ return
+
+ var current_value: Variant = _binding.property.get_value()
+ if current_value == null:
+ return
+ set_value(current_value)
+
+
+func set_static_options(options: Variant) -> void:
+ _static_options = _normalize_options_array(options)
+ if is_node_ready():
+ _populate_options()
+ else:
+ call_deferred("_populate_options")
+
+
+func _normalize_options_array(source: Variant) -> Array:
+ if source is Array:
+ return (source as Array).duplicate()
+ if source is PackedStringArray:
+ return Array(source)
+ if source is PackedInt32Array:
+ var arr: Array = []
+ for value in source:
+ 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:
+ var result: Array = []
+
+ # Get storyline from property owner
+ var storyline = _get_storyline()
+ if not storyline:
+ return result
+
+ # Get the list property from storyline
+ var list_property: Property = storyline.get_property(source)
+ if not list_property:
+ return result
+
+ var list_value = list_property.get_value()
+ if not list_value is Array:
+ return result
+
+ # Extract names from list items
+ for item in list_value:
+ if item is Dictionary:
+ var entry_name: Variant = item.get("name", item.get("id", ""))
+ if entry_name is Dictionary:
+ entry_name = entry_name.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 = _binding.owner
+
+ # If owner is a node, get its storyline
+ if story_owner is InspectableNode:
+ var node: InspectableNode = story_owner
+ var storyline_id = node.storyline_id
+ if storyline_id:
+ return StorylineManager.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 = option_button.get_item_text(index)
+ emit_value_changed(text)
+ emit_value_committed(text)
diff --git a/common/ui/editor_properties/dropdown/dropdown_field.gd.uid b/common/ui/editor_properties/dropdown/dropdown_field.gd.uid
new file mode 100644
index 00000000..3793e12f
--- /dev/null
+++ b/common/ui/editor_properties/dropdown/dropdown_field.gd.uid
@@ -0,0 +1 @@
+uid://c4n8o9p0q1r2t
diff --git a/common/ui/editor_properties/dropdown/dropdown_field.tscn b/common/ui/editor_properties/dropdown/dropdown_field.tscn
new file mode 100644
index 00000000..82617491
--- /dev/null
+++ b/common/ui/editor_properties/dropdown/dropdown_field.tscn
@@ -0,0 +1,14 @@
+[gd_scene load_steps=2 format=3 uid="uid://cqhk5p4a8jx0b"]
+
+[ext_resource type="Script" path="res://common/ui/editor_properties/dropdown/dropdown_field.gd" id="1_dropdown"]
+
+[node name="VBoxContainer" type="VBoxContainer"]
+offset_right = 76.0
+offset_bottom = 29.0
+size_flags_horizontal = 3
+script = ExtResource("1_dropdown")
+
+[node name="OptionButton" type="OptionButton" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
diff --git a/common/ui/editor_properties/dropdown/index.gd b/common/ui/editor_properties/dropdown/index.gd
new file mode 100644
index 00000000..c3ffc2ff
--- /dev/null
+++ b/common/ui/editor_properties/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")}
diff --git a/common/ui/editor_properties/dropdown/index.gd.uid b/common/ui/editor_properties/dropdown/index.gd.uid
new file mode 100644
index 00000000..f59e2aef
--- /dev/null
+++ b/common/ui/editor_properties/dropdown/index.gd.uid
@@ -0,0 +1 @@
+uid://b3m7n8p9q0r1s
diff --git a/common/ui/editor_properties/dynamic/dynamic_field.gd b/common/ui/editor_properties/dynamic/dynamic_field.gd
new file mode 100644
index 00000000..0b7c8e2f
--- /dev/null
+++ b/common/ui/editor_properties/dynamic/dynamic_field.gd
@@ -0,0 +1,82 @@
+extends Field
+
+var _case_property: String = ""
+var _cases: Dictionary = {}
+
+@onready var field_container: HBoxContainer = %FieldContainer
+var _field: Field
+
+
+func _ready() -> void:
+ super._ready()
+
+
+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 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 = null
+ if new_field is Field:
+ _field = new_field
+ _binding.property.call_deferred("bind_field", new_field, _binding.owner)
+ field_container.add_child(new_field)
+
+
+func _on_property_case_changed(_old_value: Variant, _new_value: Variant) -> void:
+ _update_case_field()
diff --git a/common/ui/editor_properties/dynamic/dynamic_field.gd.uid b/common/ui/editor_properties/dynamic/dynamic_field.gd.uid
new file mode 100644
index 00000000..d7f16ac5
--- /dev/null
+++ b/common/ui/editor_properties/dynamic/dynamic_field.gd.uid
@@ -0,0 +1 @@
+uid://6vwlwawmd6qw
diff --git a/common/ui/editor_properties/dynamic/dynamic_field.tscn b/common/ui/editor_properties/dynamic/dynamic_field.tscn
new file mode 100644
index 00000000..092ba791
--- /dev/null
+++ b/common/ui/editor_properties/dynamic/dynamic_field.tscn
@@ -0,0 +1,12 @@
+[gd_scene load_steps=2 format=3 uid="uid://bmpb6yh0x0sup"]
+
+[ext_resource type="Script" uid="uid://6vwlwawmd6qw" path="res://common/ui/editor_properties/dynamic/dynamic_field.gd" id="1_g3o7u"]
+
+[node name="DynamicField" type="VBoxContainer"]
+offset_right = 40.0
+offset_bottom = 40.0
+script = ExtResource("1_g3o7u")
+
+[node name="FieldContainer" type="HBoxContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
diff --git a/common/ui/editor_properties/dynamic/index.gd b/common/ui/editor_properties/dynamic/index.gd
new file mode 100644
index 00000000..8307cdbe
--- /dev/null
+++ b/common/ui/editor_properties/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("d1929dff")}
diff --git a/common/ui/editor_properties/dynamic/index.gd.uid b/common/ui/editor_properties/dynamic/index.gd.uid
new file mode 100644
index 00000000..80131674
--- /dev/null
+++ b/common/ui/editor_properties/dynamic/index.gd.uid
@@ -0,0 +1 @@
+uid://b6rmavumgsbkj
diff --git a/common/ui/editor_properties/field.gd b/common/ui/editor_properties/field.gd
new file mode 100644
index 00000000..384caac9
--- /dev/null
+++ b/common/ui/editor_properties/field.gd
@@ -0,0 +1,78 @@
+@abstract
+class_name Field extends VBoxContainer
+
+signal value_changed(new_value: Variant)
+signal value_committed(new_value: Variant)
+signal preview_changed
+
+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:
+ value_changed.emit(value)
+
+
+func emit_preview_changed() -> void:
+ preview_changed.emit()
+
+
+func emit_value_committed(value: Variant) -> void:
+ if settings.get("unique"):
+ var field_owner: InspectableObject = _binding.owner
+ var property_name: String = _binding.property.name
+ var property_value: Variant = _binding.property.get_value()
+
+ if field_owner is ListItemObject:
+ var list_field: ListField = field_owner.list_field
+
+ for item: ListItemObject in list_field._list_items:
+ var property: Property = item.get_property(property_name)
+ if property_value == property.get_value():
+ continue
+ if property and property.get_value() == value:
+ set_value(property_value)
+ return
+
+ value_committed.emit(value)
+
+
+func set_editable(_is_editable: bool) -> void:
+ pass
+
+
+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 after_commit(_value: Variant) -> void:
+ pass
+
+
+@abstract func set_value(value: Variant) -> void
+@abstract func get_value() -> Variant
diff --git a/common/ui/editor_properties/field.gd.uid b/common/ui/editor_properties/field.gd.uid
new file mode 100644
index 00000000..85df71f3
--- /dev/null
+++ b/common/ui/editor_properties/field.gd.uid
@@ -0,0 +1 @@
+uid://7su6leuvyoaa
diff --git a/common/ui/editor_properties/field_binding.gd b/common/ui/editor_properties/field_binding.gd
new file mode 100644
index 00000000..4160ce51
--- /dev/null
+++ b/common/ui/editor_properties/field_binding.gd
@@ -0,0 +1,140 @@
+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)
+ if field.has_signal("value_committed"):
+ field.value_committed.connect(_on_field_value_committed)
+ if field.has_signal("preview_changed"):
+ field.preview_changed.connect(_on_field_preview_changed)
+ 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.has_signal("value_committed")
+ and 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
+ _is_syncing = true
+ field.set_value(property.get_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("editable", true)
+ if property.is_intput_connected():
+ is_editable = false
+ field.set_editable(is_editable)
+
+
+func _on_field_value_changed(value: Variant) -> void:
+ if _is_syncing:
+ return
+ _process_field_value(value, false)
+
+
+func _on_field_value_committed(value: Variant) -> void:
+ if _is_syncing:
+ return
+ _process_field_value(value, true)
+
+
+func _on_field_preview_changed() -> void:
+ owner.rebuild_preview()
+
+
+func _process_field_value(value: Variant, is_commit: bool) -> void:
+ if not property or descriptor == null:
+ return
+ var validation_result = descriptor.validate(value)
+ if not validation_result.is_valid:
+ field.display_error(validation_result.message)
+ return
+ field.clear_error()
+ var formatted_value = descriptor.format(value)
+ if owner and not is_commit:
+ return
+ _is_syncing = true
+ if owner:
+ owner.set_property_value(property.name, formatted_value)
+ else:
+ property.set_value(formatted_value)
+ _is_syncing = false
+ _sync_from_property()
+ if is_commit:
+ field.after_commit(property.get_value())
+
+
+func _on_property_value_changed(_old_value: Variant, new_value: Variant) -> void:
+ if _is_released or _is_syncing or not field.is_node_ready():
+ return
+ _is_syncing = true
+ field.set_value(new_value)
+ field.clear_error()
+ _is_syncing = false
+
+
+func _on_field_tree_exiting() -> void:
+ release()
diff --git a/common/ui/editor_properties/field_binding.gd.uid b/common/ui/editor_properties/field_binding.gd.uid
new file mode 100644
index 00000000..4cfef785
--- /dev/null
+++ b/common/ui/editor_properties/field_binding.gd.uid
@@ -0,0 +1 @@
+uid://dkp1ix1cag4u5
diff --git a/common/ui/editor_properties/field_bucket.gd b/common/ui/editor_properties/field_bucket.gd
new file mode 100644
index 00000000..d8262df5
--- /dev/null
+++ b/common/ui/editor_properties/field_bucket.gd
@@ -0,0 +1,132 @@
+# Autoload
+extends Node
+
+const DEFAULT_FIELDS_LOCATION := "res://common/ui/editor_properties/"
+
+var _descriptors: Dictionary = {}
+var _is_initialized: bool = false
+var _next_type_id: int = 1
+
+
+func register_descriptor(descriptor: FieldDescriptor) -> void:
+ if descriptor == null:
+ push_warning("Attempted to register a null FieldDescriptor.")
+ return
+ if descriptor.name.is_empty():
+ push_warning("FieldDescriptor missing name property.")
+ return
+ if _descriptors.has(descriptor.name):
+ push_warning("FieldDescriptor '%s' already registered." % descriptor.name)
+ return
+ if descriptor.default_settings == null:
+ descriptor.default_settings = {}
+ descriptor.type_id = _next_type_id
+ _next_type_id += 1
+ _descriptors[descriptor.name] = 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 := get_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 := get_descriptor(field_name)
+ if descriptor:
+ return descriptor.instantiate_field()
+ return null
+
+
+func safe_create_field(field_name: String) -> Control:
+ var new_field: Field = create_field(field_name)
+ if new_field:
+ return new_field
+
+ var fb_field: Control = Label.new()
+ fb_field.theme_type_variation = "WarnLabel"
+ fb_field.text = "Unknown property type"
+ return fb_field
+
+
+func get_scene(field_name: String) -> PackedScene:
+ var descriptor := get_descriptor(field_name)
+ if descriptor:
+ return descriptor.scene
+ return null
+
+
+func get_metadata(field_name: String) -> Dictionary:
+ var descriptor := get_descriptor(field_name)
+ if descriptor:
+ return descriptor.to_metadata()
+ return {}
+
+
+func get_type_id(field_name: String) -> int:
+ var descriptor := get_descriptor(field_name)
+ return descriptor.type_id if descriptor else -1
+
+
+func get_descriptor(field_name: String) -> FieldDescriptor:
+ _ensure_initialized()
+ return _descriptors.get(field_name)
+
+
+func refresh_registry() -> void:
+ _is_initialized = false
+ _descriptors.clear()
+ _next_type_id = 1
+ _ensure_initialized()
+
+
+func _ensure_initialized() -> void:
+ if _is_initialized:
+ return
+ _search_fields()
+ _is_initialized = true
+
+
+func _search_fields() -> 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 = load(script_path)
+ if script == null:
+ push_warning("Failed to load field indexer at %s" % script_path)
+ continue
+ var indexer = script.new()
+ var descriptor := _descriptor_from_indexer(indexer)
+ if descriptor:
+ descriptor.default_settings = (
+ descriptor.default_settings.duplicate(true) if descriptor.default_settings else {}
+ )
+ register_descriptor(descriptor)
+
+
+func _descriptor_from_indexer(indexer) -> FieldDescriptor:
+ var descriptor
+ 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
+ return FieldDescriptor.new(descriptor_name, scene, metadata)
diff --git a/common/ui/editor_properties/field_bucket.gd.uid b/common/ui/editor_properties/field_bucket.gd.uid
new file mode 100644
index 00000000..070f8643
--- /dev/null
+++ b/common/ui/editor_properties/field_bucket.gd.uid
@@ -0,0 +1 @@
+uid://cmte2g7n7ogyd
diff --git a/common/ui/editor_properties/field_descriptor.gd b/common/ui/editor_properties/field_descriptor.gd
new file mode 100644
index 00000000..66330228
--- /dev/null
+++ b/common/ui/editor_properties/field_descriptor.gd
@@ -0,0 +1,76 @@
+class_name FieldDescriptor extends RefCounted
+
+const FIELD_OBJECT_TYPE := 1
+
+var name: String
+var scene: PackedScene
+var color: Color = Color.WHITE
+var default_settings: Dictionary = {}
+var validators: Array[Callable] = []
+var formatter: Callable
+var type_id: int = -1
+
+
+func _init(p_name: String = "", p_scene: PackedScene = null, metadata: Dictionary = {}) -> void:
+ name = p_name
+ scene = p_scene
+ var raw_color = metadata.get("color")
+ if raw_color is Color:
+ color = raw_color
+ elif raw_color is String and not raw_color.is_empty():
+ color = Color(raw_color)
+ default_settings = metadata.get("default_settings", {}).duplicate(true)
+ var validator_list = metadata.get("validators")
+ if validator_list is Array:
+ for validator in validator_list:
+ register_validator(validator)
+ var raw_formatter = metadata.get("formatter")
+ if raw_formatter is Callable and raw_formatter.is_valid():
+ formatter = raw_formatter
+
+
+func instantiate_field() -> Field:
+ if not scene:
+ return null
+ var instance = scene.instantiate()
+ if instance is Field:
+ return instance
+ push_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):
+ for validator in validators:
+ var result = 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 := (result as String).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:
+ return {
+ "name": name,
+ "type": FIELD_OBJECT_TYPE,
+ "color": color,
+ "type_id": type_id,
+ "default_settings": default_settings.duplicate(true),
+ }
diff --git a/common/ui/editor_properties/field_descriptor.gd.uid b/common/ui/editor_properties/field_descriptor.gd.uid
new file mode 100644
index 00000000..7cda8633
--- /dev/null
+++ b/common/ui/editor_properties/field_descriptor.gd.uid
@@ -0,0 +1 @@
+uid://c1vc0itqeuxic
diff --git a/common/ui/editor_properties/field_validation_result.gd b/common/ui/editor_properties/field_validation_result.gd
new file mode 100644
index 00000000..1b7e5184
--- /dev/null
+++ b/common/ui/editor_properties/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/ui/editor_properties/field_validation_result.gd.uid b/common/ui/editor_properties/field_validation_result.gd.uid
new file mode 100644
index 00000000..c14f8393
--- /dev/null
+++ b/common/ui/editor_properties/field_validation_result.gd.uid
@@ -0,0 +1 @@
+uid://c6ue2ruq13my7
diff --git a/common/ui/editor_properties/list/index.gd b/common/ui/editor_properties/list/index.gd
new file mode 100644
index 00000000..e62b459a
--- /dev/null
+++ b/common/ui/editor_properties/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")}
diff --git a/common/ui/editor_properties/list/index.gd.uid b/common/ui/editor_properties/list/index.gd.uid
new file mode 100644
index 00000000..b252ebed
--- /dev/null
+++ b/common/ui/editor_properties/list/index.gd.uid
@@ -0,0 +1 @@
+uid://d5o9p0q1r2s3u
diff --git a/common/ui/editor_properties/list/list_field.gd b/common/ui/editor_properties/list/list_field.gd
new file mode 100644
index 00000000..f68cd47c
--- /dev/null
+++ b/common/ui/editor_properties/list/list_field.gd
@@ -0,0 +1,216 @@
+class_name ListField extends Field
+
+var _list_items: Array[ListItemObject] = []
+var _hide_items: Array[int] = []
+var _data_schema: Dictionary = {}
+var _layout: String = "default"
+var _command_manager: CommandManager
+
+@onready var items_container: VBoxContainer = %ItemsContainer
+
+
+func _ready() -> void:
+ super._ready()
+ _command_manager = CommandManager.new()
+
+
+func hide_item(idx: int) -> void:
+ _hide_items.append(idx)
+ _rebuild_ui()
+
+
+func show_all_items() -> void:
+ _hide_items.clear()
+ _rebuild_ui()
+
+
+func set_value(value: Variant) -> void:
+ _list_items.clear()
+ for property_data: Dictionary in value:
+ var new_item: ListItemObject = ListItemObject.new(_data_schema, {}, _command_manager)
+ new_item.list_field = self
+ new_item._from_dict(property_data)
+ _connect_item_observer(new_item)
+ _list_items.append(new_item)
+
+ _rebuild_ui()
+
+
+func _connect_item_observer(item: ListItemObject) -> void:
+ item.add_observer(_on_item_changed)
+
+
+func _on_item_changed(_item: ListItemObject, prop_name: String) -> void:
+ if _has_dependent_fields(prop_name):
+ call_deferred("_rebuild_ui")
+
+ _emit_snapshot()
+
+
+func get_value() -> Variant:
+ var result: Array = []
+ for item: ListItemObject in _list_items:
+ result.append(item._to_dict())
+ return result
+
+
+func get_item_index(item: ListItemObject) -> int:
+ return _list_items.find(item)
+
+
+func set_editable(is_editable: bool) -> void:
+ if not is_instance_valid(items_container):
+ return
+
+ for child in items_container.get_children():
+ if child.has_method("set_editable"):
+ child.set_editable(is_editable)
+
+
+func _on_initialize() -> void:
+ if _binding and _binding.property:
+ _initialize_from_property(_binding.property)
+
+ if not is_node_ready():
+ await ready
+
+ _rebuild_ui()
+
+
+func _initialize_from_property(property: Property) -> void:
+ _data_schema = property.get_settings_value("data_schema", {})
+ _layout = property.get_settings_value("layout", "default")
+
+
+func _rebuild_ui() -> void:
+ if not is_instance_valid(items_container):
+ return
+
+ _populate_items_container()
+
+
+func _clear_items_container() -> void:
+ for child in items_container.get_children():
+ items_container.remove_child(child)
+ child.queue_free()
+
+
+func _populate_items_container() -> void:
+ _clear_items_container()
+
+ for i in range(_list_items.size()):
+ if i in _hide_items:
+ continue
+ var item_ui = _create_item_ui(i)
+ if item_ui:
+ items_container.add_child(item_ui)
+
+
+func _create_item_ui(index: int) -> Control:
+ var item = _list_items[index]
+
+ var item_container = PanelContainer.new()
+ item_container.theme_type_variation = "ListItemContainer"
+
+ var main_vbox = VBoxContainer.new()
+ item_container.add_child(main_vbox)
+
+ var content = LayoutManager.create_layout(_data_schema, item, self, _layout)
+ if content:
+ main_vbox.add_child(content)
+
+ return item_container
+
+
+func _has_dependent_fields(field_name: String) -> bool:
+ var properties = _data_schema.get("properties", {})
+
+ for prop_name in properties:
+ var prop_config = properties[prop_name]
+
+ if _has_condition_dependency(prop_config, field_name):
+ return true
+
+ if _has_variant_dependency(prop_config, field_name):
+ return true
+
+ return false
+
+
+func _has_condition_dependency(prop_config: Dictionary, field_name: String) -> bool:
+ if not prop_config.has("condition"):
+ return false
+
+ return prop_config["condition"].get("property") == field_name
+
+
+func _has_variant_dependency(prop_config: Dictionary, field_name: String) -> bool:
+ if not prop_config.has("cases"):
+ return false
+
+ var variants = prop_config["cases"]
+ return variants.get("property") == field_name
+
+
+func _on_edit_item(index: int) -> void:
+ print("edit item %s" % index)
+
+
+func _on_duplicate_item(index: int) -> void:
+ if not _is_valid_index(index):
+ return
+ var item_data: Dictionary = _list_items[index]._to_dict()
+ var new_item: ListItemObject = ListItemObject.new(_data_schema, {}, _command_manager, {})
+ new_item.list_field = self
+ new_item._from_dict(item_data)
+ new_item.make_all_values_unique()
+ _connect_item_observer(new_item)
+ _list_items.insert(index + 1, new_item)
+ _rebuild_ui()
+ _emit_snapshot()
+
+
+func _on_delete_item(index: int) -> void:
+ if not _is_valid_index(index):
+ return
+
+ var item = _list_items[index]
+
+ if item.is_protected():
+ push_warning("Cannot delete protected item")
+ return
+
+ if index >= 0 and index < _list_items.size():
+ _list_items.remove_at(index)
+ _rebuild_ui()
+ _emit_snapshot()
+
+
+func _is_valid_index(index: int) -> bool:
+ return index >= 0 and index < _list_items.size()
+
+
+func _emit_snapshot() -> void:
+ # Emit a deep copy of the raw store as the authoritative value
+ emit_value_changed(get_value())
+ emit_value_committed(get_value())
+
+
+func undo() -> void:
+ if _command_manager:
+ _command_manager.undo()
+ _rebuild_ui()
+
+
+func redo() -> void:
+ if _command_manager:
+ _command_manager.redo()
+ _rebuild_ui()
+
+
+func can_undo() -> bool:
+ return _command_manager and _command_manager.can_undo()
+
+
+func can_redo() -> bool:
+ return _command_manager and _command_manager.can_redo()
diff --git a/common/ui/editor_properties/list/list_field.gd.uid b/common/ui/editor_properties/list/list_field.gd.uid
new file mode 100644
index 00000000..f74b59ec
--- /dev/null
+++ b/common/ui/editor_properties/list/list_field.gd.uid
@@ -0,0 +1 @@
+uid://e6p0q1r2s3t4v
diff --git a/common/ui/editor_properties/list/list_field.tscn b/common/ui/editor_properties/list/list_field.tscn
new file mode 100644
index 00000000..6ff68a59
--- /dev/null
+++ b/common/ui/editor_properties/list/list_field.tscn
@@ -0,0 +1,14 @@
+[gd_scene load_steps=2 format=3 uid="uid://d2s4kv1234abc"]
+
+[ext_resource type="Script" uid="uid://bcb45yus8vyd4" path="res://common/ui/editor_properties/list/list_field.gd" id="1_list"]
+
+[node name="VBoxContainer" type="VBoxContainer"]
+offset_right = 300.0
+offset_bottom = 100.0
+size_flags_horizontal = 3
+script = ExtResource("1_list")
+
+[node name="ItemsContainer" type="VBoxContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
diff --git a/common/ui/editor_properties/list/list_item_object.gd b/common/ui/editor_properties/list/list_item_object.gd
new file mode 100644
index 00000000..dd6b6c9e
--- /dev/null
+++ b/common/ui/editor_properties/list/list_item_object.gd
@@ -0,0 +1,177 @@
+class_name ListItemObject extends InspectableObject
+
+var _schema: Dictionary = {}
+var _initial_data: Dictionary = {}
+
+var list_field: ListField
+
+
+func _init(
+ schema: Dictionary,
+ initial_data: Dictionary = {},
+ command_manager: CommandManager = null,
+ _settings: Dictionary = {}
+) -> void:
+ _schema = schema
+ _initial_data = initial_data
+ settings = _settings
+ super._init(command_manager)
+
+
+func initialize_properties() -> void:
+ var properties_config: Dictionary = _schema.get("properties", {})
+
+ for prop_name in properties_config:
+ var prop_config = properties_config[prop_name]
+ _define_property_from_config(prop_name, prop_config)
+
+
+func _define_property_from_config(prop_name: String, prop_config: Dictionary) -> void:
+ var prop_value: Variant = _get_initial_value(prop_name, prop_config)
+ var prop_type = prop_config.get("type", "text")
+ var category = prop_config.get("category", "General")
+
+ var prop_settings: Dictionary = prop_config.duplicate()
+ prop_settings.erase("type")
+ prop_settings.erase("default")
+ prop_settings.erase("category")
+
+ define_property(prop_name, prop_value, prop_type, prop_settings, category)
+
+
+func make_all_values_unique() -> void:
+ var properties_config: Dictionary = _schema.get("properties", {})
+ for prop_name in properties_config:
+ var prop_config = properties_config[prop_name]
+ _make_property_unique(prop_name, prop_config)
+
+
+func _make_property_unique(prop_name: String, prop_config: Dictionary) -> void:
+ var must_be_unique: bool = prop_config.get("unique", false)
+ if not must_be_unique or not list_field:
+ return
+
+ var prop_value: Variant = get_property_value(prop_name)
+ var original_value: Variant = prop_value
+ var attempt: int = 0
+ var max_attempts: int = 1000
+
+ while _value_exists_in_list(prop_name, prop_value) and attempt < max_attempts:
+ attempt += 1
+
+ if original_value is String:
+ var end_num: Variant = _get_end_number(original_value)
+ if end_num == null:
+ prop_value = "%s %d" % [original_value, attempt]
+ else:
+ var base_str: String = original_value.left(-str(end_num).length()).strip_edges()
+ prop_value = "%s %d" % [base_str, end_num + attempt]
+ elif original_value is int:
+ prop_value = original_value + attempt
+ elif original_value is bool:
+ break
+
+ var property: Property = get_property(prop_name)
+ if property:
+ property.value = prop_value
+
+
+func _value_exists_in_list(prop_name: String, value: Variant) -> bool:
+ if not list_field:
+ return false
+
+ for item: ListItemObject in list_field._list_items:
+ if item == self:
+ continue
+
+ var item_prop: Property = item.get_property(prop_name)
+ if not item_prop:
+ continue
+
+ if item_prop.get_value() == value:
+ return true
+
+ return false
+
+
+func _get_end_number(value: String) -> Variant:
+ var regex = RegEx.new()
+ regex.compile("\\d+$")
+ var result: RegExMatch = regex.search(value)
+ if result:
+ return int(result.get_string())
+ return null
+
+
+func _get_initial_value(prop_name: String, prop_config: Dictionary) -> Variant:
+ if _initial_data.has(prop_name):
+ return _initial_data[prop_name]
+
+ var default: Variant = prop_config.get("default")
+
+ if default is Callable:
+ return default.call()
+
+ if default != null:
+ return default
+
+ return _get_default_for_type(prop_config.get("type", "text"))
+
+
+func _get_default_for_type(type: String) -> Variant:
+ match type:
+ "bool":
+ return false
+ "number", "int", "float":
+ return 0
+ "text", "textarea", "dropdown":
+ return ""
+ "list":
+ return []
+ "vector2":
+ return Vector2.ZERO
+ "vector3":
+ return Vector3.ZERO
+ _:
+ return null
+
+
+func get_schema() -> Dictionary:
+ return _schema
+
+
+func is_protected() -> bool:
+ return get_property("protected") and get_property_value("protected")
+
+
+func duplicate_item(command_manager: CommandManager = null) -> ListItemObject:
+ var duplicate_data = _to_dict()
+ duplicate_data.erase("$type")
+
+ if duplicate_data.has("name"):
+ duplicate_data["name"] = str(duplicate_data["name"]) + " (Copy)"
+
+ if (
+ duplicate_data.has("id")
+ and _schema.get("properties", {}).get("id", {}).get("default") is Callable
+ ):
+ var id_generator = _schema["properties"]["id"]["default"]
+ duplicate_data["id"] = id_generator.call()
+
+ var new_item: ListItemObject = ListItemObject.new(
+ _schema, duplicate_data, command_manager if command_manager else history
+ )
+ new_item.make_all_values_unique()
+ return new_item
+
+
+func get_type() -> String:
+ return _schema.get("title", "ListItem")
+
+
+func _on_property_changed(_pname: String, _old_value: Variant, _new_value: Variant) -> void:
+ pass
+
+
+func get_settings() -> Dictionary:
+ return {}
diff --git a/common/ui/editor_properties/list/list_item_object.gd.uid b/common/ui/editor_properties/list/list_item_object.gd.uid
new file mode 100644
index 00000000..8372d106
--- /dev/null
+++ b/common/ui/editor_properties/list/list_item_object.gd.uid
@@ -0,0 +1 @@
+uid://gn61ergh0wbq
diff --git a/common/ui/editor_properties/text/index.gd b/common/ui/editor_properties/text/index.gd
new file mode 100644
index 00000000..40379840
--- /dev/null
+++ b/common/ui/editor_properties/text/index.gd
@@ -0,0 +1,9 @@
+extends MonologueIndexer
+
+
+func get_scene() -> PackedScene:
+ return preload("uid://be0xxn5gocqjo")
+
+
+func get_metadata() -> Dictionary:
+ return {"name": "text", "type": ObjectType.FIELD, "color": Color("af85fdff")}
diff --git a/common/ui/editor_properties/text/index.gd.uid b/common/ui/editor_properties/text/index.gd.uid
new file mode 100644
index 00000000..c82c935b
--- /dev/null
+++ b/common/ui/editor_properties/text/index.gd.uid
@@ -0,0 +1 @@
+uid://86csxw8qmvb4
diff --git a/common/ui/editor_properties/text/text_field.gd b/common/ui/editor_properties/text/text_field.gd
new file mode 100644
index 00000000..cb27b8ca
--- /dev/null
+++ b/common/ui/editor_properties/text/text_field.gd
@@ -0,0 +1,49 @@
+extends Field
+
+@onready var line_edit: LineEdit = %LineEdit
+
+
+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)
+
+
+func _on_initialize() -> void:
+ line_edit.placeholder_text = settings.get("placeholder", "")
+
+
+func set_value(value: Variant) -> void:
+ if not is_node_ready():
+ await ready
+
+ line_edit.text = str(value)
+
+
+func get_value() -> Variant:
+ return line_edit.text
+
+
+func set_editable(is_editable: bool) -> void:
+ line_edit.editable = is_editable
+
+
+func display_error(message: String) -> void:
+ super.display_error(message)
+ if message.is_empty():
+ line_edit.remove_theme_color_override("font_color")
+ else:
+ line_edit.add_theme_color_override("font_color", Color(0.8, 0.1, 0.1))
+
+
+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:
+ emit_value_committed(line_edit.text)
diff --git a/common/ui/editor_properties/text/text_field.gd.uid b/common/ui/editor_properties/text/text_field.gd.uid
new file mode 100644
index 00000000..789d3ca6
--- /dev/null
+++ b/common/ui/editor_properties/text/text_field.gd.uid
@@ -0,0 +1 @@
+uid://ntsxdpbgyehu
diff --git a/common/ui/editor_properties/text/text_field.tscn b/common/ui/editor_properties/text/text_field.tscn
new file mode 100644
index 00000000..2b9c7f99
--- /dev/null
+++ b/common/ui/editor_properties/text/text_field.tscn
@@ -0,0 +1,13 @@
+[gd_scene load_steps=2 format=3 uid="uid://be0xxn5gocqjo"]
+
+[ext_resource type="Script" uid="uid://ntsxdpbgyehu" path="res://common/ui/editor_properties/text/text_field.gd" id="1_56141"]
+
+[node name="VBoxContainer" type="VBoxContainer"]
+offset_right = 76.0
+offset_bottom = 29.0
+size_flags_horizontal = 3
+script = ExtResource("1_56141")
+
+[node name="LineEdit" type="LineEdit" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
diff --git a/common/ui/editor_properties/textarea/index.gd b/common/ui/editor_properties/textarea/index.gd
new file mode 100644
index 00000000..bb2819a1
--- /dev/null
+++ b/common/ui/editor_properties/textarea/index.gd
@@ -0,0 +1,9 @@
+extends MonologueIndexer
+
+
+func get_scene() -> PackedScene:
+ return preload("uid://dmqvbcct7rbn4")
+
+
+func get_metadata() -> Dictionary:
+ return {"name": "textarea", "type": ObjectType.FIELD, "color": Color("af85fdff")}
diff --git a/common/ui/editor_properties/textarea/index.gd.uid b/common/ui/editor_properties/textarea/index.gd.uid
new file mode 100644
index 00000000..82b0574f
--- /dev/null
+++ b/common/ui/editor_properties/textarea/index.gd.uid
@@ -0,0 +1 @@
+uid://yocjhdu0idyk
diff --git a/common/ui/editor_properties/textarea/textarea_field.gd b/common/ui/editor_properties/textarea/textarea_field.gd
new file mode 100644
index 00000000..56b8f795
--- /dev/null
+++ b/common/ui/editor_properties/textarea/textarea_field.gd
@@ -0,0 +1,49 @@
+extends Field
+
+@onready var text_edit: TextEdit = %TextEdit
+
+
+func _ready() -> void:
+ super._ready()
+ text_edit.text_changed.connect(_on_text_changed)
+ text_edit.focus_exited.connect(_on_focus_exited)
+
+
+func _on_initialize() -> void:
+ text_edit.placeholder_text = settings.get("placeholder", "")
+
+ var rows: int = settings.get("rows", 3)
+ var sb: StyleBox = get_theme_stylebox("normal")
+ var padding: int = int(sb.content_margin_bottom + sb.content_margin_top)
+ text_edit.custom_minimum_size.y = text_edit.get_line_height() * rows + padding
+
+
+func set_value(value: Variant) -> void:
+ if not is_node_ready():
+ await ready
+
+ text_edit.text = str(value)
+
+
+func get_value() -> Variant:
+ return text_edit.text
+
+
+func set_editable(is_editable: bool) -> void:
+ text_edit.editable = is_editable
+
+
+func display_error(message: String) -> void:
+ super.display_error(message)
+ if message.is_empty():
+ text_edit.remove_theme_color_override("font_color")
+ else:
+ text_edit.add_theme_color_override("font_color", Color(0.8, 0.1, 0.1))
+
+
+func _on_text_changed() -> void:
+ emit_value_changed(text_edit.text)
+
+
+func _on_focus_exited() -> void:
+ emit_value_committed(text_edit.text)
diff --git a/common/ui/editor_properties/textarea/textarea_field.gd.uid b/common/ui/editor_properties/textarea/textarea_field.gd.uid
new file mode 100644
index 00000000..a510ab6a
--- /dev/null
+++ b/common/ui/editor_properties/textarea/textarea_field.gd.uid
@@ -0,0 +1 @@
+uid://dgob6i4vh3bel
diff --git a/common/ui/editor_properties/textarea/textarea_field.tscn b/common/ui/editor_properties/textarea/textarea_field.tscn
new file mode 100644
index 00000000..ca455798
--- /dev/null
+++ b/common/ui/editor_properties/textarea/textarea_field.tscn
@@ -0,0 +1,13 @@
+[gd_scene load_steps=2 format=3 uid="uid://dmqvbcct7rbn4"]
+
+[ext_resource type="Script" uid="uid://dgob6i4vh3bel" path="res://common/ui/editor_properties/textarea/textarea_field.gd" id="1_17mni"]
+
+[node name="VBoxContainer" type="VBoxContainer"]
+offset_right = 76.0
+offset_bottom = 29.0
+size_flags_horizontal = 3
+script = ExtResource("1_17mni")
+
+[node name="TextEdit" type="TextEdit" parent="."]
+unique_name_in_owner = true
+layout_mode = 2
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/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/windows/file_dialog/file_dialog.gd b/common/windows/file_dialog/file_dialog.gd
index 6de2b963..26b4ba31 100644
--- a/common/windows/file_dialog/file_dialog.gd
+++ b/common/windows/file_dialog/file_dialog.gd
@@ -9,6 +9,12 @@ func _ready():
GlobalSignal.add_listener("open_files_request", _on_open_files_request)
+func save_file(
+ callback: Callable, filter_list: PackedStringArray = [], root_subdir: String = ""
+) -> void:
+ _on_save_file_request(callback, filter_list, root_subdir)
+
+
func _on_save_file_request(
callable: Callable, filter_list: PackedStringArray = [], root_subdir: String = ""
) -> void:
@@ -40,11 +46,12 @@ func _on_open_files_request(
_core_request(callable, filter_list, root_subdir)
-func _core_request(callable: Callable, filter_list: PackedStringArray = [],
- root_subdir: String = "") -> void:
+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
diff --git a/common/windows/graph_node_picker/graph_node_picker.gd b/common/windows/graph_node_picker/graph_node_picker.gd
index f54a5215..326115de 100644
--- a/common/windows/graph_node_picker/graph_node_picker.gd
+++ b/common/windows/graph_node_picker/graph_node_picker.gd
@@ -1,9 +1,6 @@
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"
+@onready var node_tree := %Tree
## The node in which the picker was spawned/dragged from.
var from_node: String
@@ -24,25 +21,27 @@ func _ready():
func _on_enable_picker_mode(
- node: String = "", port: int = -1, mouse_pos = null, graph_release_pos = null, center_pos = null, center_window: bool = false
+ 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()
+ open_for_node(node, port, mouse_pos, graph_release_pos, center_pos, center_window)
+
+
+#
+#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:
@@ -57,6 +56,35 @@ func flush() -> void:
center = null
+func open_for_node(
+ node: String = "",
+ port: int = -1,
+ mouse_pos = null,
+ graph_release_pos = null,
+ center_pos = 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()
+ node_tree.grab_focus()
+
+ 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()
@@ -66,7 +94,8 @@ func _on_cancel_button_pressed() -> void:
func _on_create_button_pressed() -> void:
- close()
+ if node_tree.create_selected_descriptor():
+ close()
func _on_visibility_changed() -> void:
diff --git a/common/windows/graph_node_picker/graph_node_picker.tscn b/common/windows/graph_node_picker/graph_node_picker.tscn
index 1e3d505d..8657d003 100644
--- a/common/windows/graph_node_picker/graph_node_picker.tscn
+++ b/common/windows/graph_node_picker/graph_node_picker.tscn
@@ -5,6 +5,7 @@
[node name="GraphNodePicker" type="Window"]
auto_translate_mode = 1
+oversampling_override = 1.0
title = "Create Graph Node"
position = Vector2i(0, 36)
size = Vector2i(345, 425)
@@ -36,6 +37,7 @@ layout_mode = 2
placeholder_text = "Search"
[node name="Tree" type="Tree" parent="PanelContainer/VBoxContainer"]
+unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
hide_root = true
diff --git a/common/windows/graph_node_picker/graph_node_tree.gd b/common/windows/graph_node_picker/graph_node_tree.gd
index 7544a5a9..6743b8bf 100644
--- a/common/windows/graph_node_picker/graph_node_tree.gd
+++ b/common/windows/graph_node_picker/graph_node_tree.gd
@@ -1,99 +1,86 @@
+class_name GraphNodeTree
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)
+ 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()
+ if categories.is_empty():
+ _add_placeholder(root, "No nodes available")
+ return
+
+ for category 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 in descriptors:
+ _create_descriptor_item(category_item, descriptor)
+
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
+func _add_placeholder(parent: TreeItem, text: String) -> void:
+ var placeholder := create_item(parent)
+ placeholder.set_text(0, text)
+ placeholder.set_selectable(0, false)
- 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_descriptor_item(parent: TreeItem, descriptor) -> 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() -> void:
- var node_type = get_selected().get_text(0)
- GlobalSignal.emit("add_graph_node", [node_type, window])
+
+func _create() -> bool:
+ var selected := get_selected()
+ if selected == null:
+ return false
+ var descriptor_name = selected.get_metadata(0)
+ if descriptor_name == null:
+ return false
+ GlobalSignal.emit("add_graph_node", [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
- else:
- _create()
+ return
+ if _create():
window.close()
func _on_item_selected() -> void:
var item: TreeItem = get_selected()
- create_btn.disabled = item.get_child_count() > 0
+ 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:
diff --git a/common/windows/prompt_window/prompt_window.tscn b/common/windows/prompt_window/prompt_window.tscn
index b0e7177b..2e27a423 100644
--- a/common/windows/prompt_window/prompt_window.tscn
+++ b/common/windows/prompt_window/prompt_window.tscn
@@ -6,7 +6,7 @@
[node name="PromptWindow" type="Window"]
transparent_bg = true
initial_position = 2
-size = Vector2i(650, 206)
+size = Vector2i(798, 226)
wrap_controls = true
unresizable = true
borderless = true
@@ -16,38 +16,50 @@ 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
+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="HBox" type="HBoxContainer" parent="PanelContainer"]
+[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
layout_mode = 2
-theme_type_variation = &"HBoxContainer_Big"
+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"]
+layout_mode = 2
+theme_override_constants/separation = 25
-[node name="TextureRect" type="TextureRect" parent="PanelContainer/HBox"]
+[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/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"]
+[node name="VBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/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"]
+[node name="LabelHBox" type="VBoxContainer" parent="PanelContainer/MarginContainer/HBox/VBox"]
layout_mode = 2
theme_type_variation = &"VBoxContainer_Medium"
-[node name="TitleLabel" type="Label" parent="PanelContainer/HBox/VBox/LabelHBox"]
+[node name="TitleLabel" type="Label" parent="PanelContainer/MarginContainer/HBox/VBox/LabelHBox"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"HeaderSmall"
@@ -55,37 +67,37 @@ text = "Save changes?"
clip_text = true
text_overrun_behavior = 2
-[node name="DescriptionLabel" type="Label" parent="PanelContainer/HBox/VBox/LabelHBox"]
+[node name="DescriptionLabel" type="Label" parent="PanelContainer/MarginContainer/HBox/VBox/LabelHBox"]
unique_name_in_owner = true
custom_minimum_size = Vector2(500, 75)
layout_mode = 2
-theme_type_variation = &"Label_Secondary"
+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/HBox/VBox"]
+[node name="HBox" type="HBoxContainer" parent="PanelContainer/MarginContainer/HBox/VBox"]
layout_mode = 2
theme_type_variation = &"HBoxContainer_Big"
-[node name="ConfirmButton" type="Button" parent="PanelContainer/HBox/VBox/HBox"]
+[node name="ConfirmButton" type="Button" parent="PanelContainer/MarginContainer/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"]
+[node name="DenyButton" type="Button" parent="PanelContainer/MarginContainer/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"]
+[node name="CancelButton" type="Button" parent="PanelContainer/MarginContainer/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"]
+[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/windows/welcome_window/welcome_window.tscn b/common/windows/welcome_window/welcome_window.tscn
index 7a4bc96d..d6798cad 100644
--- a/common/windows/welcome_window/welcome_window.tscn
+++ b/common/windows/welcome_window/welcome_window.tscn
@@ -11,7 +11,7 @@
auto_translate_mode = 1
transparent_bg = true
initial_position = 2
-size = Vector2i(450, 297)
+size = Vector2i(1000, 300)
wrap_controls = true
transient = true
transient_to_focused = true
@@ -26,13 +26,12 @@ script = ExtResource("1_hscvo")
[node name="PanelContainer" type="PanelContainer" parent="."]
clip_children = 2
-anchors_preset = 15
-anchor_right = 1.0
+custom_minimum_size = Vector2(400, 300)
+anchors_preset = -1
+anchor_right = 0.4
anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
+size_flags_horizontal = 4
+size_flags_vertical = 0
theme_type_variation = &"OuterPanel"
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
@@ -42,8 +41,14 @@ layout_mode = 2
layout_mode = 2
theme_type_variation = &"VBoxContainer_Medium"
-[node name="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer"]
-custom_minimum_size = Vector2(0, 100)
+[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer"]
+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="TextureRect" type="TextureRect" parent="PanelContainer/MarginContainer/VBoxContainer/MarginContainer"]
layout_mode = 2
texture = ExtResource("2_ked33")
expand_mode = 5
diff --git a/icon.png b/icon.png
index d4d10e60..09575b08 100644
Binary files a/icon.png and b/icon.png differ
diff --git a/icon.png.import b/icon.png.import
index 72ba513e..c61637da 100644
--- a/icon.png.import
+++ b/icon.png.import
@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://c1tt7cwyc3s5u"
+uid="uid://b2ebpoey21tpc"
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.import b/icon_min.png.import
index 105d800f..cd6665c2 100644
--- a/icon_min.png.import
+++ b/icon_min.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/icon_min.png-d31382ea5491ba6cb1c00baeef6393a0
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_transparent.png b/icon_transparent.png
new file mode 100644
index 00000000..0545feba
Binary files /dev/null and b/icon_transparent.png differ
diff --git a/addons/gdUnit4/src/reporters/html/template/css/logo.png.import b/icon_transparent.png.import
similarity index 55%
rename from addons/gdUnit4/src/reporters/html/template/css/logo.png.import
rename to icon_transparent.png.import
index 95c30a28..42981b8c 100644
--- a/addons/gdUnit4/src/reporters/html/template/css/logo.png.import
+++ b/icon_transparent.png.import
@@ -2,22 +2,24 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://2n0cbwv2t23g"
-path="res://.godot/imported/logo.png-d555ca92b260de08658ae2e1d4572e8c.ctex"
+uid="uid://bou7qelcr5dwn"
+path="res://.godot/imported/icon_transparent.png-574b1e7a19305c536c473b5d16827b4d.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"]
+source_file="res://icon_transparent.png"
+dest_files=["res://.godot/imported/icon_transparent.png-574b1e7a19305c536c473b5d16827b4d.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
@@ -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/logic/history/add_nodes_command.gd b/logic/history/add_nodes_command.gd
new file mode 100644
index 00000000..0ed13bb2
--- /dev/null
+++ b/logic/history/add_nodes_command.gd
@@ -0,0 +1,34 @@
+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 = StorylineManager.get_storyline(storyline_id)
+ if not storyline:
+ return
+
+ for node in nodes:
+ storyline.add_node(node)
+
+
+func undo() -> void:
+ var storyline: StorylineDocument = StorylineManager.get_storyline(storyline_id)
+ if not storyline:
+ return
+
+ for node in nodes:
+ storyline.remove_node(node)
+
+
+func get_description() -> String:
+ return "Add %s nodes" % nodes.size()
diff --git a/logic/history/add_nodes_command.gd.uid b/logic/history/add_nodes_command.gd.uid
new file mode 100644
index 00000000..b6349ff5
--- /dev/null
+++ b/logic/history/add_nodes_command.gd.uid
@@ -0,0 +1 @@
+uid://1br7o001ta7f
diff --git a/logic/history/command.gd b/logic/history/command.gd
new file mode 100644
index 00000000..7746633d
--- /dev/null
+++ b/logic/history/command.gd
@@ -0,0 +1,6 @@
+@abstract
+class_name Command extends RefCounted
+
+@abstract func execute()
+@abstract func undo()
+@abstract func get_description() -> String
diff --git a/logic/history/command.gd.uid b/logic/history/command.gd.uid
new file mode 100644
index 00000000..00111e41
--- /dev/null
+++ b/logic/history/command.gd.uid
@@ -0,0 +1 @@
+uid://dxrwkktseecun
diff --git a/logic/history/command_manager.gd b/logic/history/command_manager.gd
new file mode 100644
index 00000000..e1489dfd
--- /dev/null
+++ b/logic/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_ENDS) -> void:
+ var description = 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():
+ 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():
+ history_changed.emit()
+
+
+func begin_group(description: String = "Group"):
+ undo_redo.create_action(description, UndoRedo.MERGE_DISABLE)
+
+
+func add_to_group(command: Command):
+ 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():
+ undo_redo.commit_action()
+ command_executed.emit()
+ history_changed.emit()
diff --git a/logic/history/command_manager.gd.uid b/logic/history/command_manager.gd.uid
new file mode 100644
index 00000000..948b9434
--- /dev/null
+++ b/logic/history/command_manager.gd.uid
@@ -0,0 +1 @@
+uid://dwxg4gbtegie1
diff --git a/logic/history/delete_nodes_command.gd b/logic/history/delete_nodes_command.gd
new file mode 100644
index 00000000..90b5886f
--- /dev/null
+++ b/logic/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/logic/history/delete_nodes_command.gd.uid b/logic/history/delete_nodes_command.gd.uid
new file mode 100644
index 00000000..2b244e3a
--- /dev/null
+++ b/logic/history/delete_nodes_command.gd.uid
@@ -0,0 +1 @@
+uid://bi0nf2bllnil5
diff --git a/logic/history/node_connection_command.gd b/logic/history/node_connection_command.gd
new file mode 100644
index 00000000..0be3bda8
--- /dev/null
+++ b/logic/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 = graph_view.get_port_index_for_property(from_node_id, from_property_name)
+ var to_port = graph_view.get_port_index_for_property(to_node_id, to_property_name)
+
+ 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 = graph_view.get_port_index_for_property(from_node_id, from_property_name)
+ var to_port = graph_view.get_port_index_for_property(to_node_id, to_property_name)
+
+ 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 = StorylineManager.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._notify_change(from_node_id)
+
+ if not to_nodes.is_empty():
+ var to_node: InspectableNode = to_nodes[0]
+ to_node._notify_change(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/logic/history/node_connection_command.gd.uid b/logic/history/node_connection_command.gd.uid
new file mode 100644
index 00000000..6e475902
--- /dev/null
+++ b/logic/history/node_connection_command.gd.uid
@@ -0,0 +1 @@
+uid://7kht5ybdplta
diff --git a/logic/history/node_selection_command.gd b/logic/history/node_selection_command.gd
new file mode 100644
index 00000000..b51f9394
--- /dev/null
+++ b/logic/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/logic/history/node_selection_command.gd.uid b/logic/history/node_selection_command.gd.uid
new file mode 100644
index 00000000..87551d7f
--- /dev/null
+++ b/logic/history/node_selection_command.gd.uid
@@ -0,0 +1 @@
+uid://eghqtautsvc2
diff --git a/logic/history/property_command.gd b/logic/history/property_command.gd
new file mode 100644
index 00000000..b4a73c6f
--- /dev/null
+++ b/logic/history/property_command.gd
@@ -0,0 +1,49 @@
+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)
+ target._notify_change(property_name)
+ _broadcast_change()
+
+
+func undo() -> void:
+ var property: Property = target.get_property(property_name)
+ property.set_value(old_value)
+ target._notify_change(property_name)
+ _broadcast_change(true)
+
+
+func get_description() -> String:
+ return "Change value of `%s` property" % property_name
+
+
+func _broadcast_change(is_undo: bool = false) -> void:
+ if not (target is InspectableNode):
+ return
+
+ var node := target as InspectableNode
+ var property: Property = node.get_property(property_name)
+ if not property:
+ return
+
+ if target is InspectableNode and property_name == "position":
+ GlobalSignal.emit("request_node_inspection", [node, node.storyline_id, true])
+
+ if not property.get_settings_value("visible_in_inspector", true):
+ return
+
+ GlobalSignal.emit("inspector_property_changed", [node, property_name, is_undo])
diff --git a/logic/history/property_command.gd.uid b/logic/history/property_command.gd.uid
new file mode 100644
index 00000000..57f949eb
--- /dev/null
+++ b/logic/history/property_command.gd.uid
@@ -0,0 +1 @@
+uid://dmv35lufxhghw
diff --git a/logic/history/property_settings_command.gd b/logic/history/property_settings_command.gd
new file mode 100644
index 00000000..d80f1021
--- /dev/null
+++ b/logic/history/property_settings_command.gd
@@ -0,0 +1,80 @@
+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.settings[settings_name] = new_value
+ _handle_connection_visibility(new_value)
+ target._notify_change(property_name)
+ property.refresh_bindings()
+ #target._notify_property_settings_change(property_name, settings_name, old_value, new_value)
+
+
+func undo() -> void:
+ var property: Property = target.get_property(property_name)
+ if old_value:
+ property.settings[settings_name] = old_value
+ else:
+ property.settings.erase(settings_name)
+ _handle_connection_visibility(old_value)
+ target._notify_change(property_name)
+ property.refresh_bindings()
+ #target._notify_property_settings_change(property_name, settings_name, new_value, old_value)
+
+
+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 = _get_target_node()
+ if not node:
+ return
+ var graph_view = node.graph_view
+ if not graph_view:
+ return
+ var graph_edit = graph_view.get_parent()
+ if not (graph_edit is MonologueGraphEdit):
+ return
+ 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():
+ if target is InspectableNode:
+ return target
+ return null
diff --git a/logic/history/property_settings_command.gd.uid b/logic/history/property_settings_command.gd.uid
new file mode 100644
index 00000000..4201c437
--- /dev/null
+++ b/logic/history/property_settings_command.gd.uid
@@ -0,0 +1 @@
+uid://dabyel4cj64ff
diff --git a/logic/history/add_language_history.gd b/logic/old_history/add_language_history.gd
similarity index 100%
rename from logic/history/add_language_history.gd
rename to logic/old_history/add_language_history.gd
diff --git a/logic/history/add_language_history.gd.uid b/logic/old_history/add_language_history.gd.uid
similarity index 100%
rename from logic/history/add_language_history.gd.uid
rename to logic/old_history/add_language_history.gd.uid
diff --git a/logic/history/add_node_history.gd b/logic/old_history/add_node_history.gd
similarity index 100%
rename from logic/history/add_node_history.gd
rename to logic/old_history/add_node_history.gd
diff --git a/logic/history/add_node_history.gd.uid b/logic/old_history/add_node_history.gd.uid
similarity index 100%
rename from logic/history/add_node_history.gd.uid
rename to logic/old_history/add_node_history.gd.uid
diff --git a/logic/history/character_history.gd b/logic/old_history/character_history.gd
similarity index 94%
rename from logic/history/character_history.gd
rename to logic/old_history/character_history.gd
index e35bf3d6..bffbe480 100644
--- a/logic/history/character_history.gd
+++ b/logic/old_history/character_history.gd
@@ -42,4 +42,4 @@ func _hide_unrelated_windows() -> void:
func _update_character(property: String, value: Variant) -> void:
var key = Util.to_key_name(property)
- graph_edit.characters[character_index]["Character"][key] = value
+ graph_edit.characters.value[character_index]["Character"][key] = value
diff --git a/logic/history/character_history.gd.uid b/logic/old_history/character_history.gd.uid
similarity index 100%
rename from logic/history/character_history.gd.uid
rename to logic/old_history/character_history.gd.uid
diff --git a/logic/history/delete_language_history.gd b/logic/old_history/delete_language_history.gd
similarity index 100%
rename from logic/history/delete_language_history.gd
rename to logic/old_history/delete_language_history.gd
diff --git a/logic/history/delete_language_history.gd.uid b/logic/old_history/delete_language_history.gd.uid
similarity index 100%
rename from logic/history/delete_language_history.gd.uid
rename to logic/old_history/delete_language_history.gd.uid
diff --git a/logic/history/delete_node_history.gd b/logic/old_history/delete_node_history.gd
similarity index 100%
rename from logic/history/delete_node_history.gd
rename to logic/old_history/delete_node_history.gd
diff --git a/logic/history/delete_node_history.gd.uid b/logic/old_history/delete_node_history.gd.uid
similarity index 100%
rename from logic/history/delete_node_history.gd.uid
rename to logic/old_history/delete_node_history.gd.uid
diff --git a/logic/history/history_handle.gd b/logic/old_history/history_handle.gd
similarity index 100%
rename from logic/history/history_handle.gd
rename to logic/old_history/history_handle.gd
diff --git a/logic/history/history_handle.gd.uid b/logic/old_history/history_handle.gd.uid
similarity index 100%
rename from logic/history/history_handle.gd.uid
rename to logic/old_history/history_handle.gd.uid
diff --git a/logic/history/language_history.gd b/logic/old_history/language_history.gd
similarity index 100%
rename from logic/history/language_history.gd
rename to logic/old_history/language_history.gd
diff --git a/logic/history/language_history.gd.uid b/logic/old_history/language_history.gd.uid
similarity index 100%
rename from logic/history/language_history.gd.uid
rename to logic/old_history/language_history.gd.uid
diff --git a/logic/history/modify_language_history.gd b/logic/old_history/modify_language_history.gd
similarity index 100%
rename from logic/history/modify_language_history.gd
rename to logic/old_history/modify_language_history.gd
diff --git a/logic/history/modify_language_history.gd.uid b/logic/old_history/modify_language_history.gd.uid
similarity index 100%
rename from logic/history/modify_language_history.gd.uid
rename to logic/old_history/modify_language_history.gd.uid
diff --git a/logic/history/monologue_history.gd b/logic/old_history/monologue_history.gd
similarity index 100%
rename from logic/history/monologue_history.gd
rename to logic/old_history/monologue_history.gd
diff --git a/logic/history/monologue_history.gd.uid b/logic/old_history/monologue_history.gd.uid
similarity index 100%
rename from logic/history/monologue_history.gd.uid
rename to logic/old_history/monologue_history.gd.uid
diff --git a/logic/history/portrait_history.gd b/logic/old_history/portrait_history.gd
similarity index 92%
rename from logic/history/portrait_history.gd
rename to logic/old_history/portrait_history.gd
index 65203ae7..8cb572e6 100644
--- a/logic/history/portrait_history.gd
+++ b/logic/old_history/portrait_history.gd
@@ -27,7 +27,7 @@ func revert_properties() -> void:
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 character_dict = graph_edit.characters.value[character_index]["Character"]
var key = Util.to_key_name(property)
character_dict["Portraits"][portrait_index]["Portrait"][key] = value
diff --git a/logic/history/portrait_history.gd.uid b/logic/old_history/portrait_history.gd.uid
similarity index 100%
rename from logic/history/portrait_history.gd.uid
rename to logic/old_history/portrait_history.gd.uid
diff --git a/logic/history/property_change.gd b/logic/old_history/property_change.gd
similarity index 100%
rename from logic/history/property_change.gd
rename to logic/old_history/property_change.gd
diff --git a/logic/history/property_change.gd.uid b/logic/old_history/property_change.gd.uid
similarity index 100%
rename from logic/history/property_change.gd.uid
rename to logic/old_history/property_change.gd.uid
diff --git a/logic/history/property_history.gd b/logic/old_history/property_history.gd
similarity index 77%
rename from logic/history/property_history.gd
rename to logic/old_history/property_history.gd
index 55e0d293..4d9984f6 100644
--- a/logic/history/property_history.gd
+++ b/logic/old_history/property_history.gd
@@ -54,16 +54,17 @@ func set_property(node: Variant, property: String, value: Variant) -> void:
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 refresh_properties(_node: Variant, _language: String) -> void:
+ pass
+ #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:
diff --git a/logic/history/property_history.gd.uid b/logic/old_history/property_history.gd.uid
similarity index 100%
rename from logic/history/property_history.gd.uid
rename to logic/old_history/property_history.gd.uid
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/bughiuhb.tscn b/nodes/bughiuhb.tscn
new file mode 100644
index 00000000..93d9a14d
--- /dev/null
+++ b/nodes/bughiuhb.tscn
@@ -0,0 +1,36 @@
+[gd_scene format=3 uid="uid://crdsjpypmul7c"]
+
+[node name="Bughiuhb" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="GraphEdit" type="GraphEdit" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+scroll_offset = Vector2(-40, -40)
+
+[node name="GraphNode" type="GraphNode" parent="GraphEdit"]
+layout_mode = 0
+offset_left = 100.0
+offset_top = 184.0
+offset_right = 218.0
+offset_bottom = 265.0
+mouse_filter = 1
+position_offset = Vector2(60, 144)
+
+[node name="GraphNode2" type="GraphNode" parent="GraphEdit"]
+layout_mode = 0
+offset_left = 345.0
+offset_top = 173.0
+offset_right = 475.0
+offset_bottom = 257.0
+mouse_filter = 1
+position_offset = Vector2(305, 133)
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/index.gd b/nodes/root_node/index.gd
new file mode 100644
index 00000000..792b067c
--- /dev/null
+++ b/nodes/root_node/index.gd
@@ -0,0 +1,24 @@
+extends MonologueIndexer
+
+const NODE_SCRIPT := preload("res://nodes/root_node/root_node.gd")
+const ICON_PATH := "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/nodes/root_node/index.gd.uid b/nodes/root_node/index.gd.uid
new file mode 100644
index 00000000..a6d662ca
--- /dev/null
+++ b/nodes/root_node/index.gd.uid
@@ -0,0 +1 @@
+uid://c5djdhldg6qbu
diff --git a/nodes/root_node/root_node.gd b/nodes/root_node/root_node.gd
index 12ad5d29..48db27d1 100644
--- a/nodes/root_node/root_node.gd
+++ b/nodes/root_node/root_node.gd
@@ -1,80 +1,26 @@
@icon("res://ui/assets/icons/root.svg")
-class_name RootNode extends MonologueGraphNode
+class_name RootNode extends InspectableNode
-var characters := Property.new(LIST, {}, [])
-var variables := Property.new(LIST, {}, [])
-var _character_references = []
-var _variable_references = []
+func initialize_properties() -> void:
+ define_main_property("root", "context", false, null, {"exposed": false})
-func _ready():
- node_type = "NodeRoot"
- super._ready()
+func get_type() -> String:
+ return "root"
- 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 get_settings() -> Dictionary:
+ return {}
-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 get_icon() -> Texture2D:
+ return Texture2D.new()
-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_color() -> Color:
+ return Color.WHITE
-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
+func _on_property_changed(_pname: String, _old_value: Variant, _new_value: Variant) -> void:
+ pass
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/index.gd b/nodes/sentence_node/index.gd
new file mode 100644
index 00000000..ae82eeb5
--- /dev/null
+++ b/nodes/sentence_node/index.gd
@@ -0,0 +1,24 @@
+extends MonologueIndexer
+
+const NODE_SCRIPT := preload("res://nodes/sentence_node/sentence_node.gd")
+const ICON_PATH := "res://ui/assets/icons/text.svg"
+
+
+func get_scene() -> PackedScene:
+ return null
+
+
+func get_metadata() -> Dictionary:
+ return {
+ "name": "sentence",
+ "type": ObjectType.NODE,
+ "display_name": "Sentence",
+ "category": "Narration",
+ "script": NODE_SCRIPT,
+ "icon": ICON_PATH,
+ "color": Color("af85fdff")
+ }
+
+
+func get_node_script() -> Script:
+ return NODE_SCRIPT
diff --git a/nodes/sentence_node/index.gd.uid b/nodes/sentence_node/index.gd.uid
new file mode 100644
index 00000000..7b83af61
--- /dev/null
+++ b/nodes/sentence_node/index.gd.uid
@@ -0,0 +1 @@
+uid://b165nmgmvcms
diff --git a/nodes/sentence_node/sentence_node.gd b/nodes/sentence_node/sentence_node.gd
index 81b29dbf..c13848c9 100644
--- a/nodes/sentence_node/sentence_node.gd
+++ b/nodes/sentence_node/sentence_node.gd
@@ -1,47 +1,30 @@
@icon("res://ui/assets/icons/text.svg")
-class_name SentenceNode extends MonologueGraphNode
+class_name SentenceNode extends InspectableNode
-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 initialize_properties() -> void:
+ define_main_property("sentence", "context", false, null, {"export": true})
+ define_property("speaker", "", "dropdown", {"source": "characters"})
+ define_property("display_name", "", "text")
+ define_property("line", "", "text")
+ define_property("voiceline", "", "file", {"visible_in_graph": false})
-func _ready():
- node_type = "NodeSentence"
- sentence.connect("preview", _on_text_preview)
- voiceline.setters["base_path"] = get_graph_edit().file_path
- super._ready()
- _update()
+func get_type() -> String:
+ return "sentence"
-func reload_preview() -> void:
- _preview.text = sentence.value
+func get_settings() -> Dictionary:
+ return {}
-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 get_icon() -> Texture2D:
+ return Texture2D.new()
-func _on_text_preview(text: Variant):
- _preview.text = str(text)
+func get_color() -> Color:
+ return Color.WHITE
-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"]}]
+func _on_property_changed(_pname: String, _old_value: Variant, _new_value: Variant) -> void:
+ pass
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/text_node/index.gd b/nodes/text_node/index.gd
new file mode 100644
index 00000000..dfff9c8e
--- /dev/null
+++ b/nodes/text_node/index.gd
@@ -0,0 +1,24 @@
+extends MonologueIndexer
+
+const NODE_SCRIPT := preload("res://nodes/text_node/text_node.gd")
+const ICON_PATH := "res://ui/assets/icons/text.svg"
+
+
+func get_scene() -> PackedScene:
+ return null
+
+
+func get_metadata() -> Dictionary:
+ return {
+ "name": "text",
+ "type": ObjectType.NODE,
+ "display_name": "Text",
+ "category": "Narration",
+ "script": NODE_SCRIPT,
+ "icon": ICON_PATH,
+ "color": Color("af85fdff")
+ }
+
+
+func get_node_script() -> Script:
+ return NODE_SCRIPT
diff --git a/nodes/text_node/index.gd.uid b/nodes/text_node/index.gd.uid
new file mode 100644
index 00000000..97e0fbb6
--- /dev/null
+++ b/nodes/text_node/index.gd.uid
@@ -0,0 +1 @@
+uid://b67i013h21w4e
diff --git a/nodes/text_node/text_node.gd b/nodes/text_node/text_node.gd
new file mode 100644
index 00000000..5bb7583a
--- /dev/null
+++ b/nodes/text_node/text_node.gd
@@ -0,0 +1,26 @@
+@icon("res://ui/assets/icons/text.svg")
+class_name TextNode extends InspectableNode
+
+
+func initialize_properties() -> void:
+ define_main_property("text", "text", true, "", {"exposed": false})
+
+
+func get_type() -> String:
+ return "text"
+
+
+func get_settings() -> Dictionary:
+ return {}
+
+
+func get_icon() -> Texture2D:
+ return Texture2D.new()
+
+
+func get_color() -> Color:
+ return Color.WHITE
+
+
+func _on_property_changed(_pname: String, _old_value: Variant, _new_value: Variant) -> void:
+ pass
diff --git a/nodes/text_node/text_node.gd.uid b/nodes/text_node/text_node.gd.uid
new file mode 100644
index 00000000..f33d766f
--- /dev/null
+++ b/nodes/text_node/text_node.gd.uid
@@ -0,0 +1 @@
+uid://8hoiik6d0qim
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..11ce2601 100644
--- a/project.godot
+++ b/project.godot
@@ -20,23 +20,28 @@ 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.5")
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://b2ebpoey21tpc"
[autoload]
Constants="*res://autoloads/constants.gd"
+IDGen="*res://autoloads/id_gen.gd"
App="*res://autoloads/app.gd"
+ThemeManager="*res://autoloads/theme_manager.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"
+UndoRedoService="*res://autoloads/undo_redo_service.gd"
+FieldBucket="*res://common/ui/editor_properties/field_bucket.gd"
+NodeBucket="*res://common/nodes/node_bucket.gd"
+StorylineManager="*res://autoloads/storyline_manager.gd"
[debug]
@@ -46,7 +51,6 @@ gdscript/warnings/integer_division=0
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 +59,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/Todo_Manager/plugin.cfg", "res://addons/beautify_code_on_save/plugin.cfg", "res://addons/dockable_container/plugin.cfg", "res://addons/gdUnit4/plugin.cfg")
[file_customization]
@@ -75,10 +79,44 @@ ui/inspector/tree_view_mode=1
[gui]
-theme/custom="uid://budfhk1hudcfj"
-theme/custom_font="uid://xxjublydw27h"
theme/default_font_hinting=2
+theme/default_font_multichannel_signed_distance_field=true
theme/lcd_subpixel_layout=0
+theme/custom="uid://budfhk1hudcfj"
+theme/custom_font="uid://xxjublydw27h"
+
+[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]
diff --git a/scenes/main/app.tscn b/scenes/main/app.tscn
index 4263e148..a66684b2 100644
--- a/scenes/main/app.tscn
+++ b/scenes/main/app.tscn
@@ -1,14 +1,10 @@
-[gd_scene load_steps=14 format=3 uid="uid://bim51g1aibuw0"]
+[gd_scene load_steps=10 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="PackedScene" uid="uid://bqjfdabrxujp7" path="res://scenes/main/editor.tscn" id="1_kiov6"]
[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="Script" uid="uid://d0xgy6gflipxs" path="res://common/layouts/editor/editor_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"]
@@ -44,45 +40,14 @@ theme_type_variation = &"EditorBackground"
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"]
-layout_mode = 2
-current_tab = 0
-clip_tabs = false
-tab_count = 1
-tab_0/icon = ExtResource("3_gcdaj")
-
-[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")]
+[node name="MonologueEditor" parent="Frame/VBoxContainer" node_paths=PackedStringArray("welcome_window", "graph_container", "file_dialog") instance=ExtResource("1_kiov6")]
layout_mode = 2
welcome_window = NodePath("../../../WelcomeWindow")
-graph_node_picker = NodePath("../../../GraphNodePicker")
+graph_container = NodePath("DockableContainer/Graph/GraphContainer")
+file_dialog = NodePath("../../../FileDialog")
-[node name="GraphEditSwitcher" parent="Frame/VBoxContainer/MonologueControl/MainContainer/GraphEditsArea" index="0" node_paths=PackedStringArray("tab_bar")]
-tab_bar = NodePath("../../../../CustomTitleBar/HBoxContainer2/TabBar")
+[node name="GraphContainer" parent="Frame/VBoxContainer/MonologueEditor/DockableContainer/Graph" index="0" node_paths=PackedStringArray("inspector_panel")]
+inspector_panel = NodePath("../../Inspector")
[node name="Dimmer" type="ColorRect" parent="."]
visible = false
@@ -106,13 +71,6 @@ visible = false
z_index = 2
layout_mode = 1
-[node name="GraphNodePicker" parent="." node_paths=PackedStringArray("switcher") instance=ExtResource("7_nw038")]
-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
@@ -127,7 +85,10 @@ access = 2
use_native_dialog = true
script = ExtResource("10_r77hi")
+[connection signal="gui_input" from="Dimmer" to="Dimmer" method="_on_gui_input"]
[connection signal="visibility_changed" from="CharacterEditContainer" to="CharacterEditContainer" method="_on_visibility_changed"]
[connection signal="file_selected" from="FileDialog" to="FileDialog" method="_on_file_selected"]
-[editable path="Frame/VBoxContainer/MonologueControl"]
+[editable path="Frame/VBoxContainer/MonologueEditor"]
+[editable path="Frame/VBoxContainer/MonologueEditor/DockableContainer/Graph"]
+[editable path="Frame/VBoxContainer/MonologueEditor/DockableContainer/Inspector"]
diff --git a/scenes/main/editor.tscn b/scenes/main/editor.tscn
new file mode 100644
index 00000000..65b9af98
--- /dev/null
+++ b/scenes/main/editor.tscn
@@ -0,0 +1,186 @@
+[gd_scene load_steps=24 format=3 uid="uid://bqjfdabrxujp7"]
+
+[ext_resource type="Script" uid="uid://q6eg6rid6xqd" path="res://scenes/main/monologue_editor.gd" id="1_ovo1o"]
+[ext_resource type="Script" uid="uid://k2o7qui1lr6l" path="res://addons/dockable_container/dockable_container.gd" id="2_xxwqg"]
+[ext_resource type="Texture2D" uid="uid://dd8v3hpxxk33d" path="res://ui/assets/icons/logo_white.svg" id="3_mei7o"]
+[ext_resource type="Script" uid="uid://bh202gagkdkar" path="res://addons/dockable_container/layout_panel.gd" id="3_uvwqm"]
+[ext_resource type="Script" uid="uid://dos02hjhrf1ws" path="res://addons/dockable_container/layout_split.gd" id="4_kkjrq"]
+[ext_resource type="Script" uid="uid://m1wxne1g6kju" path="res://scenes/main/main_menu.gd" id="4_pwbil"]
+[ext_resource type="Script" uid="uid://cylirj261q6ru" path="res://addons/dockable_container/layout.gd" id="5_svwnc"]
+[ext_resource type="Script" uid="uid://cpi3ixjp3ye17" path="res://scenes/main/tab_bar.gd" id="6_2cy6n"]
+[ext_resource type="PackedScene" uid="uid://mfdu320oy6ex" path="res://common/layouts/editor/editor_list_section.tscn" id="6_mei7o"]
+[ext_resource type="PackedScene" uid="uid://c8525nhvwt1y0" path="res://common/layouts/editor/graph.tscn" id="9_qd5yd"]
+[ext_resource type="PackedScene" uid="uid://dgvhvxdrd58qp" path="res://common/layouts/editor/inspector/inspector_panel.tscn" id="14_craj7"]
+[ext_resource type="Script" uid="uid://q0oqx6butjwn" path="res://scenes/main/search_bar_container.gd" id="15_35jwg"]
+[ext_resource type="PackedScene" uid="uid://cmpsaafag7cwl" path="res://common/windows/graph_node_picker/graph_node_picker.tscn" id="15_q62vd"]
+[ext_resource type="PackedScene" uid="uid://cvum3eaenloix" path="res://common/layouts/search_bar/search_bar.tscn" id="16_n4eqx"]
+
+[sub_resource type="Resource" id="Resource_g5pcf"]
+resource_name = "Tabs"
+script = ExtResource("3_uvwqm")
+names = PackedStringArray("_Tabs")
+
+[sub_resource type="Resource" id="Resource_xxwqg"]
+resource_name = "Tabs"
+script = ExtResource("3_uvwqm")
+names = PackedStringArray("Characters", "Variables", "Items", "Locations")
+
+[sub_resource type="Resource" id="Resource_uvwqm"]
+resource_name = "Tabs"
+script = ExtResource("3_uvwqm")
+names = PackedStringArray("Graph")
+
+[sub_resource type="Resource" id="Resource_kkjrq"]
+resource_name = "Split"
+script = ExtResource("4_kkjrq")
+percent = 0.0
+first = SubResource("Resource_xxwqg")
+second = SubResource("Resource_uvwqm")
+
+[sub_resource type="Resource" id="Resource_svwnc"]
+resource_name = "Tabs"
+script = ExtResource("3_uvwqm")
+names = PackedStringArray("Inspector")
+
+[sub_resource type="Resource" id="Resource_mvfhp"]
+resource_name = "Split"
+script = ExtResource("4_kkjrq")
+percent = 1.0
+first = SubResource("Resource_kkjrq")
+second = SubResource("Resource_svwnc")
+
+[sub_resource type="Resource" id="Resource_e6y6g"]
+resource_name = "Split"
+script = ExtResource("4_kkjrq")
+direction = 1
+percent = 0.0
+first = SubResource("Resource_g5pcf")
+second = SubResource("Resource_mvfhp")
+
+[sub_resource type="Resource" id="Resource_jojum"]
+resource_name = "Layout"
+script = ExtResource("5_svwnc")
+root = SubResource("Resource_e6y6g")
+
+[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_sr1k3"]
+
+[node name="MonologueEditor" 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_ovo1o")
+
+[node name="DockableContainer" type="Container" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("2_xxwqg")
+tab_alignment = 0
+use_hidden_tabs_for_min_size = true
+layout = SubResource("Resource_jojum")
+metadata/_custom_type_script = "uid://k2o7qui1lr6l"
+
+[node name="_Tabs" type="PanelContainer" parent="DockableContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 0
+theme_override_styles/panel = SubResource("StyleBoxEmpty_sr1k3")
+script = ExtResource("6_2cy6n")
+
+[node name="TabBar" type="HBoxContainer" parent="DockableContainer/_Tabs"]
+custom_minimum_size = Vector2(0, 30)
+layout_mode = 2
+theme_override_constants/separation = 0
+
+[node name="MainPopupMenu" type="MenuButton" parent="DockableContainer/_Tabs/TabBar"]
+custom_minimum_size = Vector2(30, 0)
+layout_mode = 2
+icon = ExtResource("3_mei7o")
+icon_alignment = 1
+expand_icon = true
+script = ExtResource("4_pwbil")
+
+[node name="VSeparator" type="VSeparator" parent="DockableContainer/_Tabs/TabBar"]
+visible = false
+layout_mode = 2
+theme_type_variation = &"VSeparatorGrow"
+
+[node name="TabBar" type="TabBar" parent="DockableContainer/_Tabs/TabBar"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+theme_override_constants/h_separation = 4
+
+[node name="Graph" parent="DockableContainer" instance=ExtResource("9_qd5yd")]
+layout_mode = 2
+
+[node name="Inspector" parent="DockableContainer" instance=ExtResource("14_craj7")]
+unique_name_in_owner = true
+custom_minimum_size = Vector2(330, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="Characters" parent="DockableContainer" instance=ExtResource("6_mei7o")]
+unique_name_in_owner = true
+layout_mode = 2
+
+[node name="Variables" parent="DockableContainer" instance=ExtResource("6_mei7o")]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+
+[node name="Items" parent="DockableContainer" instance=ExtResource("6_mei7o")]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+
+[node name="Locations" parent="DockableContainer" instance=ExtResource("6_mei7o")]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+
+[node name="GraphNodePicker" parent="." instance=ExtResource("15_q62vd")]
+unique_name_in_owner = true
+visible = false
+keep_title_visible = false
+content_scale_mode = 2
+content_scale_aspect = 1
+
+[node name="SearchBarContainer" type="CenterContainer" parent="."]
+layout_mode = 1
+anchors_preset = -1
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = 4.0
+offset_top = 4.0
+offset_right = 4.0
+offset_bottom = 4.0
+grow_horizontal = 2
+grow_vertical = 2
+mouse_filter = 2
+script = ExtResource("15_35jwg")
+
+[node name="SearchBar" parent="SearchBarContainer" instance=ExtResource("16_n4eqx")]
+visible = false
+layout_mode = 2
+
+[node name="SplitContainer" type="SplitContainer" parent="."]
+layout_mode = 0
+offset_right = 40.0
+offset_bottom = 40.0
+
+[connection signal="add_document" from="DockableContainer/_Tabs" to="." method="_on__tabs_add_document"]
+[connection signal="tab_changed" from="DockableContainer/_Tabs/TabBar/TabBar" to="DockableContainer/_Tabs" method="_on_tab_bar_tab_changed"]
+
+[editable path="DockableContainer/Graph"]
+[editable path="DockableContainer/Inspector"]
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_container.gd b/scenes/main/graph_container.gd
new file mode 100644
index 00000000..6c3cca82
--- /dev/null
+++ b/scenes/main/graph_container.gd
@@ -0,0 +1,89 @@
+## 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 VBoxContainer
+
+var prompt_scene = preload("res://common/windows/prompt_window/prompt_window.tscn")
+
+@export var inspector_panel: InspectorPanel
+@onready var graph: MonologueGraphEdit = %GraphEdit
+
+var _selected_nodes: Dictionary = {} # storyline_id -> InspectableNode
+var _is_applying_selection: bool = false
+
+
+func _ready() -> void:
+ GlobalSignal.add_listener("request_node_inspection", _on_request_node_inspection)
+ StorylineManager.storyline_changed.connect(refresh)
+ StorylineManager.storyline_switched.connect(_on_storyline_switched)
+
+
+func refresh() -> void:
+ pass
+
+
+func _on_storyline_switched() -> void:
+ var storyline: StorylineDocument = StorylineManager.get_active_storyline()
+ graph.storyline_id = storyline.id
+ graph.refresh()
+
+
+func _on_graph_edit_node_view_selected(node: InspectableNode) -> void:
+ request_node_selection(node)
+
+
+func _on_request_node_inspection(
+ node: InspectableNode, storyline_id: String = "", skip_history: bool = false
+) -> void:
+ var target_storyline_id := storyline_id
+ if target_storyline_id.is_empty() and node:
+ target_storyline_id = node.storyline_id
+ if target_storyline_id.is_empty():
+ return
+ request_node_selection(node, skip_history)
+
+
+func request_node_selection(node: InspectableNode, skip_history: bool = false) -> void:
+ if _is_applying_selection:
+ return
+
+ var storyline: StorylineDocument = StorylineManager.get_active_storyline()
+ var current_node: InspectableNode = _selected_nodes.get(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, storyline.id)
+ return
+
+ var history: CommandManager = storyline.history if storyline else null
+ if not history:
+ _apply_selection(node, storyline.id)
+ return
+
+ var command := NodeSelectionCommand.new(
+ storyline.id, current_node, node, Callable(self, "_apply_selection")
+ )
+
+ history.execute(command, UndoRedo.MERGE_ENDS)
+
+
+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)
+
+ if inspector_panel.current_object != node:
+ inspector_panel.inspect(node)
+
+ _is_applying_selection = false
diff --git a/scenes/main/graph_edit_switcher.gd.uid b/scenes/main/graph_container.gd.uid
similarity index 100%
rename from scenes/main/graph_edit_switcher.gd.uid
rename to scenes/main/graph_container.gd.uid
diff --git a/scenes/main/graph_edit_switcher.tscn b/scenes/main/graph_container.tscn
similarity index 71%
rename from scenes/main/graph_edit_switcher.tscn
rename to scenes/main/graph_container.tscn
index 8e1a8cdb..84baacf4 100644
--- a/scenes/main/graph_edit_switcher.tscn
+++ b/scenes/main/graph_container.tscn
@@ -1,24 +1,24 @@
-[gd_scene load_steps=4 format=3 uid="uid://cvjfcgqktlyc2"]
+[gd_scene load_steps=4 format=3 uid="uid://51mo3wv8gb1o"]
-[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"]
+[ext_resource type="Script" uid="uid://nxistnt1yhxc" path="res://scenes/main/graph_container.gd" id="1_5dl3l"]
+[ext_resource type="Texture2D" uid="uid://c7vdr4e0mxst6" path="res://ui/assets/icons/close_icon.png" id="2_5pcvw"]
+[ext_resource type="PackedScene" uid="uid://qdgl8co6qy6" path="res://common/layouts/graph_edit/monologue_graph_edit.tscn" id="3_jrlr1"]
-[node name="GraphEditSwitcher" type="VBoxContainer"]
+[node name="GraphEditContainer" 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")
+script = ExtResource("1_5dl3l")
[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")
+theme_override_icons/close = ExtResource("2_5pcvw")
current_tab = 0
clip_tabs = false
tab_close_display_policy = 1
@@ -30,8 +30,11 @@ layout_mode = 2
size_flags_vertical = 3
mouse_filter = 2
-[node name="Default" parent="GraphEdits" instance=ExtResource("3_o6be6")]
+[node name="Default" parent="GraphEdits" instance=ExtResource("3_jrlr1")]
layout_mode = 1
+[node name="TabContainer" type="TabContainer" parent="."]
+layout_mode = 2
+
[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/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/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/monologue_editor.gd b/scenes/main/monologue_editor.gd
new file mode 100644
index 00000000..15a6400f
--- /dev/null
+++ b/scenes/main/monologue_editor.gd
@@ -0,0 +1,177 @@
+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
+@onready var run_window := preload("res://scenes/run/run_window.tscn")
+@onready var dimmer := $"../../../Dimmer"
+@onready var document_tab_manager: DocumentTabManager = %_Tabs
+
+@onready var characters_section := %Characters
+@onready var variables_section := %Variables
+@onready var items_section := %Items
+@onready var locations_section := %Locations
+
+
+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("load_project", load_project)
+ GlobalSignal.add_listener("test_trigger", test_project)
+ GlobalSignal.add_listener("save", save)
+
+ StorylineManager.create_storyline()
+
+ # Load the editor sections after creating the storyline
+ await get_tree().process_frame
+ load_editor_sections()
+
+
+func _select_new_node() -> void:
+ graph_node_picker.open_for_node("", -1, null, null, null, true)
+
+
+func _input(event):
+ if event.is_action_pressed("Save"):
+ 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()
+ StorylineManager.get_active_storyline().history.undo()
+
+ if event.is_action_pressed("ui_redo"):
+ StorylineManager.get_active_storyline().history.redo()
+
+
+## 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 storyline := StorylineManager.get_active_storyline()
+ if storyline == null:
+ push_warning("No active storyline available to add node.")
+ return
+
+ var node := storyline.create_node(node_type)
+ if node == null:
+ push_warning("Unable to create node of type '%s'." % node_type)
+ return
+
+ var graph_edit: MonologueGraphEdit = graph_container.graph
+ var target_position := 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 := 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)
+
+
+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 not file or graph_container.is_file_opened(path):
+ return
+
+ var data = {}
+ var text = file.get_as_text()
+ if text:
+ data = JSON.parse_string(text)
+ if not data:
+ save()
+
+ var converter := NodeConverter.new()
+ var storyline = StorylineManager.get_active_storyline()
+
+ load_editor_sections()
+
+
+func load_editor_sections() -> void:
+ var storyline := StorylineManager.get_active_storyline()
+ if storyline:
+ characters_section.load_items(storyline.get_property("characters"), storyline)
+ variables_section.load_items(storyline.get_property("variables"), storyline)
+ items_section.load_items(storyline.get_property("items"), storyline)
+ locations_section.load_items(storyline.get_property("locations"), storyline)
+
+
+func save():
+ var storyline = StorylineManager.get_active_storyline()
+ if storyline.file_path.is_empty():
+ file_dialog.save_file(save_file_logic, STORYLINE_EXTENSIONS)
+ return
+ save_file_logic(storyline.file_path)
+
+
+func save_file_logic(path: String) -> void:
+ var storyline = StorylineManager.get_active_storyline()
+ var dict: Dictionary = storyline._to_dict()
+ dict["editor_version"] = ProjectSettings.get_setting("application/config/version")
+ var storyline_data: String = JSON.stringify(dict, "\t", false, true)
+
+ if path.get_extension().is_empty():
+ path = path.trim_suffix(".")
+ path = "%s.mnlg" % path
+
+ var access: FileAccess = FileAccess.open(path, FileAccess.WRITE_READ)
+ access.store_string(storyline_data)
+
+ storyline.file_path = path
+ storyline.name = path.get_file()
+ storyline.is_dirty = false
+ storyline.content_changed.emit()
+
+
+func test_project(_from_node: Variant = null):
+ return
+ #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 _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")
+
+
+func _on__tabs_add_document() -> void:
+ welcome_window.show()
diff --git a/scenes/main/monologue_control.gd.uid b/scenes/main/monologue_editor.gd.uid
similarity index 100%
rename from scenes/main/monologue_control.gd.uid
rename to scenes/main/monologue_editor.gd.uid
diff --git a/scenes/main/search_bar_container.gd b/scenes/main/search_bar_container.gd
index 4f1978af..08e0d568 100644
--- a/scenes/main/search_bar_container.gd
+++ b/scenes/main/search_bar_container.gd
@@ -1,16 +1,17 @@
extends CenterContainer
-@onready var searchbar = $SearchBar
-@onready var graph_edit_switcher = %GraphEditSwitcher
+#@onready var searchbar = $SearchBar
+#@export 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
+ pass
+ #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/tab_bar.gd b/scenes/main/tab_bar.gd
new file mode 100644
index 00000000..1783b084
--- /dev/null
+++ b/scenes/main/tab_bar.gd
@@ -0,0 +1,59 @@
+class_name DocumentTabManager extends PanelContainer
+
+signal add_document
+
+@warning_ignore("unused_private_class_variable")
+var _static_container: bool = true
+
+@onready var tab_bar: TabBar = %TabBar
+
+var _last_opened_tab: int = 0
+var _reloading_ui: bool = false
+
+
+func _ready() -> void:
+ StorylineManager.storyline_changed.connect(_on_storyline_changed)
+ StorylineManager.storyline_created.connect(_on_storyline_created)
+
+
+func _reload_ui() -> void:
+ _reloading_ui = true
+ tab_bar.clear_tabs()
+ for document_id: String in StorylineManager.get_storyline_ids():
+ var document: StorylineDocument = StorylineManager.get_storyline(document_id)
+
+ var tab_title: String = document.name
+ if document.file_path.is_empty():
+ tab_title = "<%s>" % tab_title
+ if document.is_dirty:
+ tab_title += "*"
+
+ tab_bar.add_tab(tab_title)
+ tab_bar.set_tab_metadata(tab_bar.tab_count - 1, document_id)
+
+ tab_bar.current_tab = _last_opened_tab
+
+ tab_bar.add_tab("", preload("res://ui/assets/icons/plus.svg"))
+ _reloading_ui = false
+
+
+func _on_storyline_created() -> void:
+ _reload_ui()
+
+
+func _on_storyline_changed() -> void:
+ _reload_ui()
+
+
+func _on_tab_bar_tab_changed(tab: int) -> void:
+ if tab >= tab_bar.tab_count - 1:
+ tab_bar.current_tab = _last_opened_tab
+ if _reloading_ui:
+ return
+ add_document.emit()
+ return
+
+ if tab_bar.current_tab != _last_opened_tab:
+ StorylineManager.storyline_switched.emit()
+
+ _last_opened_tab = tab_bar.current_tab
diff --git a/scenes/main/tab_bar.gd.uid b/scenes/main/tab_bar.gd.uid
new file mode 100644
index 00000000..a980195e
--- /dev/null
+++ b/scenes/main/tab_bar.gd.uid
@@ -0,0 +1 @@
+uid://cpi3ixjp3ye17
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/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/ui/assets/icons/slot_left.svg.import b/scenes/splash/dino/Render0007.png.import
similarity index 55%
rename from ui/assets/icons/slot_left.svg.import
rename to scenes/splash/dino/Render0007.png.import
index 04ad2a9d..68935292 100644
--- a/ui/assets/icons/slot_left.svg.import
+++ b/scenes/splash/dino/Render0007.png.import
@@ -2,22 +2,24 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://px17kflnvnd7"
-path="res://.godot/imported/slot_left.svg-95dad6ec06003ae7e5766bd98efb6690.ctex"
+uid="uid://yw1qkpxfsphn"
+path="res://.godot/imported/Render0007.png-ec7643933ec76239816b200808a7dbfd.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"]
+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
@@ -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
@@ -32,6 +38,3 @@ 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/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..3b7986fb 100644
--- a/scenes/splash/splash.gd
+++ b/scenes/splash/splash.gd
@@ -1,34 +1,28 @@
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
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)
- if status == ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED and timer.is_stopped():
+ if status == ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(load_scene)
- eye.play("blink")
- await eye.animation_finished
- await get_tree().create_timer(after_blink_time).timeout
+ sprite.play("blink")
+ await sprite.animation_finished
- get_window().unresizable = false
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..3c056716 100644
--- a/scenes/splash/splash.tscn
+++ b/scenes/splash/splash.tscn
@@ -1,28 +1,129 @@
-[gd_scene load_steps=7 format=3 uid="uid://bakm53e6ecbk8"]
+[gd_scene load_steps=33 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://c41kgi20w10ks" path="res://scenes/splash/dino/Render0001.png" id="2_u6rph"]
+[ext_resource type="Texture2D" uid="uid://bb8loih7rtsm7" path="res://scenes/splash/dino/Render0002.png" id="3_sfa60"]
+[ext_resource type="Texture2D" uid="uid://bkcu31j26iae2" path="res://scenes/splash/dino/Render0003.png" id="4_slhqk"]
+[ext_resource type="Texture2D" uid="uid://drdnk23jjdwhh" path="res://scenes/splash/dino/Render0004.png" id="5_oxsyg"]
+[ext_resource type="Texture2D" uid="uid://dt4bk5bfbm3yy" path="res://scenes/splash/dino/Render0005.png" id="6_i1u52"]
+[ext_resource type="Texture2D" uid="uid://cbf4cp1cctyy4" path="res://scenes/splash/dino/Render0006.png" id="7_rrr64"]
+[ext_resource type="Texture2D" uid="uid://yw1qkpxfsphn" path="res://scenes/splash/dino/Render0007.png" id="8_38atd"]
+[ext_resource type="Texture2D" uid="uid://b14o2y1e6e3hx" path="res://scenes/splash/dino/Render0008.png" id="9_1wfnp"]
+[ext_resource type="Texture2D" uid="uid://dxwyfggk3n36h" path="res://scenes/splash/dino/Render0009.png" id="10_3sqgo"]
+[ext_resource type="Texture2D" uid="uid://bnnm4e1auy3jf" path="res://scenes/splash/dino/Render0010.png" id="11_hde8r"]
+[ext_resource type="Texture2D" uid="uid://capb5oodtr03d" path="res://scenes/splash/dino/Render0011.png" id="12_kopq8"]
+[ext_resource type="Texture2D" uid="uid://d3xd0b7slaext" path="res://scenes/splash/dino/Render0012.png" id="13_ocr2a"]
+[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://b0ealo6tadsem" path="res://scenes/splash/dino/Render0016.png" id="17_sotjw"]
+[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"]
+[ext_resource type="Texture2D" uid="uid://d2i5w3curjjrc" path="res://scenes/splash/dino/Render0019.png" id="20_42o70"]
+[ext_resource type="Texture2D" uid="uid://bge0uh3rri18y" path="res://scenes/splash/dino/Render0020.png" id="21_hdejy"]
+[ext_resource type="Texture2D" uid="uid://bjvfg8v7paow2" path="res://scenes/splash/dino/Render0021.png" id="22_o4era"]
+[ext_resource type="Texture2D" uid="uid://ewqu3mclfs6c" path="res://scenes/splash/dino/Render0022.png" id="23_t80or"]
+[ext_resource type="Texture2D" uid="uid://7nbo4iudfojf" path="res://scenes/splash/dino/Render0023.png" id="24_ysi88"]
+[ext_resource type="Texture2D" uid="uid://c8ctuf25a6uie" path="res://scenes/splash/dino/Render0024.png" id="25_7nks1"]
+[ext_resource type="Texture2D" uid="uid://dv8dbtbhofxo" path="res://scenes/splash/dino/Render0025.png" id="26_6j2es"]
+[ext_resource type="Texture2D" uid="uid://b4suruuapjkh2" path="res://scenes/splash/dino/Render0026.png" id="27_bx5ox"]
+[ext_resource type="Texture2D" uid="uid://dj7gb22q2y3qq" path="res://scenes/splash/dino/Render0027.png" id="28_wv76i"]
+[ext_resource type="Texture2D" uid="uid://dg2a46d32at3t" path="res://scenes/splash/dino/Render0028.png" id="29_ltc0p"]
+[ext_resource type="Texture2D" uid="uid://50icxn20btnq" path="res://scenes/splash/dino/Render0029.png" id="30_hc4f3"]
+[ext_resource type="Texture2D" uid="uid://dd00100w3siit" path="res://scenes/splash/dino/Render0030.png" id="31_ohppt"]
[sub_resource type="SpriteFrames" id="SpriteFrames_tj644"]
animations = [{
"frames": [{
"duration": 1.0,
-"texture": ExtResource("3_qdaos")
+"texture": ExtResource("2_u6rph")
}, {
"duration": 1.0,
-"texture": ExtResource("7_ueewa")
+"texture": ExtResource("3_sfa60")
}, {
-"duration": 2.0,
-"texture": ExtResource("5_783yv")
+"duration": 1.0,
+"texture": ExtResource("4_slhqk")
+}, {
+"duration": 1.0,
+"texture": ExtResource("5_oxsyg")
+}, {
+"duration": 1.0,
+"texture": ExtResource("6_i1u52")
+}, {
+"duration": 1.0,
+"texture": ExtResource("7_rrr64")
+}, {
+"duration": 1.0,
+"texture": ExtResource("8_38atd")
+}, {
+"duration": 1.0,
+"texture": ExtResource("9_1wfnp")
+}, {
+"duration": 1.0,
+"texture": ExtResource("10_3sqgo")
+}, {
+"duration": 1.0,
+"texture": ExtResource("11_hde8r")
+}, {
+"duration": 1.0,
+"texture": ExtResource("12_kopq8")
+}, {
+"duration": 1.0,
+"texture": ExtResource("13_ocr2a")
+}, {
+"duration": 1.0,
+"texture": ExtResource("14_wk58s")
+}, {
+"duration": 1.0,
+"texture": ExtResource("15_pacsv")
+}, {
+"duration": 1.0,
+"texture": ExtResource("16_tm5oe")
+}, {
+"duration": 1.0,
+"texture": ExtResource("17_sotjw")
+}, {
+"duration": 1.0,
+"texture": ExtResource("18_7g2nk")
+}, {
+"duration": 1.0,
+"texture": ExtResource("19_hgma0")
+}, {
+"duration": 1.0,
+"texture": ExtResource("20_42o70")
+}, {
+"duration": 1.0,
+"texture": ExtResource("21_hdejy")
+}, {
+"duration": 1.0,
+"texture": ExtResource("22_o4era")
+}, {
+"duration": 1.0,
+"texture": ExtResource("23_t80or")
+}, {
+"duration": 1.0,
+"texture": ExtResource("24_ysi88")
+}, {
+"duration": 1.0,
+"texture": ExtResource("25_7nks1")
+}, {
+"duration": 1.0,
+"texture": ExtResource("26_6j2es")
}, {
"duration": 1.0,
-"texture": ExtResource("7_ueewa")
+"texture": ExtResource("27_bx5ox")
}, {
"duration": 1.0,
-"texture": ExtResource("3_qdaos")
+"texture": ExtResource("28_wv76i")
+}, {
+"duration": 1.0,
+"texture": ExtResource("29_ltc0p")
+}, {
+"duration": 1.0,
+"texture": ExtResource("30_hc4f3")
+}, {
+"duration": 1.0,
+"texture": ExtResource("31_ohppt")
}],
"loop": false,
"name": &"blink",
@@ -39,35 +140,17 @@ grow_vertical = 2
script = ExtResource("1_pl0ug")
load_scene = "res://scenes/main/app.tscn"
-[node name="ColorRect" type="ColorRect" parent="."]
+[node name="PanelContainer" type="PanelContainer" 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="."]
-layout_mode = 1
-anchors_preset = 15
-anchor_right = 1.0
-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="."]
+scale = Vector2(0.5, 0.5)
sprite_frames = SubResource("SpriteFrames_tj644")
animation = &"blink"
-
-[node name="tMinDisplayTime" type="Timer" parent="."]
-one_shot = true
+frame = 29
+frame_progress = 1.0
diff --git a/src/common/property.gd b/src/common/property.gd
new file mode 100644
index 00000000..4a80f580
--- /dev/null
+++ b/src/common/property.gd
@@ -0,0 +1,186 @@
+class_name Property extends RefCounted
+
+signal value_changed(old_value: Variant, new_value: Variant)
+
+var name: String = ""
+var value: Variant = 0
+var type: String = ""
+var _settings: Dictionary = {}
+var settings: Dictionary = {}
+var descriptor
+var _bindings: Array = []
+
+const DEFAULT_SETTINGS := {
+ "visible_in_graph": true,
+ "visible_in_inspector": true,
+ "editable": true,
+ "exposable": true,
+ "exposed": false,
+ "export": false,
+ "category": "General",
+ "label": "",
+}
+
+## Tracks input connections to this property (nodes connecting TO this property)
+var connected_from: Array[Dictionary] = [] # [{node_name: String, property_name: String, port: int}]
+
+## Tracks output connections from this property (nodes this property connects TO)
+var connected_to: Array[Dictionary] = [] # [{node_name: String, property_name: String, port: int}]
+
+## 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
+ type = ptype
+ descriptor = FieldBucket.get_descriptor(ptype)
+ _settings = DEFAULT_SETTINGS.duplicate(true)
+ if descriptor and descriptor.default_settings:
+ _settings.merge(descriptor.default_settings, true)
+ if psettings:
+ _settings.merge(psettings, true)
+ if not _settings.get("category"):
+ _settings["category"] = DEFAULT_SETTINGS["category"]
+ if _settings.get("label", "") == "":
+ _settings.erase("label")
+
+
+func bind_field(field: Field, target_owner: InspectableObject = null):
+ if not is_instance_valid(field):
+ return null
+ if not field.is_inside_tree():
+ field.tree_entered.connect(
+ _on_field_tree_entered.bind(field, target_owner), CONNECT_ONE_SHOT
+ )
+ return null
+ var binding = FieldBucket.bind(self, field, target_owner)
+ if binding:
+ _bindings.append(binding)
+ return binding
+
+
+func _on_field_tree_entered(field: Field, target_owner: InspectableObject) -> void:
+ bind_field(field, target_owner)
+
+
+func set_value(new_value: Variant) -> void:
+ if 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 get_settings() -> Dictionary:
+ var merged_settings: Dictionary = settings.duplicate(true)
+ merged_settings.merge(_settings)
+ return merged_settings
+
+
+func has_settings(skey) -> 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()
+
+
+func is_intput_connected() -> bool:
+ return is_input_connected()
+
+
+func add_connection_from(node_id: String, property_name: String) -> void:
+ var conn = {"node_id": node_id, "property_name": property_name}
+ if conn not in connected_from:
+ connected_from.append(conn)
+
+
+func add_connection_to(node_id: String, property_name: String) -> void:
+ var conn = {"node_id": node_id, "property_name": property_name}
+ if conn not in connected_to:
+ connected_to.append(conn)
+
+
+func remove_connection_from(node_id: String, property_name: String) -> void:
+ connected_from = connected_from.filter(
+ func(c): return not (c["node_id"] == node_id and c["property_name"] == property_name)
+ )
+
+
+func remove_connection_to(node_id: String, property_name: String) -> void:
+ connected_to = connected_to.filter(
+ func(c): return not (c["node_id"] == node_id and c["property_name"] == property_name)
+ )
+
+
+func clear_connections() -> void:
+ connected_from.clear()
+ connected_to.clear()
+
+
+func refresh_bindings() -> void:
+ _bindings = _bindings.filter(func(binding): return binding and binding.is_active())
+ for binding in _bindings:
+ binding.refresh()
+
+
+func get_descriptor():
+ if descriptor == null:
+ descriptor = FieldBucket.get_descriptor(type)
+ return descriptor
+
+
+func _to_dict() -> Dictionary:
+ var dict: Dictionary = {}
+ if not settings.is_empty():
+ dict["_editor_settings"] = settings
+
+ if get_settings_value("exposed") and not connected_from.is_empty():
+ dict["from_node"] = connected_from
+ dict["value"] = get_value()
+
+ if get_settings_value("export"):
+ dict["to_node"] = connected_to
+ return dict
+
+
+# Do not trigger undo/redo
+func _from_dict(dict: Dictionary) -> void:
+ value = dict.get("value", value)
+ settings = dict.get("_editor_settings", settings)
+ connected_from = dict.get("from_node", connected_from)
+ connected_to = dict.get("to_node", connected_to)
diff --git a/src/common/property.gd.uid b/src/common/property.gd.uid
new file mode 100644
index 00000000..a56a8f95
--- /dev/null
+++ b/src/common/property.gd.uid
@@ -0,0 +1 @@
+uid://cvxcfitjpar4y
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..c5160e0c 100644
--- a/ui/assets/cursors/closed_hand.svg.import
+++ b/ui/assets/cursors/closed_hand.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/cursors/cursor.svg.import b/ui/assets/cursors/cursor.svg.import
index 26afe26e..1d802f36 100644
--- a/ui/assets/cursors/cursor.svg.import
+++ b/ui/assets/cursors/cursor.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/cursors/hand.svg.import b/ui/assets/cursors/hand.svg.import
index 1ddf285f..1e9206d2 100644
--- a/ui/assets/cursors/hand.svg.import
+++ b/ui/assets/cursors/hand.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import b/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import
index 9dd5ab85..a5355734 100644
--- a/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import
+++ b/ui/assets/fonts/CourierNewPS-BoldMT.ttf.import
@@ -21,6 +21,7 @@ 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
diff --git a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import b/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import
index 24fe5396..9a9a52cf 100644
--- a/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import
+++ b/ui/assets/fonts/CourierNewPS-ItalicMT.ttf.import
@@ -21,6 +21,7 @@ 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
diff --git a/ui/assets/fonts/CourierNewPSMT.ttf.import b/ui/assets/fonts/CourierNewPSMT.ttf.import
index 2197aec0..4def57fb 100644
--- a/ui/assets/fonts/CourierNewPSMT.ttf.import
+++ b/ui/assets/fonts/CourierNewPSMT.ttf.import
@@ -21,6 +21,7 @@ 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
diff --git a/ui/assets/fonts/GeneralSans-Bold.otf.import b/ui/assets/fonts/GeneralSans-Bold.otf.import
index c5798a0f..9ecab4ba 100644
--- a/ui/assets/fonts/GeneralSans-Bold.otf.import
+++ b/ui/assets/fonts/GeneralSans-Bold.otf.import
@@ -21,10 +21,11 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
diff --git a/ui/assets/fonts/GeneralSans-BoldItalic.otf.import b/ui/assets/fonts/GeneralSans-BoldItalic.otf.import
index d504e870..63e68d39 100644
--- a/ui/assets/fonts/GeneralSans-BoldItalic.otf.import
+++ b/ui/assets/fonts/GeneralSans-BoldItalic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Extralight.otf.import b/ui/assets/fonts/GeneralSans-Extralight.otf.import
index cb5fe973..c11df6e5 100644
--- a/ui/assets/fonts/GeneralSans-Extralight.otf.import
+++ b/ui/assets/fonts/GeneralSans-Extralight.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-ExtralightItalic.otf.import b/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import
index 4b6fe0a7..28967deb 100644
--- a/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import
+++ b/ui/assets/fonts/GeneralSans-ExtralightItalic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Italic.otf.import b/ui/assets/fonts/GeneralSans-Italic.otf.import
index 566271a8..07da4880 100644
--- a/ui/assets/fonts/GeneralSans-Italic.otf.import
+++ b/ui/assets/fonts/GeneralSans-Italic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Light.otf.import b/ui/assets/fonts/GeneralSans-Light.otf.import
index 8db9b3ba..010a3d69 100644
--- a/ui/assets/fonts/GeneralSans-Light.otf.import
+++ b/ui/assets/fonts/GeneralSans-Light.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-LightItalic.otf.import b/ui/assets/fonts/GeneralSans-LightItalic.otf.import
index a42f79d1..8e9c4a2b 100644
--- a/ui/assets/fonts/GeneralSans-LightItalic.otf.import
+++ b/ui/assets/fonts/GeneralSans-LightItalic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Medium.otf.import b/ui/assets/fonts/GeneralSans-Medium.otf.import
index aba933b4..43c89b3f 100644
--- a/ui/assets/fonts/GeneralSans-Medium.otf.import
+++ b/ui/assets/fonts/GeneralSans-Medium.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-MediumItalic.otf.import b/ui/assets/fonts/GeneralSans-MediumItalic.otf.import
index fa77a3b4..aa83228a 100644
--- a/ui/assets/fonts/GeneralSans-MediumItalic.otf.import
+++ b/ui/assets/fonts/GeneralSans-MediumItalic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Regular.otf.import b/ui/assets/fonts/GeneralSans-Regular.otf.import
index 37850d24..b45d5aaf 100644
--- a/ui/assets/fonts/GeneralSans-Regular.otf.import
+++ b/ui/assets/fonts/GeneralSans-Regular.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-Semibold.otf.import b/ui/assets/fonts/GeneralSans-Semibold.otf.import
index 7de9ed1e..00e48935 100644
--- a/ui/assets/fonts/GeneralSans-Semibold.otf.import
+++ b/ui/assets/fonts/GeneralSans-Semibold.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/GeneralSans-SemiboldItalic.otf.import b/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import
index 94813f6d..6b775981 100644
--- a/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import
+++ b/ui/assets/fonts/GeneralSans-SemiboldItalic.otf.import
@@ -21,15 +21,22 @@ 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=0.0
+oversampling=2.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
-preload=[]
+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/Montserrat-Variable.ttf.import b/ui/assets/fonts/Montserrat-Variable.ttf.import
index 7915eb2f..ee1aca6d 100644
--- a/ui/assets/fonts/Montserrat-Variable.ttf.import
+++ b/ui/assets/fonts/Montserrat-Variable.ttf.import
@@ -21,6 +21,7 @@ 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
diff --git a/ui/assets/icons/action.svg.import b/ui/assets/icons/action.svg.import
index 366afe8a..8b09c5fd 100644
--- a/ui/assets/icons/action.svg.import
+++ b/ui/assets/icons/action.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/arrow_down.svg.import b/ui/assets/icons/arrow_down.svg.import
index 21a0eea3..5e591439 100644
--- a/ui/assets/icons/arrow_down.svg.import
+++ b/ui/assets/icons/arrow_down.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/arrow_left.svg.import b/ui/assets/icons/arrow_left.svg.import
index 7d63cc96..49a8608f 100644
--- a/ui/assets/icons/arrow_left.svg.import
+++ b/ui/assets/icons/arrow_left.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/arrow_right.svg.import b/ui/assets/icons/arrow_right.svg.import
index 34d4d3a8..93acb82b 100644
--- a/ui/assets/icons/arrow_right.svg.import
+++ b/ui/assets/icons/arrow_right.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/arrow_up.svg.import b/ui/assets/icons/arrow_up.svg.import
index dce78ce0..b8058dd3 100644
--- a/ui/assets/icons/arrow_up.svg.import
+++ b/ui/assets/icons/arrow_up.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..6c1d2e79 100644
--- a/ui/assets/icons/calendar.svg.import
+++ b/ui/assets/icons/calendar.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/cell_empty.svg.import b/ui/assets/icons/cell_empty.svg.import
index 4c7f7339..72578de7 100644
--- a/ui/assets/icons/cell_empty.svg.import
+++ b/ui/assets/icons/cell_empty.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/cell_left.svg.import b/ui/assets/icons/cell_left.svg.import
index f32f0386..2549aa26 100644
--- a/ui/assets/icons/cell_left.svg.import
+++ b/ui/assets/icons/cell_left.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/cell_middle.svg.import b/ui/assets/icons/cell_middle.svg.import
index 63bbb18f..9c1b5d1d 100644
--- a/ui/assets/icons/cell_middle.svg.import
+++ b/ui/assets/icons/cell_middle.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/cell_right.svg.import b/ui/assets/icons/cell_right.svg.import
index 8e13952c..0605bf97 100644
--- a/ui/assets/icons/cell_right.svg.import
+++ b/ui/assets/icons/cell_right.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/cell_single.svg.import b/ui/assets/icons/cell_single.svg.import
index 8af2441e..9d25d76b 100644
--- a/ui/assets/icons/cell_single.svg.import
+++ b/ui/assets/icons/cell_single.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/character.svg.import b/ui/assets/icons/character.svg.import
index 3ef8ec84..f67d3230 100644
--- a/ui/assets/icons/character.svg.import
+++ b/ui/assets/icons/character.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/check.svg.import b/ui/assets/icons/check.svg.import
index d401f7a7..198dbd8a 100644
--- a/ui/assets/icons/check.svg.import
+++ b/ui/assets/icons/check.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://rrcqi68frkrm"
-path="res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.dpitex"
[deps]
source_file="res://ui/assets/icons/check.svg"
-dest_files=["res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.ctex"]
+dest_files=["res://.godot/imported/check.svg-aed49858c602f08277040dc13320eb4c.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={}
+compress=true
diff --git a/ui/assets/icons/choice.svg.import b/ui/assets/icons/choice.svg.import
index 0e8cb413..ec981dff 100644
--- a/ui/assets/icons/choice.svg.import
+++ b/ui/assets/icons/choice.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..c6fa5eb4 100644
--- a/ui/assets/icons/comment.svg.import
+++ b/ui/assets/icons/comment.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/condition.svg.import b/ui/assets/icons/condition.svg.import
index 7575f16b..8cec19b1 100644
--- a/ui/assets/icons/condition.svg.import
+++ b/ui/assets/icons/condition.svg.import
@@ -1,37 +1,18 @@
[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={}
+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.import b/ui/assets/icons/cross.svg.import
index 3ed6436f..12e8d5e5 100644
--- a/ui/assets/icons/cross.svg.import
+++ b/ui/assets/icons/cross.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/dice.svg.import b/ui/assets/icons/dice.svg.import
index 592ce0ab..445377d8 100644
--- a/ui/assets/icons/dice.svg.import
+++ b/ui/assets/icons/dice.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..72272232 100644
--- a/ui/assets/icons/dots_grid.svg.import
+++ b/ui/assets/icons/dots_grid.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/exit.png.import b/ui/assets/icons/exit.png.import
index 26b5d016..2c9b5cb6 100644
--- a/ui/assets/icons/exit.png.import
+++ b/ui/assets/icons/exit.png.import
@@ -18,6 +18,8 @@ dest_files=["res://.godot/imported/exit.png-89bb20104225ee47e1c62bcf63e94c1a.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/exit.svg.import b/ui/assets/icons/exit.svg.import
index 703dc4c2..f3b78464 100644
--- a/ui/assets/icons/exit.svg.import
+++ b/ui/assets/icons/exit.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://b8r5v6gy8jyyn"
-path="res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.dpitex"
[deps]
source_file="res://ui/assets/icons/exit.svg"
-dest_files=["res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.ctex"]
+dest_files=["res://.godot/imported/exit.svg-c9f57222511a31936a0e1bf2afef851e.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={}
+compress=true
diff --git a/ui/assets/icons/expand.svg.import b/ui/assets/icons/expand.svg.import
index 61d9e5df..5ce09a42 100644
--- a/ui/assets/icons/expand.svg.import
+++ b/ui/assets/icons/expand.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..be223b38 100644
--- a/ui/assets/icons/eye.svg.import
+++ b/ui/assets/icons/eye.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/eye_closed.svg.import b/ui/assets/icons/eye_closed.svg.import
index e979e831..5ed7ccf1 100644
--- a/ui/assets/icons/eye_closed.svg.import
+++ b/ui/assets/icons/eye_closed.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/file.svg.import b/ui/assets/icons/file.svg.import
index 23741512..4b6f5c39 100644
--- a/ui/assets/icons/file.svg.import
+++ b/ui/assets/icons/file.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/file_character.svg.import b/ui/assets/icons/file_character.svg.import
index 6f672434..480c4e44 100644
--- a/ui/assets/icons/file_character.svg.import
+++ b/ui/assets/icons/file_character.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/file_plus.svg.import b/ui/assets/icons/file_plus.svg.import
index 175447c1..6c400db5 100644
--- a/ui/assets/icons/file_plus.svg.import
+++ b/ui/assets/icons/file_plus.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/filter_funnel.svg.import b/ui/assets/icons/filter_funnel.svg.import
index 09f03a6e..6be67c57 100644
--- a/ui/assets/icons/filter_funnel.svg.import
+++ b/ui/assets/icons/filter_funnel.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/folder.svg.import b/ui/assets/icons/folder.svg.import
index ea53cb16..47c7313a 100644
--- a/ui/assets/icons/folder.svg.import
+++ b/ui/assets/icons/folder.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/folder_download.svg.import b/ui/assets/icons/folder_download.svg.import
index bc38bfae..b1ab8dad 100644
--- a/ui/assets/icons/folder_download.svg.import
+++ b/ui/assets/icons/folder_download.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..d9c3e24e 100644
--- a/ui/assets/icons/folder_search.svg.import
+++ b/ui/assets/icons/folder_search.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/grid.svg.import b/ui/assets/icons/grid.svg.import
index 5c8fffcf..a9e6c5b5 100644
--- a/ui/assets/icons/grid.svg.import
+++ b/ui/assets/icons/grid.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..ece1db4d 100644
--- a/ui/assets/icons/image_down.svg.import
+++ b/ui/assets/icons/image_down.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/improt_cell.svg.import b/ui/assets/icons/improt_cell.svg.import
index 20253c8d..21cd635b 100644
--- a/ui/assets/icons/improt_cell.svg.import
+++ b/ui/assets/icons/improt_cell.svg.import
@@ -1,37 +1,18 @@
[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={}
+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/layout_right.svg.import b/ui/assets/icons/layout_right.svg.import
index d43c0029..6634706b 100644
--- a/ui/assets/icons/layout_right.svg.import
+++ b/ui/assets/icons/layout_right.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/link.svg.import b/ui/assets/icons/link.svg.import
index c255a162..172e2a75 100644
--- a/ui/assets/icons/link.svg.import
+++ b/ui/assets/icons/link.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/logo_white.svg.import b/ui/assets/icons/logo_white.svg.import
index 5f615f9d..85af35f2 100644
--- a/ui/assets/icons/logo_white.svg.import
+++ b/ui/assets/icons/logo_white.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://dd8v3hpxxk33d"
-path="res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.dpitex"
[deps]
source_file="res://ui/assets/icons/logo_white.svg"
-dest_files=["res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.ctex"]
+dest_files=["res://.godot/imported/logo_white.svg-9b22651952339bccf9269a0af7ea05cc.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={}
+compress=true
diff --git a/ui/assets/icons/media_loop.svg.import b/ui/assets/icons/media_loop.svg.import
index 05510620..923feb73 100644
--- a/ui/assets/icons/media_loop.svg.import
+++ b/ui/assets/icons/media_loop.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/media_play.svg.import b/ui/assets/icons/media_play.svg.import
index ad200ffd..ab0f467d 100644
--- a/ui/assets/icons/media_play.svg.import
+++ b/ui/assets/icons/media_play.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/media_play_backward.svg.import b/ui/assets/icons/media_play_backward.svg.import
index e8d39a96..1418768c 100644
--- a/ui/assets/icons/media_play_backward.svg.import
+++ b/ui/assets/icons/media_play_backward.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/media_skip_backward.svg.import b/ui/assets/icons/media_skip_backward.svg.import
index 9646ce33..371c039a 100644
--- a/ui/assets/icons/media_skip_backward.svg.import
+++ b/ui/assets/icons/media_skip_backward.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/media_skip_forward.svg.import b/ui/assets/icons/media_skip_forward.svg.import
index 9d78cf43..4770a7da 100644
--- a/ui/assets/icons/media_skip_forward.svg.import
+++ b/ui/assets/icons/media_skip_forward.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/media_stop.svg.import b/ui/assets/icons/media_stop.svg.import
index b370ed48..eb44e99e 100644
--- a/ui/assets/icons/media_stop.svg.import
+++ b/ui/assets/icons/media_stop.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/minus_min.svg.import b/ui/assets/icons/minus_min.svg.import
index 3b67b8d8..74aee1e0 100644
--- a/ui/assets/icons/minus_min.svg.import
+++ b/ui/assets/icons/minus_min.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/move.svg.import b/ui/assets/icons/move.svg.import
index 095f9b5b..d0129c1a 100644
--- a/ui/assets/icons/move.svg.import
+++ b/ui/assets/icons/move.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..b569a509 100644
--- a/ui/assets/icons/node.svg.import
+++ b/ui/assets/icons/node.svg.import
@@ -1,37 +1,18 @@
[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={}
+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..24dbba44 100644
--- a/ui/assets/icons/offset_cross.svg.import
+++ b/ui/assets/icons/offset_cross.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/path.svg.import b/ui/assets/icons/path.svg.import
index c6b643ae..262a2e3a 100644
--- a/ui/assets/icons/path.svg.import
+++ b/ui/assets/icons/path.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://bmpfvosa78tot"
-path="res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.dpitex"
[deps]
source_file="res://ui/assets/icons/path.svg"
-dest_files=["res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.ctex"]
+dest_files=["res://.godot/imported/path.svg-41a42a51f6587400da9006aee062b4bc.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={}
+compress=true
diff --git a/ui/assets/icons/pen.svg.import b/ui/assets/icons/pen.svg.import
index f3b96fcd..174c8b09 100644
--- a/ui/assets/icons/pen.svg.import
+++ b/ui/assets/icons/pen.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/picture.svg.import b/ui/assets/icons/picture.svg.import
index 04f02951..9a0d1e6f 100644
--- a/ui/assets/icons/picture.svg.import
+++ b/ui/assets/icons/picture.svg.import
@@ -1,37 +1,18 @@
[remap]
-importer="texture"
-type="CompressedTexture2D"
+importer="svg"
+type="DPITexture"
uid="uid://csskef1726pia"
-path="res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.ctex"
-metadata={
-"vram_texture": false
-}
+path="res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.dpitex"
[deps]
source_file="res://ui/assets/icons/picture.svg"
-dest_files=["res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.ctex"]
+dest_files=["res://.godot/imported/picture.svg-fb6e9ca01b12bc3ac51c48cf77dff73d.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={}
+compress=true
diff --git a/ui/assets/icons/play.svg.import b/ui/assets/icons/play.svg.import
index b6d19dfd..03476935 100644
--- a/ui/assets/icons/play.svg.import
+++ b/ui/assets/icons/play.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/plus.svg.import b/ui/assets/icons/plus.svg.import
index 1ba9f56c..932ebbab 100644
--- a/ui/assets/icons/plus.svg.import
+++ b/ui/assets/icons/plus.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/plus_min.svg.import b/ui/assets/icons/plus_min.svg.import
index fc95b74d..3c6b4f2f 100644
--- a/ui/assets/icons/plus_min.svg.import
+++ b/ui/assets/icons/plus_min.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/px_character.png b/ui/assets/icons/px_character.png
new file mode 100644
index 00000000..4a1872db
Binary files /dev/null and b/ui/assets/icons/px_character.png differ
diff --git a/ui/assets/icons/px_character.png.import b/ui/assets/icons/px_character.png.import
new file mode 100644
index 00000000..8b6faa55
--- /dev/null
+++ b/ui/assets/icons/px_character.png.import
@@ -0,0 +1,40 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dd4l57i1csm18"
+path="res://.godot/imported/px_character.png-36485977ecc4fa24e5383e7c00c00380.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://ui/assets/icons/px_character.png"
+dest_files=["res://.godot/imported/px_character.png-36485977ecc4fa24e5383e7c00c00380.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/ui/assets/icons/recording.svg.import b/ui/assets/icons/recording.svg.import
index ca19ad9b..10261f2a 100644
--- a/ui/assets/icons/recording.svg.import
+++ b/ui/assets/icons/recording.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/root.svg.import b/ui/assets/icons/root.svg.import
index c20bf61c..ec92b77d 100644
--- a/ui/assets/icons/root.svg.import
+++ b/ui/assets/icons/root.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/search.svg.import b/ui/assets/icons/search.svg.import
index 1234316a..34e9f249 100644
--- a/ui/assets/icons/search.svg.import
+++ b/ui/assets/icons/search.svg.import
@@ -1,37 +1,18 @@
[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={}
+compress=true
diff --git a/ui/assets/icons/settings.svg.import b/ui/assets/icons/settings.svg.import
index 468d96ff..38f089aa 100644
--- a/ui/assets/icons/settings.svg.import
+++ b/ui/assets/icons/settings.svg.import
@@ -1,37 +1,18 @@
[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={}
+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 @@
-