diff --git a/Assets/Icons/Add.svg b/Assets/Icons/Add.svg
new file mode 100644
index 0000000..3475284
--- /dev/null
+++ b/Assets/Icons/Add.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/Assets/Icons/Add.svg.import b/Assets/Icons/Add.svg.import
new file mode 100644
index 0000000..aa962d7
--- /dev/null
+++ b/Assets/Icons/Add.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bbennjke0ioen"
+path="res://.godot/imported/Add.svg-078ad3409bd2c967c3fab28fa9bba462.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/Add.svg"
+dest_files=["res://.godot/imported/Add.svg-078ad3409bd2c967c3fab28fa9bba462.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/Icons/Arrow_back.svg b/Assets/Icons/Arrow_back.svg
new file mode 100644
index 0000000..aceb02b
--- /dev/null
+++ b/Assets/Icons/Arrow_back.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/Assets/Icons/Arrow_back.svg.import b/Assets/Icons/Arrow_back.svg.import
new file mode 100644
index 0000000..9caefe0
--- /dev/null
+++ b/Assets/Icons/Arrow_back.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c1cl6qetwg8st"
+path="res://.godot/imported/Arrow_back.svg-a0911a1be2b399f6e203ad85c7bde526.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/Arrow_back.svg"
+dest_files=["res://.godot/imported/Arrow_back.svg-a0911a1be2b399f6e203ad85c7bde526.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=2.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/Icons/Check.svg b/Assets/Icons/Check.svg
new file mode 100644
index 0000000..689ab54
--- /dev/null
+++ b/Assets/Icons/Check.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/Assets/Icons/Check.svg.import b/Assets/Icons/Check.svg.import
new file mode 100644
index 0000000..a925a22
--- /dev/null
+++ b/Assets/Icons/Check.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bi4ttx5uklwl7"
+path="res://.godot/imported/Check.svg-3fc7cd3289fca542d2045dad8f21839e.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/Check.svg"
+dest_files=["res://.godot/imported/Check.svg-3fc7cd3289fca542d2045dad8f21839e.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/Icons/Checked.svg b/Assets/Icons/Checked.svg
new file mode 100644
index 0000000..ef045a8
--- /dev/null
+++ b/Assets/Icons/Checked.svg
@@ -0,0 +1,41 @@
+
+
diff --git a/Assets/Icons/Checked.svg.import b/Assets/Icons/Checked.svg.import
new file mode 100644
index 0000000..13567af
--- /dev/null
+++ b/Assets/Icons/Checked.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c6x6e8xi6lupf"
+path="res://.godot/imported/Checked.svg-ef4a8ffe3594dc64b1dc900e0809d8c0.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/Checked.svg"
+dest_files=["res://.godot/imported/Checked.svg-ef4a8ffe3594dc64b1dc900e0809d8c0.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/Icons/Invert.svg b/Assets/Icons/Invert.svg
new file mode 100644
index 0000000..e351db2
--- /dev/null
+++ b/Assets/Icons/Invert.svg
@@ -0,0 +1,50 @@
+
+
diff --git a/Assets/Icons/Invert.svg.import b/Assets/Icons/Invert.svg.import
new file mode 100644
index 0000000..37f934e
--- /dev/null
+++ b/Assets/Icons/Invert.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b2nos4alwgqcg"
+path="res://.godot/imported/Invert.svg-3001572e5e1f25db4c2b26d0a6c42fb1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Icons/Invert.svg"
+dest_files=["res://.godot/imported/Invert.svg-3001572e5e1f25db4c2b26d0a6c42fb1.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/Assets/Main.theme b/Assets/Main.theme
index d83e1d3..3ed66f6 100644
Binary files a/Assets/Main.theme and b/Assets/Main.theme differ
diff --git a/Components/Accept Dialog/Accept_dialog.tscn b/Components/Accept Dialog/Accept_dialog.tscn
index 358fa21..9ce7723 100644
--- a/Components/Accept Dialog/Accept_dialog.tscn
+++ b/Components/Accept Dialog/Accept_dialog.tscn
@@ -6,7 +6,6 @@
initial_position = 4
size = Vector2i(647, 100)
visible = true
-always_on_top = true
dialog_autowrap = true
script = ExtResource("1_55rlu")
diff --git a/Components/File Load Menu/FileLoadMenu.gd b/Components/File Load Menu/FileLoadMenu.gd
new file mode 100644
index 0000000..3db950a
--- /dev/null
+++ b/Components/File Load Menu/FileLoadMenu.gd
@@ -0,0 +1,4 @@
+extends FileDialog
+
+func _any_close_signal() -> void:
+ self.queue_free()
diff --git a/Components/File Load Menu/FileLoadMenu.tscn b/Components/File Load Menu/FileLoadMenu.tscn
new file mode 100644
index 0000000..c88df0a
--- /dev/null
+++ b/Components/File Load Menu/FileLoadMenu.tscn
@@ -0,0 +1,20 @@
+[gd_scene load_steps=3 format=3 uid="uid://djm2s80n0ci54"]
+
+[ext_resource type="Script" path="res://Components/File Load Menu/FileLoadMenu.gd" id="1_bjt7g"]
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_ulnoa"]
+
+[node name="FileLoadMenu" type="FileDialog"]
+title = "Open a File"
+initial_position = 5
+size = Vector2i(778, 449)
+visible = true
+theme = ExtResource("1_ulnoa")
+ok_button_text = "Open"
+file_mode = 0
+access = 2
+use_native_dialog = true
+script = ExtResource("1_bjt7g")
+
+[connection signal="canceled" from="." to="." method="_any_close_signal"]
+[connection signal="close_requested" from="." to="." method="_any_close_signal"]
+[connection signal="confirmed" from="." to="." method="_any_close_signal"]
diff --git a/Components/File Save Menu/FileSaveMenu.gd b/Components/File Save Menu/FileSaveMenu.gd
new file mode 100644
index 0000000..3db950a
--- /dev/null
+++ b/Components/File Save Menu/FileSaveMenu.gd
@@ -0,0 +1,4 @@
+extends FileDialog
+
+func _any_close_signal() -> void:
+ self.queue_free()
diff --git a/Components/File Save Menu/FileSaveMenu.tscn b/Components/File Save Menu/FileSaveMenu.tscn
new file mode 100644
index 0000000..c6d7792
--- /dev/null
+++ b/Components/File Save Menu/FileSaveMenu.tscn
@@ -0,0 +1,18 @@
+[gd_scene load_steps=3 format=3 uid="uid://cfgsds74efvse"]
+
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_56t30"]
+[ext_resource type="Script" path="res://Components/File Save Menu/FileSaveMenu.gd" id="1_st57w"]
+
+[node name="FileSaveMenu" type="FileDialog"]
+initial_position = 5
+size = Vector2i(903, 449)
+visible = true
+theme = ExtResource("1_56t30")
+ok_button_text = "Save"
+access = 2
+use_native_dialog = true
+script = ExtResource("1_st57w")
+
+[connection signal="canceled" from="." to="." method="_any_close_signal"]
+[connection signal="close_requested" from="." to="." method="_any_close_signal"]
+[connection signal="confirmed" from="." to="." method="_any_close_signal"]
diff --git a/Components/ItemListView/ItemListView.gd b/Components/ItemListView/ItemListView.gd
new file mode 100644
index 0000000..47949c4
--- /dev/null
+++ b/Components/ItemListView/ItemListView.gd
@@ -0,0 +1,247 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+@tool
+extends Control
+## GUI Component for a list view.
+
+signal edit_requested(items: Array)
+signal delete_requested(items: Array)
+signal take_requested(items: Array)
+signal add_requested()
+signal selection_changed(items: Array)
+
+@export var show_tool_bar: bool = true : set = set_show_tool_bar
+@export var show_new: bool = true : set = set_show_new
+@export var show_select: bool = true : set = set_show_select
+@export var show_invert: bool = true : set = set_show_invert
+@export var show_take: bool = true : set = set_show_take
+@export var show_edit: bool = true : set = set_show_edit
+@export var show_delete: bool = true : set = set_show_delete
+@export var show_separators: bool = true : set = set_show_separators
+
+@export var buttons_enabled: bool = true : set = set_buttons_enabled
+@export var allow_multi_select: bool = true
+
+@onready var item_container: VBoxContainer = self.get_node("PanelContainer2/ScrollContainer/ItemContainer")
+
+var currently_selected_items: Array = []
+var last_selected_item: Control
+
+var object_refs: Dictionary
+
+func _ready() -> void:
+ set_show_tool_bar(show_tool_bar)
+ set_show_new(show_new)
+ set_show_select(show_select)
+ set_show_invert(show_invert)
+ set_show_take(show_take)
+ set_show_edit(show_edit)
+ set_show_delete(show_delete)
+ set_show_separators(show_separators)
+
+
+func add_items(items: Array) -> void:
+ ## Adds an item to the list
+
+ for item in items:
+ if _is_valid_object(item):
+ var new_item_node: Control = Globals.components.list_item.instantiate()
+ new_item_node.set_item_name(item.name)
+
+ new_item_node.control_node = self
+ new_item_node.name = item.uuid
+ new_item_node.select_requested.connect(self._on_list_item_select_request)
+
+ if item.get("is_selected") == true:
+ new_item_node.set_highlighted(item.is_selected)
+ currently_selected_items.append(item)
+
+ item_container.add_child(new_item_node)
+ object_refs[new_item_node] = item
+
+
+func remove_all() -> void:
+ ## Removes all items from the list
+
+ object_refs = {}
+ last_selected_item = null
+ currently_selected_items = []
+
+ for item in item_container.get_children():
+ item_container.remove_child(item)
+ item.queue_free()
+
+
+func get_objects_from_nodes(items: Array) -> Array:
+ ## Converts a list of list Item nodes into the objects they are representing
+
+ var object_list: Array = []
+
+ for item in items:
+ object_list.append(object_refs[item])
+
+ return object_list
+
+
+func set_buttons_enabled(state: bool):
+
+ buttons_enabled = state
+
+ for node: Node in $ToolBarContainer/HBoxContainer.get_children():
+ if node is Button:
+ node.disabled = not buttons_enabled
+
+
+func set_selected(items: Array) -> void:
+ ## Sets the selected list items
+
+ currently_selected_items = []
+
+ for item in items:
+ if _is_valid_object(item):
+ if item_container.has_node(item.uuid):
+ currently_selected_items.append(item)
+
+ if currently_selected_items and not last_selected_item:
+ last_selected_item = item_container.get_node(currently_selected_items[-1].uuid)
+
+ _update_selected()
+
+
+func _on_list_item_select_request(selected_item: Control) -> void:
+ if Input.is_key_pressed(KEY_SHIFT) and last_selected_item and allow_multi_select:
+ var children: Array[Node] = item_container.get_children()
+ var pos_1: int = children.find(last_selected_item)
+ var pos_2: int = children.find(selected_item)
+
+ if pos_1 > pos_2:
+ var x = pos_1
+ pos_1 = pos_2
+ pos_2 = x
+
+ var items_to_select: Array = []
+
+ for i in range(pos_1, pos_2+1):
+ items_to_select.append(children[i])
+
+ selection_changed.emit(get_objects_from_nodes(items_to_select))
+
+ else:
+ last_selected_item = selected_item
+ selection_changed.emit(get_objects_from_nodes([selected_item]))
+
+
+
+func _update_selected(no_signal: bool = false) -> void:
+ for item: Control in item_container.get_children():
+ item.set_highlighted(false)
+
+ for item: Object in currently_selected_items:
+ item_container.get_node(item.uuid).set_highlighted(true)
+
+
+func _is_valid_object(object: Variant) -> bool:
+ ## Checks if an object is valid for use in an item list, object must have a uuid and name
+
+ if object is Object and "uuid" in object and "name" in object:
+ return true
+ else:
+ return false
+
+
+#region Button Callbacks
+
+func _on_new_pressed() -> void:
+ add_requested.emit()
+
+
+func _on_select_all_pressed() -> void:
+ if object_refs:
+ print("_on_select_all_pressed")
+ selection_changed.emit(get_objects_from_nodes( item_container.get_children()))
+
+
+func _on_select_none_pressed() -> void:
+ if object_refs:
+ selection_changed.emit([])
+
+
+func _on_select_invert_pressed() -> void:
+ if object_refs:
+ var all_items: Array = object_refs.values()
+
+ for item: Object in currently_selected_items:
+ all_items.erase(item)
+
+ selection_changed.emit(all_items)
+
+
+func _on_take_selection_pressed() -> void:
+ if currently_selected_items:
+ take_requested.emit(currently_selected_items)
+
+
+func _on_edit_pressed() -> void:
+ if currently_selected_items:
+ edit_requested.emit(currently_selected_items)
+
+
+func _on_delete_pressed() -> void:
+ if currently_selected_items:
+ delete_requested.emit(currently_selected_items)
+
+#endregion
+
+
+#region UI displaying functions
+func set_show_tool_bar(is_visible) -> void:
+ show_tool_bar = is_visible
+ if is_node_ready():
+ $ToolBarContainer.visible = is_visible
+
+
+func set_show_edit(is_visible) -> void:
+ show_edit = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/Edit.visible = is_visible
+
+
+func set_show_delete(is_visible) -> void:
+ show_delete = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/Delete.visible = is_visible
+
+
+func set_show_select(is_visible) -> void:
+ show_select = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/SelectAll.visible = is_visible
+ $ToolBarContainer/HBoxContainer/SelectNone.visible = is_visible
+
+
+func set_show_invert(is_visible) -> void:
+ show_invert = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/SelectInvert.visible = is_visible
+
+
+func set_show_new(is_visible) -> void:
+ show_new = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/New.visible = is_visible
+ $ToolBarContainer/HBoxContainer/VSeparator1.visible = is_visible
+
+
+func set_show_take(is_visible) -> void:
+ show_take = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/TakeSelection.visible = is_visible
+
+
+func set_show_separators(is_visible) -> void:
+ show_separators = is_visible
+ if is_node_ready():
+ $ToolBarContainer/HBoxContainer/VSeparator1.visible = is_visible
+ $ToolBarContainer/HBoxContainer/VSeparator2.visible = is_visible
+#endregion
diff --git a/Components/ItemListView/ItemListView.tscn b/Components/ItemListView/ItemListView.tscn
new file mode 100644
index 0000000..de429a1
--- /dev/null
+++ b/Components/ItemListView/ItemListView.tscn
@@ -0,0 +1,95 @@
+[gd_scene load_steps=10 format=3 uid="uid://c6smssfk7gn2w"]
+
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_fhj3g"]
+[ext_resource type="Script" path="res://Components/ItemListView/ItemListView.gd" id="2_xkuya"]
+[ext_resource type="Texture2D" uid="uid://bbennjke0ioen" path="res://Assets/Icons/Add.svg" id="3_kvafp"]
+[ext_resource type="Texture2D" uid="uid://c6x6e8xi6lupf" path="res://Assets/Icons/Checked.svg" id="4_21ueo"]
+[ext_resource type="Texture2D" uid="uid://su38vnysxa1c" path="res://Assets/Icons/Unchecked.svg" id="5_eti65"]
+[ext_resource type="Texture2D" uid="uid://b2nos4alwgqcg" path="res://Assets/Icons/Invert.svg" id="6_xwuqg"]
+[ext_resource type="Texture2D" uid="uid://bi4ttx5uklwl7" path="res://Assets/Icons/Check.svg" id="7_g74a1"]
+[ext_resource type="Texture2D" uid="uid://rqf8w11gk6ud" path="res://Assets/Icons/Edit.svg" id="8_y732o"]
+[ext_resource type="Texture2D" uid="uid://dfrrs2dnvlvsu" path="res://Assets/Icons/Delete.svg" id="9_dwy31"]
+
+[node name="ItemListView" type="VBoxContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme = ExtResource("1_fhj3g")
+script = ExtResource("2_xkuya")
+
+[node name="ToolBarContainer" type="PanelContainer" parent="."]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="ToolBarContainer"]
+layout_mode = 2
+
+[node name="New" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+text = "New"
+icon = ExtResource("3_kvafp")
+
+[node name="VSeparator1" type="VSeparator" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+
+[node name="SelectAll" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select All"
+text = "All"
+icon = ExtResource("4_21ueo")
+
+[node name="SelectNone" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select None"
+text = "None"
+icon = ExtResource("5_eti65")
+
+[node name="SelectInvert" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select None"
+text = "Invert"
+icon = ExtResource("6_xwuqg")
+
+[node name="TakeSelection" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select None"
+text = "Take"
+icon = ExtResource("7_g74a1")
+
+[node name="VSeparator2" type="VSeparator" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+
+[node name="Edit" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select None"
+text = "Edit"
+icon = ExtResource("8_y732o")
+
+[node name="Delete" type="Button" parent="ToolBarContainer/HBoxContainer"]
+layout_mode = 2
+tooltip_text = "Select All"
+text = "Delete"
+icon = ExtResource("9_dwy31")
+
+[node name="PanelContainer2" type="PanelContainer" parent="."]
+layout_mode = 2
+size_flags_vertical = 3
+
+[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer2"]
+layout_mode = 2
+
+[node name="ItemContainer" type="VBoxContainer" parent="PanelContainer2/ScrollContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/New" to="." method="_on_new_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/SelectAll" to="." method="_on_select_all_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/SelectNone" to="." method="_on_select_none_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/SelectInvert" to="." method="_on_select_invert_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/TakeSelection" to="." method="_on_take_selection_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/Edit" to="." method="_on_edit_pressed"]
+[connection signal="pressed" from="ToolBarContainer/HBoxContainer/Delete" to="." method="_on_delete_pressed"]
diff --git a/Components/Knob/Encoder.gd b/Components/Knob/Encoder.gd
index b63de31..db3db2f 100644
--- a/Components/Knob/Encoder.gd
+++ b/Components/Knob/Encoder.gd
@@ -54,9 +54,7 @@ func set_value_no_signal(value: int) -> int:
else:
rotation = clampi(remap(value, min_value, max_value, _gap_min, _gap_max), _gap_min, _gap_max)
new_value = clampi(value, min_value, max_value)
-
- print(rotation)
-
+
$TextureRect.rotation_degrees = rotation + rotation_offset
$Label.text = str(new_value)
diff --git a/Components/Knob/Encoder.tscn b/Components/Knob/Encoder.tscn
index 464f193..8d795aa 100644
--- a/Components/Knob/Encoder.tscn
+++ b/Components/Knob/Encoder.tscn
@@ -11,6 +11,7 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_5y8la")
+value = -930
[node name="TextureRect" type="TextureRect" parent="."]
layout_mode = 1
diff --git a/Components/List Item/List_item.gd b/Components/List Item/List_item.gd
index 5f76662..58c1ef0 100644
--- a/Components/List Item/List_item.gd
+++ b/Components/List Item/List_item.gd
@@ -1,4 +1,10 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
extends PanelContainer
+## GUI component for a list item
+
+signal select_requested(from: Control) ## Emmited when this control is clicked on
var control_node
@@ -27,17 +33,8 @@ func dissable_buttons(dissable):
$Container/Delete.disabled = dissable
$Container/Edit.disabled = dissable
-func _on_delete_pressed():
- if control_node.has_method("delete_request"):
- control_node.delete_request(self)
-
-func _on_edit_pressed():
- if control_node.has_method("edit_request"):
- control_node.edit_request(self)
-
func _on_gui_input(event):
if event is InputEventMouseButton:
if event.pressed == true and event.button_index == MOUSE_BUTTON_LEFT: # Check if the mouse button is released
- if control_node.has_method("on_selected"):
- control_node.on_selected(self)
+ select_requested.emit(self)
diff --git a/Components/List Item/List_item.tscn b/Components/List Item/List_item.tscn
index bf9eb21..7923037 100644
--- a/Components/List Item/List_item.tscn
+++ b/Components/List Item/List_item.tscn
@@ -1,8 +1,6 @@
-[gd_scene load_steps=5 format=3 uid="uid://d0wg6bo67u7cr"]
+[gd_scene load_steps=3 format=3 uid="uid://d0wg6bo67u7cr"]
[ext_resource type="Script" path="res://Components/List Item/List_item.gd" id="1_plvbh"]
-[ext_resource type="Texture2D" uid="uid://dfrrs2dnvlvsu" path="res://Assets/Icons/Delete.svg" id="2_jm4an"]
-[ext_resource type="Texture2D" uid="uid://rqf8w11gk6ud" path="res://Assets/Icons/Edit.svg" id="3_02fjg"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_564t2"]
content_margin_left = 13.0
@@ -10,7 +8,7 @@ content_margin_top = 5.0
content_margin_right = 5.0
content_margin_bottom = 5.0
bg_color = Color(0.0862745, 0.0862745, 0.0862745, 0.466667)
-border_width_left = 10
+border_width_left = 5
border_color = Color(1, 1, 1, 1)
corner_radius_top_left = 5
corner_radius_top_right = 5
@@ -18,6 +16,7 @@ corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[node name="Item" type="PanelContainer"]
+custom_minimum_size = Vector2(0, 50)
theme_override_styles/panel = SubResource("StyleBoxFlat_564t2")
script = ExtResource("1_plvbh")
@@ -31,15 +30,4 @@ layout_mode = 2
layout_mode = 2
text = "Name"
-[node name="Delete" type="Button" parent="Container"]
-layout_mode = 2
-size_flags_horizontal = 10
-icon = ExtResource("2_jm4an")
-
-[node name="Edit" type="Button" parent="Container"]
-layout_mode = 2
-icon = ExtResource("3_02fjg")
-
[connection signal="gui_input" from="." to="." method="_on_gui_input"]
-[connection signal="pressed" from="Container/Delete" to="." method="_on_delete_pressed"]
-[connection signal="pressed" from="Container/Edit" to="." method="_on_edit_pressed"]
diff --git a/Components/Trigger Button/TriggerButton.gd b/Components/Trigger Button/TriggerButton.gd
new file mode 100644
index 0000000..5c0fb11
--- /dev/null
+++ b/Components/Trigger Button/TriggerButton.gd
@@ -0,0 +1,20 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+extends Button
+## Ui button to trigger an action on click
+
+signal right_clicked(from: Button)
+
+
+func set_label_text(label_text: String) -> void:
+ ## Sets the text of this button,
+ ## use this instead of buttons built in set_text methord, as this label supports text wrapping
+
+ $Label.text = label_text
+
+
+func _on_gui_input(event: InputEvent) -> void:
+ if event is InputEventMouseButton:
+ if event.pressed and event.button_index == 2:
+ right_clicked.emit(self)
diff --git a/Components/Trigger Button/TriggerButton.tscn b/Components/Trigger Button/TriggerButton.tscn
new file mode 100644
index 0000000..1075592
--- /dev/null
+++ b/Components/Trigger Button/TriggerButton.tscn
@@ -0,0 +1,35 @@
+[gd_scene load_steps=3 format=3 uid="uid://c2c3t6jriymr6"]
+
+[ext_resource type="Script" path="res://Components/Trigger Button/TriggerButton.gd" id="1_3rl6v"]
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_v6l20"]
+
+[node name="TriggerButton" type="Button"]
+custom_minimum_size = Vector2(70, 70)
+anchors_preset = -1
+anchor_right = 0.0364583
+anchor_bottom = 0.0648148
+size_flags_horizontal = 3
+size_flags_vertical = 4
+theme = ExtResource("1_v6l20")
+theme_override_font_sizes/font_size = 9
+toggle_mode = true
+action_mode = 0
+clip_text = true
+script = ExtResource("1_3rl6v")
+metadata/_edit_use_anchors_ = true
+
+[node name="Label" type="Label" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_font_sizes/font_size = 14
+horizontal_alignment = 1
+vertical_alignment = 1
+autowrap_mode = 3
+text_overrun_behavior = 3
+
+[connection signal="gui_input" from="." to="." method="_on_gui_input"]
+[connection signal="toggled" from="." to="." method="_on_toggled"]
diff --git a/Components/Virtual Fixture/Virtual_fixture.gd b/Components/Virtual Fixture/Virtual_fixture.gd
index 36f7d96..842f3b1 100644
--- a/Components/Virtual Fixture/Virtual_fixture.gd
+++ b/Components/Virtual Fixture/Virtual_fixture.gd
@@ -1,15 +1,27 @@
extends GraphElement
-var control_node
+var fixture: Fixture
var color_override = false
var is_highlight = false
-# Called when the node enters the scene tree for the first time.
+var virtual_fixture_index: int = 0
+
func _ready():
$"Color Box".add_theme_stylebox_override("panel", $"Color Box".get_theme_stylebox("panel").duplicate())
-func set_color_rgb(color):
+func set_color(color):
$"Color Box".get_theme_stylebox("panel").bg_color = color
+func set_fixture(control_fixture: Fixture) -> void:
+ ## Sets the fixture this virtual fixture is atached to
+
+ if is_instance_valid(fixture):
+ fixture.color_changed.disconnect(self.set_color)
+
+ fixture = control_fixture
+ fixture.color_changed.connect(self.set_color)
+ fixture.selected.connect(self.set_highlighted)
+ fixture.delete_request.connect(self.delete)
+
func serialize():
return {
"position_offset":{
@@ -26,7 +38,8 @@ func set_highlighted(highlight):
else:
$"Color Box".get_theme_stylebox("panel").border_color = Color.BLACK
-func delete():
+func delete(_fixture = null):
+ self.get_parent()._selected_virtual_fixtures.erase(self)
self.queue_free()
@@ -38,3 +51,7 @@ func _on_node_deselected():
color_override = false
if is_highlight:set_highlighted(true)
else:$"Color Box".get_theme_stylebox("panel").border_color = Color.BLACK
+
+
+func _on_dragged(from: Vector2, to: Vector2) -> void:
+ fixture.user_meta.virtual_fixtures[virtual_fixture_index] = to
diff --git a/Components/Virtual Fixture/Virtual_fixture.tscn b/Components/Virtual Fixture/Virtual_fixture.tscn
index e18e11f..7dd2951 100644
--- a/Components/Virtual Fixture/Virtual_fixture.tscn
+++ b/Components/Virtual Fixture/Virtual_fixture.tscn
@@ -27,5 +27,6 @@ layout_mode = 2
mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_0nr4c")
+[connection signal="dragged" from="." to="." method="_on_dragged"]
[connection signal="node_deselected" from="." to="." method="_on_node_deselected"]
[connection signal="node_selected" from="." to="." method="_on_node_selected"]
diff --git a/Fixtures/generic/desk-channel.json b/Fixtures/generic/desk-channel.json
deleted file mode 100644
index 704846e..0000000
--- a/Fixtures/generic/desk-channel.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "schema_version": "1.0",
- "info": {
- "brand": "Generic",
- "name": "Desk Channel",
- "date": "21-07-2018",
- "author": ["Flo Edelmann", "Liam Sherwin"],
- "categories": ["Dimmer"],
- "links": {
- "Website": "https://open-fixture-library.org/generic/desk-channel",
- "OFLLibrary": "https://open-fixture-library.org/generic/desk-channel"
- }
- },
- "modes": {
- "1 Channel": {
- "channels": ["Intensity"]
- },
- "2 Channel": {
- "channels": ["Intensity", "IntensityFine"]
- },
- "3 Channel": {
- "channels": ["Intensity", "IntensityFine", "IntensityFine2"]
- }
- }
-}
diff --git a/IO Plugins/DataIOPlugin.gd b/IO Plugins/DataIOPlugin.gd
deleted file mode 100644
index c384e06..0000000
--- a/IO Plugins/DataIOPlugin.gd
+++ /dev/null
@@ -1,31 +0,0 @@
-extends Object
-class_name DataIOPlugin
-
-var type: String = ""
-var uuid: String = ""
-var name: String = ""
-
-func set_type(new_type:String) -> void:
- type = new_type
- print(self, " Setting Type ", new_type, type)
-
-func get_type() -> String:
- return type
-
-func set_uuid(new_uuid:String) -> void:
- uuid = new_uuid
-
-func get_uuid() -> String:
- return uuid
-
-func set_name(new_name: String) -> void:
- name = new_name
-
-func get_name() -> String:
- return name
-
-func send_packet(packet) -> void:
- return
-
-func delete() -> void:
- return
diff --git a/IO Plugins/Input Plugins/Empty Input.gd b/IO Plugins/Input Plugins/Empty Input.gd
deleted file mode 100644
index 1eb83c0..0000000
--- a/IO Plugins/Input Plugins/Empty Input.gd
+++ /dev/null
@@ -1,9 +0,0 @@
-extends DataIOPlugin
-class_name EmptyInput
-
-var exposed_values = []
-
-func _init():
- self.set_type("input")
- self.set_name("Empty Input")
-
diff --git a/IO Plugins/Output Plugins/Empty Output.gd b/IO Plugins/Output Plugins/Empty Output.gd
deleted file mode 100644
index 6374537..0000000
--- a/IO Plugins/Output Plugins/Empty Output.gd
+++ /dev/null
@@ -1,8 +0,0 @@
-extends DataIOPlugin
-class_name EmptyOutput
-
-var exposed_values = []
-
-func _init():
- self.set_type("output")
- self.set_name("Empty Output")
diff --git a/Main.tscn b/Main.tscn
index 9539630..81e8573 100644
--- a/Main.tscn
+++ b/Main.tscn
@@ -1,16 +1,14 @@
-[gd_scene load_steps=26 format=3 uid="uid://p3sohjs1pt37"]
+[gd_scene load_steps=23 format=3 uid="uid://p3sohjs1pt37"]
[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_lygr6"]
+[ext_resource type="PackedScene" uid="uid://cim02rtl7kfpc" path="res://Panels/IO Controls/IOControls.tscn" id="3_k6wx5"]
[ext_resource type="Script" path="res://Scripts/fps.gd" id="3_l3bnx"]
-[ext_resource type="Script" path="res://Scripts/Tab_container.gd" id="3_nk5d5"]
-[ext_resource type="Script" path="res://Scripts/System.gd" id="5_qili6"]
-[ext_resource type="PackedScene" uid="uid://dhor5xf4xd2ul" path="res://Panels/Desk/Desk.tscn" id="8_12iq0"]
-[ext_resource type="PackedScene" uid="uid://cdg8rr3v7el85" path="res://Panels/Patch Bay/Patch_bay.tscn" id="9_hxqqf"]
-[ext_resource type="PackedScene" uid="uid://pe1tet2410en" path="res://Panels/Fixtures/Fixtures.tscn" id="10_e6e26"]
-[ext_resource type="Script" path="res://Panels/Popups/Popup_window.gd" id="10_yavq3"]
-[ext_resource type="PackedScene" uid="uid://dhrijn1m88172" path="res://Panels/Virtual Fixtures/Virtual_fixtures.tscn" id="11_7alyx"]
-[ext_resource type="Texture2D" uid="uid://bhl73t2e0jcwn" path="res://Assets/Icons/close.svg" id="11_mn7am"]
-[ext_resource type="Texture2D" uid="uid://vw0vs7dlct55" path="res://Assets/Icons/menu.svg" id="24_oc2jn"]
+[ext_resource type="PackedScene" uid="uid://d12eb0smjpwuk" path="res://Panels/Fixtures/Fixtures.tscn" id="3_u55oi"]
+[ext_resource type="PackedScene" uid="uid://03t7su6t2rq2" path="res://Panels/Universes/Universes.tscn" id="4_8kgbc"]
+[ext_resource type="PackedScene" uid="uid://cedqo06n6en8u" path="res://Panels/Functions/Functions.tscn" id="4_35sxj"]
+[ext_resource type="PackedScene" uid="uid://dhrijn1m88172" path="res://Panels/Virtual Fixtures/Virtual_fixtures.tscn" id="5_8370q"]
+[ext_resource type="PackedScene" uid="uid://c3lrdtiw05qft" path="res://Panels/Add Fixture/Add_fixture.tscn" id="6_wm8dh"]
+[ext_resource type="Script" path="res://Scripts/Menu_buttons.gd" id="7_gussm"]
[ext_resource type="PackedScene" uid="uid://nd4u83us3lgo" path="res://Panels/Settings/Settings.tscn" id="27_03pyd"]
[ext_resource type="Script" path="res://Scripts/Window.gd" id="27_nmwy1"]
@@ -75,13 +73,6 @@ unicode = 112
[sub_resource type="Shortcut" id="Shortcut_sn8ws"]
events = [SubResource("InputEventKey_7uieo")]
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5sp73"]
-content_margin_left = 5.0
-content_margin_top = 5.0
-content_margin_right = 5.0
-content_margin_bottom = 5.0
-bg_color = Color(0.12549, 0.12549, 0.12549, 0)
-
[node name="Main" type="Control"]
layout_mode = 3
anchors_preset = 15
@@ -90,7 +81,6 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_lygr6")
-script = ExtResource("5_qili6")
[node name="ColorRect" type="ColorRect" parent="."]
layout_mode = 1
@@ -108,35 +98,36 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
-current_tab = 3
drag_to_rearrange_enabled = true
use_hidden_tabs_for_min_size = true
-script = ExtResource("3_nk5d5")
-[node name="Fixtures" parent="TabContainer" instance=ExtResource("10_e6e26")]
-visible = false
+[node name="Universes" type="HSplitContainer" parent="TabContainer"]
layout_mode = 2
-[node name="Desk" parent="TabContainer" instance=ExtResource("8_12iq0")]
+[node name="Universes" parent="TabContainer/Universes" instance=ExtResource("4_8kgbc")]
+layout_mode = 2
+
+[node name="IO Controls" parent="TabContainer/Universes" instance=ExtResource("3_k6wx5")]
+layout_mode = 2
+
+[node name="Functions" parent="TabContainer" instance=ExtResource("4_35sxj")]
visible = false
layout_mode = 2
-[node name="Patch Bay" parent="TabContainer" instance=ExtResource("9_hxqqf")]
+[node name="Fixtures" type="HSplitContainer" parent="TabContainer"]
visible = false
layout_mode = 2
-[node name="Virtual Fixtures" parent="TabContainer" instance=ExtResource("11_7alyx")]
+[node name="Fixtures" parent="TabContainer/Fixtures" instance=ExtResource("3_u55oi")]
layout_mode = 2
+size_flags_horizontal = 3
-[node name="Open Drop Down" type="Button" parent="."]
+[node name="Add Fixture" parent="TabContainer/Fixtures" instance=ExtResource("6_wm8dh")]
+layout_mode = 2
+
+[node name="Virtual Fixtures" parent="TabContainer" instance=ExtResource("5_8370q")]
+visible = false
layout_mode = 2
-offset_left = 3.0
-offset_top = 3.0
-offset_right = 44.0
-offset_bottom = 42.0
-size_flags_horizontal = 2
-icon = ExtResource("24_oc2jn")
-icon_alignment = 1
[node name="Menu Buttons" type="HBoxContainer" parent="."]
layout_mode = 1
@@ -147,6 +138,7 @@ offset_left = -495.915
offset_right = -3.91504
offset_bottom = 45.0
grow_horizontal = 0
+script = ExtResource("7_gussm")
[node name="Label" type="Label" parent="Menu Buttons"]
custom_minimum_size = Vector2(76, 0)
@@ -219,60 +211,6 @@ color = Color(0.0666667, 0.0666667, 0.0666667, 1)
[node name="Settings" parent="Settings" instance=ExtResource("27_03pyd")]
-[node name="Popups" type="Window" parent="."]
-title = "Warnings"
-initial_position = 5
-size = Vector2i(570, 330)
-visible = false
-script = ExtResource("10_yavq3")
-
-[node name="VBoxContainer" type="PanelContainer" parent="Popups"]
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-
-[node name="PanelContainer" type="VBoxContainer" parent="Popups/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Popups/VBoxContainer/PanelContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-theme_override_styles/panel = SubResource("StyleBoxFlat_5sp73")
-
-[node name="Content" type="VBoxContainer" parent="Popups/VBoxContainer/PanelContainer/ScrollContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="HSeparator" type="HSeparator" parent="Popups/VBoxContainer/PanelContainer"]
-layout_mode = 2
-
-[node name="Close" type="Button" parent="Popups/VBoxContainer/PanelContainer"]
-layout_mode = 2
-size_flags_horizontal = 4
-size_flags_vertical = 8
-text = "Close"
-icon = ExtResource("11_mn7am")
-
-[node name="Load File Dialog" type="FileDialog" parent="."]
-title = "Open a File"
-initial_position = 5
-size = Vector2i(532, 439)
-ok_button_text = "Open"
-dialog_hide_on_ok = true
-file_mode = 0
-access = 2
-use_native_dialog = true
-
-[node name="Save File Dialog" type="FileDialog" parent="."]
-initial_position = 5
-size = Vector2i(742, 659)
-access = 2
-use_native_dialog = true
-
[node name="Debug Text" type="Label" parent="."]
layout_mode = 1
anchors_preset = 3
@@ -292,12 +230,7 @@ text = "Beta Version 0.4.0
[connection signal="tab_button_pressed" from="TabContainer" to="TabContainer" method="_on_tab_button_pressed"]
[connection signal="tab_clicked" from="TabContainer" to="TabContainer" method="_on_tab_clicked"]
-[connection signal="toggled" from="Menu Buttons/Edit Mode" to="." method="_on_edit_mode_toggled"]
-[connection signal="pressed" from="Menu Buttons/Save" to="." method="_on_save_pressed"]
-[connection signal="pressed" from="Menu Buttons/Load" to="." method="_on_load_pressed"]
+[connection signal="pressed" from="Menu Buttons/Save" to="Menu Buttons" method="_on_save_pressed"]
+[connection signal="pressed" from="Menu Buttons/Load" to="Menu Buttons" method="_on_load_pressed"]
[connection signal="pressed" from="Menu Buttons/Settings" to="Settings" method="show"]
[connection signal="close_requested" from="Settings" to="Settings" method="_on_close_requested"]
-[connection signal="close_requested" from="Popups" to="Popups" method="_on_close_requested"]
-[connection signal="pressed" from="Popups/VBoxContainer/PanelContainer/Close" to="Popups" method="_on_close_pressed"]
-[connection signal="file_selected" from="Load File Dialog" to="." method="_on_load_file_dialog_file_selected"]
-[connection signal="file_selected" from="Save File Dialog" to="." method="_on_save_file_dialog_file_selected"]
diff --git a/Panels/Add Fixture/Add_fixture.gd b/Panels/Add Fixture/Add_fixture.gd
index f4d0f8d..c97eede 100644
--- a/Panels/Add Fixture/Add_fixture.gd
+++ b/Panels/Add Fixture/Add_fixture.gd
@@ -24,7 +24,8 @@ var options: Dictionary = {
func _ready() -> void:
- Globals.subscribe("reload_universes", self._reload_universes)
+ Core.universes_added.connect(self._reload_universes)
+ Core.universes_removed.connect(self._reload_universes)
_reload_universes()
_reload_fixture_tree()
@@ -38,14 +39,14 @@ func _reload_fixture_tree() -> void:
var root: TreeItem = tree.create_item()
tree.hide_root = true
- for manufacturer: String in Globals.fixtures.keys():
+ for manufacturer: String in Core.fixtures_definitions.keys():
var manufacturer_item: TreeItem = tree.create_item(root)
manufacturer_item.set_text(0, manufacturer)
manufacturer_item.collapsed = true
- for fixture: String in Globals.fixtures[manufacturer].keys():
+ for fixture: String in Core.fixtures_definitions[manufacturer].keys():
var fixture_item: TreeItem = tree.create_item(manufacturer_item)
- fixture_item.set_text(0, Globals.fixtures[manufacturer][fixture].info.name)
+ fixture_item.set_text(0, Core.fixtures_definitions[manufacturer][fixture].info.name)
func _reload_menu() -> void:
@@ -66,13 +67,13 @@ func _reload_menu() -> void:
self.get_node(fixture_channel_list).add_item(channel)
-func _reload_universes() -> void:
+func _reload_universes(_universes=null) -> void:
## Reload the list of universes
self.get_node(fixture_universe_option).clear()
- for universe: Universe in Globals.universes.values():
- self.get_node(fixture_universe_option).add_item(universe.get_universe_name())
+ for universe: Universe in Core.universes.values():
+ self.get_node(fixture_universe_option).add_item(universe.name)
func _on_fixture_tree_item_selected() -> void:
@@ -86,7 +87,7 @@ func _on_fixture_tree_item_selected() -> void:
var manufacturer: String = selected.get_parent().get_text(0)
var fixture: String = selected.get_text(0)
- current_fixture = Globals.fixtures[manufacturer][fixture]
+ current_fixture = Core.fixtures_definitions[manufacturer][fixture]
options.mode = 0
_reload_menu()
@@ -101,7 +102,13 @@ func _on_add_fixture_button_pressed() -> void:
if self.get_node(fixture_universe_option).selected < 0:
return
- Globals.universes.values()[self.get_node(fixture_universe_option).selected].new_fixture(current_fixture, options)
+ Core.universes.values()[self.get_node(fixture_universe_option).selected].new_fixture(
+ current_fixture,
+ options.mode,
+ options.channel,
+ options.quantity,
+ options.offset
+ )
func _on_quantity_value_changed(value: int) -> void:
diff --git a/Panels/Add Fixture/Add_fixture.tscn b/Panels/Add Fixture/Add_fixture.tscn
index ae28f2c..d0670db 100644
--- a/Panels/Add Fixture/Add_fixture.tscn
+++ b/Panels/Add Fixture/Add_fixture.tscn
@@ -3,16 +3,7 @@
[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_460tm"]
[ext_resource type="Script" path="res://Panels/Add Fixture/Add_fixture.gd" id="2_q8wav"]
-[node name="Add Fixture" type="Control"]
-layout_mode = 3
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-
-[node name="Add Fixture" type="PanelContainer" parent="."]
-layout_mode = 1
+[node name="Add Fixture" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
@@ -22,72 +13,65 @@ size_flags_horizontal = 3
size_flags_vertical = 3
theme = ExtResource("1_460tm")
script = ExtResource("2_q8wav")
-fixture_tree = NodePath("MarginContainer/HSplitContainer/Fixture Tree")
-fixture_channel_list = NodePath("MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/Channel List")
-fixture_modes_option = NodePath("MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4/Modes")
-fixture_universe_option = NodePath("MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3/Fixture Universe Option")
-add_fixture_button = NodePath("MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2/Add Fixture Button")
-
-[node name="MarginContainer" type="MarginContainer" parent="Add Fixture"]
-layout_mode = 2
-theme_override_constants/margin_left = 10
-theme_override_constants/margin_top = 10
-theme_override_constants/margin_right = 10
-theme_override_constants/margin_bottom = 10
+fixture_tree = NodePath("HSplitContainer/Fixture Tree")
+fixture_channel_list = NodePath("HSplitContainer/PanelContainer/VBoxContainer/Channel List")
+fixture_modes_option = NodePath("HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4/Modes")
+fixture_universe_option = NodePath("HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3/Fixture Universe Option")
+add_fixture_button = NodePath("HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2/Add Fixture Button")
-[node name="HSplitContainer" type="HSplitContainer" parent="Add Fixture/MarginContainer"]
+[node name="HSplitContainer" type="HSplitContainer" parent="."]
layout_mode = 2
split_offset = 100
-[node name="Fixture Tree" type="Tree" parent="Add Fixture/MarginContainer/HSplitContainer"]
+[node name="Fixture Tree" type="Tree" parent="HSplitContainer"]
layout_mode = 2
size_flags_horizontal = 3
-[node name="PanelContainer" type="PanelContainer" parent="Add Fixture/MarginContainer/HSplitContainer"]
+[node name="PanelContainer" type="PanelContainer" parent="HSplitContainer"]
layout_mode = 2
size_flags_horizontal = 3
-[node name="VBoxContainer" type="VBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/PanelContainer"]
layout_mode = 2
-[node name="HBoxContainer" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
-[node name="Label" type="Label" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer"]
+[node name="Label" type="Label" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Channel"
-[node name="Channel" type="SpinBox" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer"]
+[node name="Channel" type="SpinBox" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 10
min_value = 1.0
max_value = 512.0
value = 1.0
-[node name="HBoxContainer3" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer3" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
-[node name="Label" type="Label" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3"]
+[node name="Label" type="Label" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3"]
layout_mode = 2
text = "Universe"
-[node name="Fixture Universe Option" type="OptionButton" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3"]
+[node name="Fixture Universe Option" type="OptionButton" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3"]
custom_minimum_size = Vector2(200, 40)
layout_mode = 2
size_flags_horizontal = 10
-[node name="HSeparator" type="HSeparator" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HSeparator" type="HSeparator" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
-[node name="HBoxContainer4" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer4" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
-[node name="Label" type="Label" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4"]
+[node name="Label" type="Label" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4"]
layout_mode = 2
text = "Mode
"
-[node name="Modes" type="OptionButton" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4"]
+[node name="Modes" type="OptionButton" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
size_flags_horizontal = 10
@@ -97,50 +81,50 @@ fit_to_longest_item = false
popup/item_0/text = " "
popup/item_0/id = 0
-[node name="Channel List" type="ItemList" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="Channel List" type="ItemList" parent="HSplitContainer/PanelContainer/VBoxContainer"]
custom_minimum_size = Vector2(0, 200)
layout_mode = 2
allow_search = false
-[node name="HBoxContainer5" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer5" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 10
-[node name="Label" type="Label" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5"]
+[node name="Label" type="Label" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5"]
layout_mode = 2
text = "Quantity"
-[node name="Quantity" type="SpinBox" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5"]
+[node name="Quantity" type="SpinBox" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5"]
layout_mode = 2
size_flags_horizontal = 10
min_value = 1.0
max_value = 512.0
value = 1.0
-[node name="HBoxContainer6" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer6" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
-[node name="Label" type="Label" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6"]
+[node name="Label" type="Label" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6"]
layout_mode = 2
text = "Offset"
-[node name="Offset" type="SpinBox" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6"]
+[node name="Offset" type="SpinBox" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6"]
layout_mode = 2
size_flags_horizontal = 10
-[node name="HBoxContainer2" type="HBoxContainer" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer"]
+[node name="HBoxContainer2" type="HBoxContainer" parent="HSplitContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
-[node name="Add Fixture Button" type="Button" parent="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2"]
+[node name="Add Fixture Button" type="Button" parent="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
size_flags_horizontal = 3
text = "Add Fixture"
-[connection signal="item_selected" from="Add Fixture/MarginContainer/HSplitContainer/Fixture Tree" to="Add Fixture" method="_on_fixture_tree_item_selected"]
-[connection signal="value_changed" from="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer/Channel" to="Add Fixture" method="_on_channel_value_changed"]
-[connection signal="item_selected" from="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4/Modes" to="Add Fixture" method="_on_modes_item_selected"]
-[connection signal="value_changed" from="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5/Quantity" to="Add Fixture" method="_on_quantity_value_changed"]
-[connection signal="value_changed" from="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6/Offset" to="Add Fixture" method="_on_offset_value_changed"]
-[connection signal="pressed" from="Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2/Add Fixture Button" to="Add Fixture" method="_on_add_fixture_button_pressed"]
+[connection signal="item_selected" from="HSplitContainer/Fixture Tree" to="." method="_on_fixture_tree_item_selected"]
+[connection signal="value_changed" from="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer/Channel" to="." method="_on_channel_value_changed"]
+[connection signal="item_selected" from="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4/Modes" to="." method="_on_modes_item_selected"]
+[connection signal="value_changed" from="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer5/Quantity" to="." method="_on_quantity_value_changed"]
+[connection signal="value_changed" from="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer6/Offset" to="." method="_on_offset_value_changed"]
+[connection signal="pressed" from="HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2/Add Fixture Button" to="." method="_on_add_fixture_button_pressed"]
diff --git a/Panels/Fixtures/Fixtures.gd b/Panels/Fixtures/Fixtures.gd
index d4bc309..8629c20 100644
--- a/Panels/Fixtures/Fixtures.gd
+++ b/Panels/Fixtures/Fixtures.gd
@@ -2,110 +2,38 @@
# All rights reserved.
extends Control
+## GUI element for managing fixtures
-@export var physical_fixture_list: NodePath
-@export var fixture_groups_list: NodePath
+@export var item_list_view: NodePath
-var active_fixtures : Array = []
-var last_selected_node: Control
func _ready() -> void:
- Globals.subscribe("reload_fixtures", self.reload_fixtures)
- Globals.subscribe("active_fixtures", self.set_active_fixtures)
+ Core.fixture_added.connect(self._reload_fixtures)
+ Core.fixture_removed.connect(self._reload_fixtures)
+ Core.universes_added.connect(self._reload_fixtures)
+ Core.universes_removed.connect(self._reload_fixtures)
+ Core.fixture_selection_changed.connect(self._reload_fixtures)
-func delete_request(node:Control) -> void :
- ## Called when the delete button is clicked on a fixture List_Item
+func _reload_fixtures(_fixture=null) -> void:
+ ## Reload the list of fixtures
- var confirmation_dialog: AcceptDialog = Globals.components.accept_dialog.instantiate()
- confirmation_dialog.dialog_text = "Are you sure you want to delete this? This action can not be undone"
+ self.get_node(item_list_view).remove_all()
+ self.get_node(item_list_view).add_items(Core.fixtures.values())
- confirmation_dialog.confirmed.connect((
- func(node):
-
- var fixture: Fixture = node.get_meta("fixture")
- fixture.config.universe.remove_fixture(fixture)
-
- Globals.call_subscription("reload_fixtures")
- ).bind(node))
-
- add_child(confirmation_dialog)
-
-
-func edit_request(node:Control) -> void:
- ## WIP function to edit a fixture, change channel, type, universe, ect
- pass
-
-
-func on_selected(selected_node:Control) -> void:
- ## Called when the user clicks on a fixture List_Item
-
- if Input.is_key_pressed(KEY_SHIFT) and last_selected_node:
- var children: Array[Node] = self.get_node(physical_fixture_list).get_children()
- var pos_1: int = children.find(last_selected_node)
- var pos_2: int = children.find(selected_node)
-
- if pos_1 > pos_2:
- var x = pos_1
- pos_1 = pos_2
- pos_2 = x
-
- var fixtures_to_select: Array = []
-
- for i in range(pos_1, pos_2+1):
- fixtures_to_select.append(children[i].get_meta("fixture"))
-
- Globals.set_value("active_fixtures", fixtures_to_select)
-
- else:
- last_selected_node = selected_node
- Globals.set_value("active_fixtures", [selected_node.get_meta("fixture")])
-func reload_fixtures() -> void:
- for node in get_node(physical_fixture_list).get_children():
- node.get_parent().remove_child(node)
- node.queue_free()
+func _on_item_list_view_delete_requested(items: Array) -> void:
+ ## Called when the delete button is pressed on the ItemListView
- for universe in Globals.universes.values():
- for fixture in universe.get_fixtures().values():
- var node_to_add : Control = Globals.components.list_item.instantiate()
- node_to_add.control_node = self
- node_to_add.set_item_name(fixture.config.fixture_name + " | " + universe.get_universe_name() + " CH: " + str(fixture.config.channel) + "-" + str(fixture.config.channel+fixture.config.length-1))
- node_to_add.name = fixture.config.uuid
- node_to_add.set_meta("fixture", fixture)
- get_node(physical_fixture_list).add_child(node_to_add)
-
- set_active_fixtures(active_fixtures)
+ for fixture: Fixture in items:
+ fixture.universe.remove_fixture(fixture)
-func set_active_fixtures(fixtures:Array) -> void:
- for fixture in get_node(physical_fixture_list).get_children():
- fixture.set_highlighted(false)
- active_fixtures = fixtures
-
- for fixture in active_fixtures:
- get_node(physical_fixture_list).get_node(fixture.config.uuid).set_highlighted(true)
- pass
-
-func _on_new_physical_fixture_pressed() -> void:
+func _on_item_list_view_add_requested() -> void:
Globals.open_panel_in_window("add_fixture")
-func _on_new_fixture_group_pressed() -> void:
- pass
-
-
-func _on_select_all_pressed() -> void:
-
- var fixtures_to_select: Array = []
-
- for list_item: Control in get_node(physical_fixture_list).get_children():
- fixtures_to_select.append(list_item.get_meta("fixture"))
-
- Globals.set_value("active_fixtures", fixtures_to_select)
-
-
-func _on_select_none_pressed() -> void:
- Globals.set_value("active_fixtures", [])
+func _on_item_list_view_selection_changed(items: Array) -> void:
+ Core.set_fixture_selection(items)
diff --git a/Panels/Fixtures/Fixtures.tscn b/Panels/Fixtures/Fixtures.tscn
index 19ccbdb..13c5b38 100644
--- a/Panels/Fixtures/Fixtures.tscn
+++ b/Panels/Fixtures/Fixtures.tscn
@@ -1,138 +1,21 @@
-[gd_scene load_steps=11 format=3 uid="uid://pe1tet2410en"]
+[gd_scene load_steps=3 format=3 uid="uid://d12eb0smjpwuk"]
-[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_g8n3l"]
[ext_resource type="Script" path="res://Panels/Fixtures/Fixtures.gd" id="1_xjo7n"]
-[ext_resource type="Texture2D" uid="uid://dis005hfepinf" path="res://Assets/Icons/Fixture.svg" id="2_tonwl"]
-[ext_resource type="Texture2D" uid="uid://ds5mmj3a0ukl4" path="res://Assets/Icons/Fixture_group.svg" id="3_dj1r6"]
-[ext_resource type="Texture2D" uid="uid://cwv0ml7wy07au" path="res://Assets/Icons/Checklist.svg" id="5_7ecew"]
-[ext_resource type="Texture2D" uid="uid://su38vnysxa1c" path="res://Assets/Icons/Unchecked.svg" id="6_c3kim"]
+[ext_resource type="PackedScene" uid="uid://c6smssfk7gn2w" path="res://Components/ItemListView/ItemListView.tscn" id="2_o0s47"]
-[sub_resource type="InputEventKey" id="InputEventKey_6ywj7"]
-device = -1
-ctrl_pressed = true
-keycode = 65
-unicode = 97
-
-[sub_resource type="Shortcut" id="Shortcut_svqby"]
-events = [SubResource("InputEventKey_6ywj7")]
-
-[sub_resource type="InputEventKey" id="InputEventKey_jvtdl"]
-device = -1
-shift_pressed = true
-ctrl_pressed = true
-keycode = 65
-unicode = 65
-
-[sub_resource type="Shortcut" id="Shortcut_vy8ba"]
-events = [SubResource("InputEventKey_jvtdl")]
-
-[node name="Fixtures" type="Control"]
-layout_mode = 3
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-theme = ExtResource("1_g8n3l")
-
-[node name="Fixtures" type="MarginContainer" parent="."]
-layout_mode = 1
+[node name="Fixtures" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
-theme_override_constants/margin_left = 10
-theme_override_constants/margin_top = 10
-theme_override_constants/margin_right = 10
-theme_override_constants/margin_bottom = 10
script = ExtResource("1_xjo7n")
-physical_fixture_list = NodePath("VBoxContainer/VSplitContainer/PanelContainer3/ScrollContainer/Physical Fixtures")
-fixture_groups_list = NodePath("VBoxContainer/VSplitContainer/PanelContainer2/ScrollContainer/Fixture Groups")
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Fixtures"]
-layout_mode = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="Fixtures/VBoxContainer"]
-layout_mode = 2
-theme_override_constants/margin_bottom = 5
-
-[node name="PanelContainer" type="PanelContainer" parent="Fixtures/VBoxContainer/MarginContainer"]
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="Fixtures/VBoxContainer/MarginContainer/PanelContainer"]
-layout_mode = 2
-
-[node name="New Physical Fixture" type="Button" parent="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-tooltip_text = "Open fixture creation window"
-text = "New Fixture"
-icon = ExtResource("2_tonwl")
-
-[node name="New Fixture Group" type="Button" parent="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-tooltip_text = "Work in progress, Create a new fixture group"
-text = "New Fixture Group"
-icon = ExtResource("3_dj1r6")
-
-[node name="Select All" type="Button" parent="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-tooltip_text = "Select All"
-shortcut = SubResource("Shortcut_svqby")
-icon = ExtResource("5_7ecew")
-
-[node name="Select None" type="Button" parent="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-tooltip_text = "Select All"
-shortcut = SubResource("Shortcut_vy8ba")
-icon = ExtResource("6_c3kim")
-
-[node name="VSplitContainer" type="VSplitContainer" parent="Fixtures/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-
-[node name="PanelContainer3" type="PanelContainer" parent="Fixtures/VBoxContainer/VSplitContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Fixtures/VBoxContainer/VSplitContainer/PanelContainer3"]
-layout_mode = 2
-size_flags_vertical = 3
+item_list_view = NodePath("ItemListView")
-[node name="Physical Fixtures" type="VBoxContainer" parent="Fixtures/VBoxContainer/VSplitContainer/PanelContainer3/ScrollContainer"]
+[node name="ItemListView" parent="." instance=ExtResource("2_o0s47")]
layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="PanelContainer2" type="PanelContainer" parent="Fixtures/VBoxContainer/VSplitContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Fixtures/VBoxContainer/VSplitContainer/PanelContainer2"]
-layout_mode = 2
-size_flags_vertical = 3
-
-[node name="Fixture Groups" type="VBoxContainer" parent="Fixtures/VBoxContainer/VSplitContainer/PanelContainer2/ScrollContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="Panel" type="Panel" parent="Fixtures"]
-visible = false
-layout_mode = 2
-
-[node name="Label" type="Label" parent="Fixtures/Panel"]
-layout_mode = 1
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-text = "Fixtures are a work in progress and will be available in a future version of Spectrum."
-horizontal_alignment = 1
-vertical_alignment = 1
+show_new = false
-[connection signal="pressed" from="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/New Physical Fixture" to="Fixtures" method="_on_new_physical_fixture_pressed"]
-[connection signal="pressed" from="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/Select All" to="Fixtures" method="_on_select_all_pressed"]
-[connection signal="pressed" from="Fixtures/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/Select None" to="Fixtures" method="_on_select_none_pressed"]
+[connection signal="add_requested" from="ItemListView" to="." method="_on_item_list_view_add_requested"]
+[connection signal="delete_requested" from="ItemListView" to="." method="_on_item_list_view_delete_requested"]
+[connection signal="selection_changed" from="ItemListView" to="." method="_on_item_list_view_selection_changed"]
diff --git a/Panels/Functions/Functions.gd b/Panels/Functions/Functions.gd
index 3c154dc..8bde6e6 100644
--- a/Panels/Functions/Functions.gd
+++ b/Panels/Functions/Functions.gd
@@ -1,53 +1,31 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
extends Control
+## GUI element for managing functions
+
+@export var item_list_view: NodePath
+
+
+func _ready() -> void:
+ Core.scenes_added.connect(self._reload_functions)
+ Core.scenes_removed.connect(self._reload_functions)
-var functions = {
- "scenes":{},
- "effects":{},
- "cues":{},
-}
-
-func _ready():
- Globals.subscribe("edit_mode", self.on_edit_mode_changed)
-
-func delete_request(node):
- node.queue_free()
-
-func edit_request(node):
- pass
-
-func on_edit_mode_changed(edit_mode):
- for function_item in Globals.nodes.scenes_list.get_children():
- function_item.dissable_buttons(not edit_mode)
-
- for function_item in Globals.nodes.effects_list.get_children():
- function_item.dissable_buttons(not edit_mode)
-
- for function_item in Globals.nodes.cues_list.get_children():
- function_item.dissable_buttons(not edit_mode)
-
-func new_scene():
- var node_to_add = Globals.components.list_item.instantiate()
- node_to_add.set_item_name("Scene")
- node_to_add.control_node = self
- Globals.nodes.scenes_list.add_child(node_to_add)
+
+func _reload_functions(_scene=null) -> void:
+ ## Reload the list of fixtures
+
+ self.get_node(item_list_view).remove_all()
+ self.get_node(item_list_view).add_items(Core.scenes.values())
-func new_effect():
- var node_to_add = Globals.components.list_item.instantiate()
- node_to_add.set_item_name("Effect")
- node_to_add.control_node = self
- Globals.nodes.effects_list.add_child(node_to_add)
+
+
+func _on_item_list_view_delete_requested(items: Array) -> void:
+ ## Called when the delete button is pressed on the ItemListView
-func new_cue():
- var node_to_add = Globals.components.list_item.instantiate()
- node_to_add.set_item_name("Cue")
- node_to_add.control_node = self
- Globals.nodes.cues_list.add_child(node_to_add)
+ Core.remove_scenes(items)
-func _on_new_scene_pressed():
- new_scene()
-func _on_new_effect_pressed():
- new_effect()
-func _on_new_cue_list_pressed():
- new_cue()
+func _on_item_list_view_selection_changed(items: Array) -> void:
+ self.get_node(item_list_view).set_selected(items)
diff --git a/Panels/Functions/Functions.tscn b/Panels/Functions/Functions.tscn
index a0011cd..029f8e0 100644
--- a/Panels/Functions/Functions.tscn
+++ b/Panels/Functions/Functions.tscn
@@ -1,169 +1,20 @@
-[gd_scene load_steps=5 format=3 uid="uid://k641v8l2l3wp"]
+[gd_scene load_steps=3 format=3 uid="uid://cedqo06n6en8u"]
-[ext_resource type="Script" path="res://Panels/Functions/Functions.gd" id="1_uwvo7"]
-[ext_resource type="Texture2D" uid="uid://cjx13jidaqjyf" path="res://Assets/Icons/Scene.svg" id="2_iqhgr"]
-[ext_resource type="Texture2D" uid="uid://cw7e02n080in3" path="res://Assets/Icons/Effect.svg" id="3_prjry"]
-[ext_resource type="Texture2D" uid="uid://dnfqijjcnit7c" path="res://Assets/Icons/Cue_list.svg" id="4_q8830"]
+[ext_resource type="Script" path="res://Panels/Functions/Functions.gd" id="2_bfwkh"]
+[ext_resource type="PackedScene" uid="uid://c6smssfk7gn2w" path="res://Components/ItemListView/ItemListView.tscn" id="3_knoqd"]
-[node name="Functions" type="Control"]
-layout_mode = 3
+[node name="Functions" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
+script = ExtResource("2_bfwkh")
+item_list_view = NodePath("ItemListView")
-[node name="Functions" type="MarginContainer" parent="."]
-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 = 10
-theme_override_constants/margin_top = 10
-theme_override_constants/margin_right = 10
-theme_override_constants/margin_bottom = 10
-script = ExtResource("1_uwvo7")
-
-[node name="Panel" type="Panel" parent="Functions"]
-layout_mode = 2
-
-[node name="Label" type="Label" parent="Functions/Panel"]
-layout_mode = 1
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-text = "Functions are a work in progress and will be available in a future version of Spectrum."
-horizontal_alignment = 1
-vertical_alignment = 1
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Functions"]
-visible = false
-layout_mode = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="Functions/VBoxContainer"]
-layout_mode = 2
-theme_override_constants/margin_bottom = 5
-
-[node name="PanelContainer" type="PanelContainer" parent="Functions/VBoxContainer/MarginContainer"]
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="Functions/VBoxContainer/MarginContainer/PanelContainer"]
-layout_mode = 2
-
-[node name="New Scene" type="Button" parent="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-text = "New Scene"
-icon = ExtResource("2_iqhgr")
-
-[node name="New Effect" type="Button" parent="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-text = "New Effect"
-icon = ExtResource("3_prjry")
-
-[node name="New Cue List" type="Button" parent="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer"]
-layout_mode = 2
-text = "New Cue List"
-icon = ExtResource("4_q8830")
-
-[node name="PanelContainer2" type="PanelContainer" parent="Functions/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-
-[node name="HBoxContainer" type="HBoxContainer" parent="Functions/VBoxContainer/PanelContainer2"]
-layout_mode = 2
-
-[node name="Scenes" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes/ScrollContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="Scenes" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes/ScrollContainer/VBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="HSeparator" type="HSeparator" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes"]
-layout_mode = 2
-
-[node name="Label" type="Label" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes"]
-layout_mode = 2
-text = "Scenes"
-horizontal_alignment = 1
-
-[node name="VSeparator" type="VSeparator" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-
-[node name="Effects" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects/ScrollContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="Effects" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects/ScrollContainer/VBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="HSeparator" type="HSeparator" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects"]
-layout_mode = 2
-
-[node name="Label" type="Label" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects"]
-layout_mode = 2
-text = "Effects"
-horizontal_alignment = 1
-
-[node name="VSeparator2" type="VSeparator" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-
-[node name="Cues" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-
-[node name="ScrollContainer" type="ScrollContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues/ScrollContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="Cues" type="VBoxContainer" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues/ScrollContainer/VBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="HSeparator" type="HSeparator" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues"]
-layout_mode = 2
-
-[node name="Label" type="Label" parent="Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues"]
+[node name="ItemListView" parent="." instance=ExtResource("3_knoqd")]
layout_mode = 2
-text = "Cues
-"
-horizontal_alignment = 1
+allow_multi_select = false
-[connection signal="pressed" from="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/New Scene" to="Functions" method="_on_new_scene_pressed"]
-[connection signal="pressed" from="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/New Effect" to="Functions" method="_on_new_effect_pressed"]
-[connection signal="pressed" from="Functions/VBoxContainer/MarginContainer/PanelContainer/HBoxContainer/New Cue List" to="Functions" method="_on_new_cue_list_pressed"]
+[connection signal="delete_requested" from="ItemListView" to="." method="_on_item_list_view_delete_requested"]
+[connection signal="selection_changed" from="ItemListView" to="." method="_on_item_list_view_selection_changed"]
diff --git a/Panels/Home/Home.tscn b/Panels/Home/Home.tscn
new file mode 100644
index 0000000..bf9ae08
--- /dev/null
+++ b/Panels/Home/Home.tscn
@@ -0,0 +1,17 @@
+[gd_scene format=3 uid="uid://bsp1a8e33a6u4"]
+
+[node name="Home" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[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
diff --git a/Panels/IO Controls/IOControls.gd b/Panels/IO Controls/IOControls.gd
new file mode 100644
index 0000000..906ae77
--- /dev/null
+++ b/Panels/IO Controls/IOControls.gd
@@ -0,0 +1,64 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+extends Control
+## GUI element for managing universe io devices
+
+@export var item_list_view: NodePath
+
+var current_universe: Universe
+
+func _ready() -> void:
+ Core.universe_selection_changed.connect(self._reload_universes)
+
+func _reload_universes(selected_universes: Array[Universe] = [current_universe]) -> void:
+
+ ## Check if mutiple universes (or none) are selected, if so dont update the output list, only clear it
+ if len(selected_universes) == 1:
+
+ if current_universe:
+ _dissconnect()
+
+ current_universe = selected_universes[0]
+
+ current_universe.outputs_added.connect(self._reload_io)
+ current_universe.outputs_removed.connect(self._reload_io)
+
+ self.get_node(item_list_view).buttons_enabled = true
+
+ else:
+ if current_universe:
+ _dissconnect()
+
+ current_universe = null
+ self.get_node(item_list_view).buttons_enabled = false
+
+ _reload_io()
+
+
+func _dissconnect() -> void:
+ current_universe.outputs_added.disconnect(self._reload_io)
+ current_universe.outputs_removed.disconnect(self._reload_io)
+
+
+func _reload_io(_io=null) -> void:
+ ## Reloads the list of io devices
+
+ self.get_node(item_list_view).remove_all()
+
+ if current_universe:
+ self.get_node(item_list_view).add_items(current_universe.outputs.values())
+
+
+func _on_item_list_view_delete_requested(items: Array) -> void:
+ ## Called when the delete button is pressed on the ItemListView
+
+ current_universe.remove_outputs(items)
+
+
+func _on_item_list_view_add_requested() -> void:
+ current_universe.new_output()
+
+
+func _on_item_list_view_selection_changed(items: Array) -> void:
+ self.get_node(item_list_view).set_selected(items)
diff --git a/Panels/IO Controls/IOControls.tscn b/Panels/IO Controls/IOControls.tscn
new file mode 100644
index 0000000..cd86324
--- /dev/null
+++ b/Panels/IO Controls/IOControls.tscn
@@ -0,0 +1,26 @@
+[gd_scene load_steps=4 format=3 uid="uid://cim02rtl7kfpc"]
+
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_je60j"]
+[ext_resource type="Script" path="res://Panels/IO Controls/IOControls.gd" id="2_mfak0"]
+[ext_resource type="PackedScene" uid="uid://c6smssfk7gn2w" path="res://Components/ItemListView/ItemListView.tscn" id="3_rjw7h"]
+
+[node name="IO Controls" type="PanelContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme = ExtResource("1_je60j")
+script = ExtResource("2_mfak0")
+item_list_view = NodePath("ItemListView")
+
+[node name="ItemListView" parent="." instance=ExtResource("3_rjw7h")]
+layout_mode = 2
+show_take = false
+buttons_enabled = false
+
+[connection signal="add_requested" from="ItemListView" to="." method="_on_item_list_view_add_requested"]
+[connection signal="delete_requested" from="ItemListView" to="." method="_on_item_list_view_delete_requested"]
+[connection signal="selection_changed" from="ItemListView" to="." method="_on_item_list_view_selection_changed"]
diff --git a/Panels/Patch Bay/Patch_bay.gd b/Panels/Patch Bay/Patch_bay.gd
index b4d9789..f3a32bb 100644
--- a/Panels/Patch Bay/Patch_bay.gd
+++ b/Panels/Patch Bay/Patch_bay.gd
@@ -18,21 +18,19 @@ var _current_universe: Universe
var _current_io: DataIOPlugin
func _ready() -> void:
- Globals.subscribe("reload_universes", self._reload_universes)
- Globals.subscribe("patch_bay_reload_io", self._reload_io)
+ Core.universe_added.connect(self._reload_universes)
+ Core.universes_removed.connect(self._reload_universes)
func _new_universe() -> Universe:
## Creates a new Universe, and calls the reload_universes subscription
- var new_universe: Universe = Globals.new_universe()
- new_universe.set_universe_name("Universe " + str(len(Globals.universes.keys())))
+ var new_universe: Universe = Core.new_universe("Universe " + str( len( Core.universes.keys() ) + 1 ) )
- Globals.call_subscription("reload_universes")
return new_universe
-func _reload_universes() -> void:
+func _reload_universes(_universe) -> void:
## Reloads the list of Universes in the UI
if not is_instance_valid(_current_universe):
@@ -45,23 +43,23 @@ func _reload_universes() -> void:
if not _current_universe:
_set_universe_controls_enabled(false)
- for universe: Universe in Globals.universes.values():
+ for universe: Universe in Core.universes.values():
_add_universe_list_item(universe)
-
- Globals.call_subscription("patch_bay_reload_io")
+
func _add_universe_list_item(universe: Universe) -> void:
## Adds a list item to the universe list
var new_list_item: Control = Globals.components.list_item.instantiate()
- new_list_item.set_item_name(universe.get_universe_name())
+
+ new_list_item.set_item_name(universe.name)
new_list_item.control_node = self
- new_list_item.name = universe.get_uuid()
+ new_list_item.name = universe.uuid
new_list_item.set_meta("object", universe)
- if _current_universe and _current_universe.get_uuid() == universe.get_uuid():
+ if _current_universe and _current_universe.uuid == universe.uuid:
new_list_item.set_highlighted(true)
else:
new_list_item.set_highlighted(false)
@@ -82,17 +80,10 @@ func _set_universe_controls_enabled(enabled:bool) -> void:
self.get_node(universe_name).text = ""
-func _new_input() -> void:
- ## WIP Function for adding a Universe input
-
- pass
-
-
func _new_output() -> void:
- ## Adds a new output plugin to the currently selected universe, and calls reload_io()
+ ## Adds a new ArtNetOutput plugin to the currently selected universe
- _current_universe.new_output()
- Globals.call_subscription("patch_bay_reload_io")
+ _current_universe.new_output(ArtNetOutput)
func _change_io_config(io: DataIOPlugin = null) -> void:
@@ -139,7 +130,7 @@ func _expose_value(value_to_expose: Dictionary) -> void:
self.get_node(universe_io_controls).add_child(container)
-func _reload_io() -> void:
+func _reload_io(_output=null) -> void:
## Reloads the Input and Output lists for the currently selected Universe
if not is_instance_valid(_current_io):
@@ -152,13 +143,13 @@ func _reload_io() -> void:
# If _current_universe is not defined, exit the function now, this will only delete the current IO nodes and leave the list empty
if not _current_universe or not _current_io:
- _change_io_config()
+ _change_io_config(null)
_set_io_type_option("")
if not _current_universe:
return
# Loop through all outputs in _current_universe, and add them to the list
- for output: DataIOPlugin in _current_universe.get_all_outputs().values():
+ for output: DataIOPlugin in _current_universe.outputs.values():
_add_output_list_item(output)
@@ -166,16 +157,16 @@ func _add_output_list_item(output: DataIOPlugin) -> void:
## Adds a list_item component to the output list
var new_list_item: Control = Globals.components.list_item.instantiate()
- new_list_item.set_item_name(output.get_name())
+ new_list_item.set_item_name(output.name)
new_list_item.control_node = self
- new_list_item.name = output.get_uuid()
+ new_list_item.name = output.uuid
new_list_item.set_meta("object", output)
- if _current_io and _current_io.get_uuid() == output.get_uuid():
+ if _current_io and _current_io.uuid == output.uuid:
new_list_item.set_highlighted(true)
_set_io_type_option("output")
- self.get_node(universe_io_type).selected = Globals.output_plugins.keys().find(output.get_name())
+ self.get_node(universe_io_type).selected = Globals.output_plugins.keys().find(output.name)
_change_io_config(output)
else:
new_list_item.set_highlighted(false)
@@ -189,35 +180,34 @@ func _new_channel_override() -> void:
pass
-func edit_request(list_item:Control) -> void:
+func edit_request(list_item: Control) -> void:
## Called when the edit button is pressed on any IO or Universe
if list_item.get_meta("object") is Universe:
if _current_universe:
- self.get_node(universe_list).get_node(_current_universe.get_uuid()).set_highlighted(false)
+ self.get_node(universe_list).get_node(_current_universe.uuid).set_highlighted(false)
_current_universe = list_item.get_meta("object")
- self.get_node(universe_list).get_node(_current_universe.get_uuid()).set_highlighted(true)
- self.get_node(universe_name).text = _current_universe.get_universe_name()
+ _current_universe.output_added.connect(self._reload_io)
+ _current_universe.output_removed.connect(self._reload_io)
+
+ self.get_node(universe_list).get_node(_current_universe.uuid).set_highlighted(true)
+
+ self.get_node(universe_name).text = _current_universe.name
_set_universe_controls_enabled(true)
- Globals.call_subscription("patch_bay_reload_io")
+ _reload_io()
elif list_item.get_meta("object") is DataIOPlugin:
- match list_item.get_meta("object").get_type():
- "input":
- pass
-
- "output":
- if _current_io:
- self.get_node(universe_outputs).get_node(_current_io.get_uuid()).set_highlighted(false)
- _current_io = list_item.get_meta("object")
- self.get_node(universe_outputs).get_node(_current_io.get_uuid()).set_highlighted(true)
-
- Globals.call_subscription("patch_bay_reload_io")
+ if _current_io:
+ self.get_node(universe_outputs).get_node(_current_io.uuid).set_highlighted(false)
+ _current_io = list_item.get_meta("object")
+ self.get_node(universe_outputs).get_node(_current_io.uuid).set_highlighted(true)
+
+ _reload_io()
-func _set_io_type_option(io_type:String) -> void:
+func _set_io_type_option(io_type: String) -> void:
## Updates the list of IO types
self.get_node(universe_io_type).clear()
@@ -226,12 +216,14 @@ func _set_io_type_option(io_type:String) -> void:
# Otherwise dissable the list dropdown
if io_type:
self.get_node(universe_io_type).disabled = false
+
match io_type:
"input":
pass
"output":
- for name in Globals.output_plugins:
+ for name in Core.output_plugins:
self.get_node(universe_io_type).add_item(name)
+
else:
self.get_node(universe_io_type).disabled = true
@@ -245,6 +237,9 @@ func delete_request(list_item:Control) -> void:
confirmation_dialog.confirmed.connect((
func(list_item: Control):
if list_item.get_meta("object") is Universe:
+
+ print("about to delete")
+
_delete_universe(list_item.get_meta("object"))
elif list_item.get_meta("object") is DataIOPlugin:
@@ -257,11 +252,12 @@ func delete_request(list_item:Control) -> void:
func _delete_universe(universe: Universe) -> void:
## Deletes a Universe
- Globals.delete_universe(universe)
+ print("deleting universe")
+
+ Core.delete_universe(universe)
_current_io = null
_current_universe = null
- _reload_universes()
_set_universe_controls_enabled(false)
@@ -269,27 +265,19 @@ func _delete_io(io: DataIOPlugin) -> void:
## Deletes an IO plugin
var to_delete: DataIOPlugin = io
- match to_delete.get_type():
- "input":
- _current_universe.remove_input(to_delete)
- "output":
- _current_universe.remove_output(to_delete)
+ _current_universe.remove_output(to_delete)
if to_delete == _current_io:
_current_io = null
- Globals.call_subscription("patch_bay_reload_io")
func _on_io_type_item_selected(index:int) -> void:
## Called when the user selects an IO type from the list dropdown
- if _current_io:
- match _current_io.get_type():
- "input":
- _current_io = _current_universe.change_input_type(_current_io.get_uuid(), Globals.input_plugins.values()[index])
- "output":
- _current_io = _current_universe.change_output_type(_current_io.get_uuid(), Globals.output_plugins.values()[index])
- Globals.call_subscription("patch_bay_reload_io")
+ _current_universe.remove_output(_current_io)
+ _current_io = _current_universe.new_output(Core.output_plugins.values()[index].plugin)
+
+ _reload_io()
func _on_new_universe_pressed() -> void:
@@ -300,14 +288,9 @@ func _on_new_channel_overide_pressed() -> void:
pass
-func _on_new_input_pressed() -> void:
- pass
-
-
func _on_new_output_pressed() -> void:
_new_output()
func _on_universe_name_text_changed(new_text) -> void:
- _current_universe.set_universe_name(new_text)
- _reload_universes()
+ _current_universe.name = new_text
diff --git a/Panels/Playback Buttons/PlaybackButtons.gd b/Panels/Playback Buttons/PlaybackButtons.gd
new file mode 100644
index 0000000..9edb0e4
--- /dev/null
+++ b/Panels/Playback Buttons/PlaybackButtons.gd
@@ -0,0 +1,34 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+extends Control
+## Temp ui panel for triggering scenes
+
+func _ready() -> void:
+ Core.scenes_added.connect(self._reload_buttons)
+ Core.scenes_removed.connect(self._reload_buttons)
+
+
+func _reload_buttons(_scene) -> void:
+ ## Reloads the buttons in the ui
+
+ for old_button: Button in self.get_children():
+ self.remove_child(old_button)
+ old_button.queue_free()
+
+ for scene: Scene in Core.scenes.values():
+ var button_to_add: Button = Globals.components.trigger_button.instantiate()
+
+ button_to_add.set_label_text(scene.name)
+ button_to_add.toggled.connect(
+ func(state):
+ scene.enabled = state
+ )
+
+ button_to_add.set_pressed_no_signal(scene.enabled)
+
+ self.add_child(button_to_add)
+
+
+func _on_resized() -> void:
+ self.columns = clamp(int(self.size.x / 110), 1, INF)
diff --git a/Panels/Playback Buttons/PlaybackButtons.tscn b/Panels/Playback Buttons/PlaybackButtons.tscn
new file mode 100644
index 0000000..26f237a
--- /dev/null
+++ b/Panels/Playback Buttons/PlaybackButtons.tscn
@@ -0,0 +1,27 @@
+[gd_scene load_steps=2 format=3 uid="uid://c5h003o3qi1wl"]
+
+[ext_resource type="Script" path="res://Panels/Playback Buttons/PlaybackButtons.gd" id="1_0mir5"]
+
+[node name="PlaybackButtons" type="PanelContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_right = -2.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="ScrollContainer" type="ScrollContainer" parent="."]
+layout_mode = 2
+horizontal_scroll_mode = 0
+
+[node name="GridContainer" type="GridContainer" parent="ScrollContainer"]
+clip_contents = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+columns = 18
+script = ExtResource("1_0mir5")
+
+[connection signal="resized" from="ScrollContainer/GridContainer" to="ScrollContainer/GridContainer" method="_on_resized"]
diff --git a/Scripts/Playbacks.gd b/Panels/Playbacks/Playbacks.gd
similarity index 100%
rename from Scripts/Playbacks.gd
rename to Panels/Playbacks/Playbacks.gd
diff --git a/Panels/Playbacks.tscn b/Panels/Playbacks/Playbacks.tscn
similarity index 93%
rename from Panels/Playbacks.tscn
rename to Panels/Playbacks/Playbacks.tscn
index 31d9396..e947a11 100644
--- a/Panels/Playbacks.tscn
+++ b/Panels/Playbacks/Playbacks.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://baortk0rwrhup"]
-[ext_resource type="Script" path="res://Scripts/Playbacks.gd" id="1_io88g"]
+[ext_resource type="Script" path="res://Panels/Playbacks/Playbacks.gd" id="1_io88g"]
[node name="MarginContainer" type="MarginContainer"]
offset_right = 1920.0
diff --git a/Panels/Programmer/ProgrammerUi.tscn b/Panels/Programmer/ProgrammerUi.tscn
new file mode 100644
index 0000000..8b4d124
--- /dev/null
+++ b/Panels/Programmer/ProgrammerUi.tscn
@@ -0,0 +1,153 @@
+[gd_scene load_steps=7 format=3 uid="uid://bwxr0mrsrt0iu"]
+
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_jp1gn"]
+[ext_resource type="Texture2D" uid="uid://be3tq05v1ne7n" path="res://Assets/Icons/Horizontal_distribute.svg" id="2_h3xak"]
+[ext_resource type="Texture2D" uid="uid://bn4jkekidjyne" path="res://Assets/Icons/Vertical_distribute.svg" id="3_yvf0x"]
+[ext_resource type="Texture2D" uid="uid://vm1u4mv102e3" path="res://Assets/Icons/Grid.svg" id="4_48am0"]
+[ext_resource type="Texture2D" uid="uid://th5chd6y3ygu" path="res://Assets/Icons/Rotate.svg" id="5_tu5fs"]
+[ext_resource type="PackedScene" uid="uid://ccibt4jo4f1jt" path="res://Components/Knob/Encoder.tscn" id="6_0w05c"]
+
+[node name="TabContainer" type="TabContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme = ExtResource("1_jp1gn")
+
+[node name="Programmer" type="HBoxContainer" parent="."]
+layout_mode = 2
+size_flags_horizontal = 0
+size_flags_vertical = 0
+metadata/_edit_use_anchors_ = true
+
+[node name="PanelContainer2" type="PanelContainer" parent="Programmer"]
+layout_mode = 2
+size_flags_horizontal = 0
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Programmer/PanelContainer2"]
+layout_mode = 2
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer/PanelContainer2/HBoxContainer"]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer"]
+layout_mode = 2
+
+[node name="VBoxContainer" type="VBoxContainer" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer"]
+custom_minimum_size = Vector2(100, 0)
+layout_mode = 2
+
+[node name="HAlign" type="Button" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+icon = ExtResource("2_h3xak")
+icon_alignment = 1
+
+[node name="VAlign" type="Button" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+icon = ExtResource("3_yvf0x")
+icon_alignment = 1
+
+[node name="VBoxContainer2" type="VBoxContainer" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer"]
+custom_minimum_size = Vector2(100, 0)
+layout_mode = 2
+
+[node name="HAlign" type="Button" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+size_flags_vertical = 3
+icon = ExtResource("4_48am0")
+icon_alignment = 1
+
+[node name="VAlign" type="Button" parent="Programmer/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+size_flags_vertical = 3
+icon = ExtResource("5_tu5fs")
+icon_alignment = 1
+
+[node name="VBoxContainer2" type="VBoxContainer" parent="Programmer/PanelContainer2/HBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+
+[node name="Encoder" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer2/PanelContainer" instance=ExtResource("6_0w05c")]
+custom_minimum_size = Vector2(150, 150)
+layout_mode = 2
+size_flags_vertical = 3
+rotation_offset = 90
+angle_gap = 0
+wrap_around_value = true
+
+[node name="SpinBox" type="SpinBox" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+prefix = "Gap:"
+
+[node name="VBoxContainer3" type="VBoxContainer" parent="Programmer/PanelContainer2/HBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer3"]
+layout_mode = 2
+
+[node name="Encoder" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer3/PanelContainer" instance=ExtResource("6_0w05c")]
+custom_minimum_size = Vector2(150, 150)
+layout_mode = 2
+size_flags_vertical = 3
+rotation_offset = 90
+angle_gap = 0
+wrap_around_value = true
+
+[node name="SpinBox" type="SpinBox" parent="Programmer/PanelContainer2/HBoxContainer/VBoxContainer3"]
+layout_mode = 2
+prefix = "Phase:"
+
+[node name="VSeparator2" type="VSeparator" parent="Programmer"]
+layout_mode = 2
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer"]
+layout_mode = 2
+
+[node name="HBoxContainer" type="HBoxContainer" parent="Programmer/PanelContainer"]
+layout_mode = 2
+
+[node name="VBoxContainer2" type="VBoxContainer" parent="Programmer/PanelContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+
+[node name="Encoder" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer2/PanelContainer" instance=ExtResource("6_0w05c")]
+custom_minimum_size = Vector2(150, 150)
+layout_mode = 2
+size_flags_vertical = 3
+rotation_offset = 90
+angle_gap = 0
+wrap_around_value = true
+
+[node name="SpinBox" type="SpinBox" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer2"]
+layout_mode = 2
+prefix = "Rotation:"
+suffix = "°"
+
+[node name="VBoxContainer3" type="VBoxContainer" parent="Programmer/PanelContainer/HBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="PanelContainer" type="PanelContainer" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer3"]
+layout_mode = 2
+
+[node name="Encoder" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer3/PanelContainer" instance=ExtResource("6_0w05c")]
+custom_minimum_size = Vector2(150, 150)
+layout_mode = 2
+size_flags_vertical = 3
+rotation_offset = 90
+angle_gap = 0
+wrap_around_value = true
+
+[node name="SpinBox" type="SpinBox" parent="Programmer/PanelContainer/HBoxContainer/VBoxContainer3"]
+layout_mode = 2
+prefix = "Phase:"
diff --git a/Panels/Settings/Settings.gd b/Panels/Settings/Settings.gd
index b77752e..b9c2c76 100644
--- a/Panels/Settings/Settings.gd
+++ b/Panels/Settings/Settings.gd
@@ -36,7 +36,7 @@ func load_settings() -> void:
var section_node := add_section_node(section)
for setting in settings[section].keys():
var config : Dictionary = settings[section][setting]
- var value : Variant = config_file.get_value(section, setting, null)
+ var value : Variant = config_file.get_value(section, setting, "")
if value:
config.function.call(value)
var input_node = add_setting_node(section_node, setting, value, config.input, config.signal, config.function, config.setter, config.get("configs", {}), config.get("tooltip", ""))
diff --git a/Panels/Universes/Universes.gd b/Panels/Universes/Universes.gd
new file mode 100644
index 0000000..0ddb52d
--- /dev/null
+++ b/Panels/Universes/Universes.gd
@@ -0,0 +1,39 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+extends Control
+## GUI element for managing universes
+
+@export var item_list_view: NodePath
+
+
+func _ready() -> void:
+ print(item_list_view)
+ Core.universes_added.connect(self._reload_universes)
+ Core.universes_removed.connect(self._reload_universes)
+ Core.universe_name_changed.connect(self._reload_universes)
+ Core.universe_selection_changed.connect(self._universe_selection_changed)
+
+
+func _reload_universes(_universes=null) -> void:
+ ## Reload the list of fixtures
+
+ self.get_node(item_list_view).remove_all()
+ self.get_node(item_list_view).add_items(Core.universes.values())
+
+
+func _universe_selection_changed(selected_universes: Array) -> void:
+ self.get_node(item_list_view).set_selected(selected_universes)
+
+
+func _on_item_list_view_delete_requested(items: Array) -> void:
+ ## Called when the delete button is pressed on the ItemListView
+ Core.remove_universes(items)
+
+
+func _on_item_list_view_add_requested() -> void:
+ Core.new_universe()
+
+
+func _on_item_list_view_selection_changed(items: Array) -> void:
+ Core.set_universe_selection(items)
diff --git a/Panels/Universes/Universes.tscn b/Panels/Universes/Universes.tscn
new file mode 100644
index 0000000..eb182e1
--- /dev/null
+++ b/Panels/Universes/Universes.tscn
@@ -0,0 +1,24 @@
+[gd_scene load_steps=4 format=3 uid="uid://03t7su6t2rq2"]
+
+[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_hrdmq"]
+[ext_resource type="Script" path="res://Panels/Universes/Universes.gd" id="2_dy1nn"]
+[ext_resource type="PackedScene" uid="uid://c6smssfk7gn2w" path="res://Components/ItemListView/ItemListView.tscn" id="2_nny3h"]
+
+[node name="Universes" type="PanelContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme = ExtResource("1_hrdmq")
+script = ExtResource("2_dy1nn")
+item_list_view = NodePath("ItemListView")
+
+[node name="ItemListView" parent="." instance=ExtResource("2_nny3h")]
+layout_mode = 2
+
+[connection signal="add_requested" from="ItemListView" to="." method="_on_item_list_view_add_requested"]
+[connection signal="delete_requested" from="ItemListView" to="." method="_on_item_list_view_delete_requested"]
+[connection signal="selection_changed" from="ItemListView" to="." method="_on_item_list_view_selection_changed"]
diff --git a/Panels/Virtual Fixtures/Virtual_fixtures.gd b/Panels/Virtual Fixtures/Virtual_fixtures.gd
index 02f7ff8..050f822 100644
--- a/Panels/Virtual Fixtures/Virtual_fixtures.gd
+++ b/Panels/Virtual Fixtures/Virtual_fixtures.gd
@@ -15,6 +15,8 @@ var _add_fixture_button: Button
var _position_offset: Vector2 = Vector2(100, 100)
+var last_call_time: float = 0.0
+
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
## Add extra buttons to GraphEdit menu, and subscribe to global variables
@@ -24,7 +26,7 @@ func _ready() -> void:
_add_menu_hbox_button(ResourceLoader.load("res://Assets/Icons/Horizontal_distribute.svg"), self._align.bind(ORIENTATION_HORIZONTAL), "Align the selected fixtures horizontally" )
_add_menu_hbox_button(ResourceLoader.load("res://Assets/Icons/Vertical_distribute.svg"), self._align.bind(ORIENTATION_VERTICAL), "Align the selected fixtures verticality" )
- Globals.subscribe("active_fixtures", self._active_fixtures_changed)
+ Core.fixture_selection_changed.connect(self._active_fixtures_changed)
func _add_menu_hbox_button(content:Variant, method: Callable, tooltip: String = "", disabled: bool = false) -> Button:
@@ -48,43 +50,47 @@ func _add_menu_hbox_button(content:Variant, method: Callable, tooltip: String =
func _add_fixture() -> void:
## Adds the currently selected virtual fixtures to the view
- for fixture: Fixture in Globals.get_value("active_fixtures"):
+ for fixture: Fixture in Core.selected_fixtures:
var node_to_add: Control = Globals.components.virtual_fixture.instantiate()
- fixture.add_virtual_fixture(node_to_add)
-
- node_to_add.control_node = fixture
+ node_to_add.set_fixture(fixture)
node_to_add.set_highlighted(true)
+
+ if not fixture.user_meta.get("virtual_fixtures"):
+ fixture.user_meta.virtual_fixtures = []
+
+ node_to_add.virtual_fixture_index = len(fixture.user_meta.virtual_fixtures)
node_to_add.position_offset += _position_offset
_position_offset += Vector2(5, 5)
+ fixture.user_meta.virtual_fixtures.append(node_to_add.position_offset)
+
self.add_child(node_to_add)
func _request_delete() -> void:
## Deletes a virtual fixtures from the current view
-
var to_remove: Array = _selected_virtual_fixtures.duplicate()
for virtual_fixture: Control in to_remove:
- virtual_fixture.control_node.remove_virtual_fixture(virtual_fixture)
+ virtual_fixture.fixture.user_meta.virtual_fixtures.remove_at(virtual_fixture.virtual_fixture_index)
virtual_fixture.queue_free()
_selected_virtual_fixtures.erase(virtual_fixture)
-
+ #
func _active_fixtures_changed(new_active_fixtures: Array) -> void:
## Function to update highlighting on virtual fixtures, when their corresponding fixture is selected
_add_fixture_button.disabled = true if new_active_fixtures == [] else false
- for virtual_fixture: Control in get_children():
- virtual_fixture.set_highlighted(false)
-
- for active_fixture: Fixture in new_active_fixtures:
- for virtual_fixture in active_fixture.virtual_fixtures:
- virtual_fixture.set_highlighted(true)
-
- _old_active_fixtures = new_active_fixtures
+ #for virtual_fixture: Control in get_children():
+ #virtual_fixture.set_highlighted(false)
+ #
+ #for active_fixture: Fixture in new_active_fixtures:
+ #for virtual_fixture in active_fixture.get_user_meta("virtual_fixtures", []):
+ #virtual_fixture.set_highlighted(true)
+ #
+ #_old_active_fixtures = new_active_fixtures
func _align(orientation: int) -> void:
@@ -94,10 +100,10 @@ func _align(orientation: int) -> void:
return
var base_position: Vector2 = _selected_virtual_fixtures[0].position_offset
- var i: int = 0
for virtual_fixture: Control in _selected_virtual_fixtures:
virtual_fixture.position_offset = base_position
+ virtual_fixture.fixture.user_meta.virtual_fixtures[virtual_fixture.virtual_fixture_index] = base_position
if orientation:
base_position.x += 100
@@ -111,7 +117,7 @@ func from(config: Dictionary, control_fixture: Fixture) -> void:
var node_to_add: Control = Globals.components.virtual_fixture.instantiate()
node_to_add._position_offset = Vector2(config._position_offset.x, config._position_offset.y)
- node_to_add.control_node = control_fixture
+ node_to_add.set_fixture(control_fixture)
control_fixture.add_virtual_fixture(node_to_add)
self.add_child(node_to_add)
@@ -120,9 +126,22 @@ func from(config: Dictionary, control_fixture: Fixture) -> void:
func _on_virtual_fixture_selected(node) -> void:
if node not in _selected_virtual_fixtures:
_selected_virtual_fixtures.append(node)
- Globals.select_fixture(node.control_node)
+ Core.select_fixtures([node.fixture])
func _on_virtual_fixture_deselected(node) -> void:
_selected_virtual_fixtures.erase(node)
- Globals.deselect_fixture(node.control_node)
+ Core.deselect_fixtures([node.fixture])
+
+
+func _on_color_picker_color_changed(color: Color) -> void:
+ var current_time = Time.get_ticks_msec() / 1000.0 # Convert milliseconds to seconds
+
+ if current_time - last_call_time >= Core.call_interval:
+ Core.programmer.set_color(Core.selected_fixtures, color)
+
+ last_call_time = current_time
+
+
+func _on_save_pressed() -> void:
+ Core.programmer.save_to_scene()
diff --git a/Panels/Virtual Fixtures/Virtual_fixtures.tscn b/Panels/Virtual Fixtures/Virtual_fixtures.tscn
index fa857a8..89865c9 100644
--- a/Panels/Virtual Fixtures/Virtual_fixtures.tscn
+++ b/Panels/Virtual Fixtures/Virtual_fixtures.tscn
@@ -1,14 +1,10 @@
-[gd_scene load_steps=10 format=3 uid="uid://dhrijn1m88172"]
+[gd_scene load_steps=6 format=3 uid="uid://dhrijn1m88172"]
[ext_resource type="Theme" uid="uid://cyua45ur0ijqo" path="res://Assets/Main.theme" id="1_hwlxv"]
[ext_resource type="Script" path="res://Panels/Virtual Fixtures/Virtual_fixtures.gd" id="1_y16fu"]
[ext_resource type="Script" path="res://Scripts/Sidebar.gd" id="2_j8tmg"]
[ext_resource type="Texture2D" uid="uid://vw0vs7dlct55" path="res://Assets/Icons/menu.svg" id="3_xlq73"]
-[ext_resource type="Texture2D" uid="uid://be3tq05v1ne7n" path="res://Assets/Icons/Horizontal_distribute.svg" id="5_0jdj6"]
-[ext_resource type="Texture2D" uid="uid://bn4jkekidjyne" path="res://Assets/Icons/Vertical_distribute.svg" id="6_ivvmw"]
-[ext_resource type="Texture2D" uid="uid://vm1u4mv102e3" path="res://Assets/Icons/Grid.svg" id="7_krdug"]
-[ext_resource type="PackedScene" uid="uid://ccibt4jo4f1jt" path="res://Components/Knob/Encoder.tscn" id="7_ny1u7"]
-[ext_resource type="Texture2D" uid="uid://th5chd6y3ygu" path="res://Assets/Icons/Rotate.svg" id="8_8ml5j"]
+[ext_resource type="PackedScene" uid="uid://c5h003o3qi1wl" path="res://Panels/Playback Buttons/PlaybackButtons.tscn" id="5_uh5xn"]
[node name="Virtual Fixtures" type="Control"]
layout_mode = 3
@@ -19,7 +15,7 @@ grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_hwlxv")
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
+[node name="HBoxContainer" type="HSplitContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@@ -27,209 +23,78 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
-[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-
-[node name="Virtual Fixtures" type="GraphEdit" parent="HBoxContainer/VBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 3
-size_flags_vertical = 3
-theme_override_colors/grid_minor = Color(0.101961, 0.101961, 0.101961, 1)
-theme_override_colors/grid_major = Color(0.0784314, 0.0784314, 0.0784314, 1)
-show_minimap_button = false
-show_arrange_button = false
-script = ExtResource("1_y16fu")
-
-[node name="Bottom Bar" type="PanelContainer" parent="HBoxContainer/VBoxContainer"]
-visible = false
+[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
-[node name="MarginContainer" type="MarginContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar"]
-layout_mode = 2
-theme_override_constants/margin_left = 4
-theme_override_constants/margin_top = 4
-theme_override_constants/margin_right = 4
-theme_override_constants/margin_bottom = 4
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer"]
-layout_mode = 2
-
-[node name="Open" type="Button" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer"]
-layout_mode = 2
-size_flags_horizontal = 0
-icon = ExtResource("3_xlq73")
-icon_alignment = 1
-
-[node name="Content" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer" node_paths=PackedStringArray("contence")]
-layout_mode = 2
-size_flags_horizontal = 3
-script = ExtResource("2_j8tmg")
-contence = NodePath(".")
-
-[node name="VSeparator" type="VSeparator" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content"]
-layout_mode = 2
-
-[node name="PanelContainer2" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content"]
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer"]
-layout_mode = 2
-
-[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer"]
-custom_minimum_size = Vector2(100, 0)
-layout_mode = 2
-
-[node name="HAlign" type="Button" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/PanelContainer"]
layout_mode = 2
size_flags_vertical = 3
-icon = ExtResource("5_0jdj6")
-icon_alignment = 1
-[node name="VAlign" type="Button" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer"]
+[node name="PlaybackButtons" parent="HBoxContainer/PanelContainer/VBoxContainer" instance=ExtResource("5_uh5xn")]
layout_mode = 2
-size_flags_vertical = 3
-icon = ExtResource("6_ivvmw")
-icon_alignment = 1
-[node name="VBoxContainer2" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer"]
-custom_minimum_size = Vector2(100, 0)
+[node name="Button" type="Button" parent="HBoxContainer/PanelContainer/VBoxContainer"]
layout_mode = 2
+size_flags_vertical = 8
+text = "Save"
-[node name="HAlign" type="Button" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer2"]
+[node name="HSplitContainer" type="HSplitContainer" parent="HBoxContainer"]
layout_mode = 2
-size_flags_vertical = 3
-icon = ExtResource("7_krdug")
-icon_alignment = 1
-[node name="VAlign" type="Button" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/PanelContainer/HBoxContainer/VBoxContainer2"]
-layout_mode = 2
-size_flags_vertical = 3
-icon = ExtResource("8_8ml5j")
-icon_alignment = 1
-
-[node name="VBoxContainer2" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer2"]
-layout_mode = 2
-
-[node name="Encoder" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer2/PanelContainer" instance=ExtResource("7_ny1u7")]
-custom_minimum_size = Vector2(150, 150)
-layout_mode = 2
-size_flags_vertical = 3
-rotation_offset = 90
-angle_gap = 0
-wrap_around_value = true
-
-[node name="SpinBox" type="SpinBox" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer2"]
-layout_mode = 2
-prefix = "Gap:"
-
-[node name="VBoxContainer3" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer3"]
-layout_mode = 2
-
-[node name="Encoder" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer3/PanelContainer" instance=ExtResource("7_ny1u7")]
-custom_minimum_size = Vector2(150, 150)
-layout_mode = 2
-size_flags_vertical = 3
-rotation_offset = 90
-angle_gap = 0
-wrap_around_value = true
-
-[node name="SpinBox" type="SpinBox" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer2/HBoxContainer/VBoxContainer3"]
-layout_mode = 2
-prefix = "Phase:"
-
-[node name="VSeparator2" type="VSeparator" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content"]
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer"]
-layout_mode = 2
-
-[node name="VBoxContainer2" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer2"]
-layout_mode = 2
-
-[node name="Encoder" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer2/PanelContainer" instance=ExtResource("7_ny1u7")]
-custom_minimum_size = Vector2(150, 150)
-layout_mode = 2
-size_flags_vertical = 3
-rotation_offset = 90
-angle_gap = 0
-wrap_around_value = true
-
-[node name="SpinBox" type="SpinBox" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer2"]
-layout_mode = 2
-prefix = "Rotation:"
-suffix = "°"
-
-[node name="VBoxContainer3" type="VBoxContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer"]
-layout_mode = 2
-
-[node name="PanelContainer" type="PanelContainer" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer3"]
+[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/HSplitContainer"]
layout_mode = 2
+size_flags_horizontal = 3
-[node name="Encoder" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer3/PanelContainer" instance=ExtResource("7_ny1u7")]
-custom_minimum_size = Vector2(150, 150)
+[node name="Virtual Fixtures" type="GraphEdit" parent="HBoxContainer/HSplitContainer/VBoxContainer"]
layout_mode = 2
+size_flags_horizontal = 3
size_flags_vertical = 3
-rotation_offset = 90
-angle_gap = 0
-wrap_around_value = true
+theme_override_colors/grid_minor = Color(0.101961, 0.101961, 0.101961, 1)
+theme_override_colors/grid_major = Color(0.0784314, 0.0784314, 0.0784314, 1)
+show_minimap_button = false
+show_arrange_button = false
+script = ExtResource("1_y16fu")
-[node name="SpinBox" type="SpinBox" parent="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content/PanelContainer/HBoxContainer/VBoxContainer3"]
+[node name="Bottom Bar" type="PanelContainer" parent="HBoxContainer/HSplitContainer/VBoxContainer"]
+visible = false
+custom_minimum_size = Vector2(50, 0)
layout_mode = 2
-prefix = "Phase:"
-[node name="Sidebar" type="PanelContainer" parent="HBoxContainer"]
+[node name="Sidebar" type="PanelContainer" parent="HBoxContainer/HSplitContainer"]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
-[node name="MarginContainer" type="MarginContainer" parent="HBoxContainer/Sidebar"]
+[node name="MarginContainer" type="MarginContainer" parent="HBoxContainer/HSplitContainer/Sidebar"]
layout_mode = 2
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 4
-[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/Sidebar/MarginContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/HSplitContainer/Sidebar/MarginContainer"]
layout_mode = 2
-[node name="Side Bar Open" type="Button" parent="HBoxContainer/Sidebar/MarginContainer/VBoxContainer"]
+[node name="Side Bar Open" type="Button" parent="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer"]
layout_mode = 2
icon = ExtResource("3_xlq73")
icon_alignment = 1
-[node name="Contence" type="VBoxContainer" parent="HBoxContainer/Sidebar/MarginContainer/VBoxContainer" node_paths=PackedStringArray("contence")]
-visible = false
+[node name="Contence" type="VBoxContainer" parent="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer" node_paths=PackedStringArray("contence")]
layout_mode = 2
+size_flags_vertical = 3
script = ExtResource("2_j8tmg")
contence = NodePath(".")
-[node name="ColorPicker" type="ColorPicker" parent="HBoxContainer/Sidebar/MarginContainer/VBoxContainer/Contence"]
+[node name="ColorPicker" type="ColorPicker" parent="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer/Contence"]
layout_mode = 2
color = Color(0, 1, 1, 1)
edit_alpha = false
picker_shape = 1
color_modes_visible = false
-[connection signal="node_deselected" from="HBoxContainer/VBoxContainer/Virtual Fixtures" to="HBoxContainer/VBoxContainer/Virtual Fixtures" method="_on_virtual_fixture_deselected"]
-[connection signal="node_selected" from="HBoxContainer/VBoxContainer/Virtual Fixtures" to="HBoxContainer/VBoxContainer/Virtual Fixtures" method="_on_virtual_fixture_selected"]
-[connection signal="pressed" from="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Open" to="HBoxContainer/VBoxContainer/Bottom Bar/MarginContainer/HBoxContainer/Content" method="_on_button_pressed"]
-[connection signal="pressed" from="HBoxContainer/Sidebar/MarginContainer/VBoxContainer/Side Bar Open" to="HBoxContainer/Sidebar/MarginContainer/VBoxContainer/Contence" method="_on_button_pressed"]
+[connection signal="pressed" from="HBoxContainer/PanelContainer/VBoxContainer/Button" to="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" method="_on_save_pressed"]
+[connection signal="node_deselected" from="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" to="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" method="_on_virtual_fixture_deselected"]
+[connection signal="node_selected" from="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" to="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" method="_on_virtual_fixture_selected"]
+[connection signal="pressed" from="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer/Side Bar Open" to="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer/Contence" method="_on_button_pressed"]
+[connection signal="color_changed" from="HBoxContainer/HSplitContainer/Sidebar/MarginContainer/VBoxContainer/Contence/ColorPicker" to="HBoxContainer/HSplitContainer/VBoxContainer/Virtual Fixtures" method="_on_color_picker_color_changed"]
diff --git a/Scripts/Classes/Fixture.gd b/Scripts/Classes/Fixture.gd
deleted file mode 100644
index 6d1dbef..0000000
--- a/Scripts/Classes/Fixture.gd
+++ /dev/null
@@ -1,80 +0,0 @@
-extends Node
-class_name Fixture
-
-var config = {
- "universe":null,
- "channel":1,
- "length":1,
- "fixture_brand":"",
- "fixture_name":"",
- "display_name":"",
- "file_path":"",
- "uuid":"",
- "mode":""
-}
-
-var channels = {}
-var channel_ranges = {}
-var compiled_dmx_data = {}
-var virtual_fixtures = []
-var parameters = {}
-
-func serialize():
- var serialized_virtual_fixtures = []
- for fixture in virtual_fixtures:
- serialized_virtual_fixtures.append(fixture.serialize())
-
- return {
- "display_name":config.display_name,
- "fixture_brand":config.fixture_brand,
- "fixture_name":config.fixture_name,
- "mode":config.mode,
- "virtual_fixtures":serialized_virtual_fixtures
- }
-
-func from(universe, manifest, channel, mode, name, uuid, virtual_fixtures):
- channel_ranges = manifest.get("channels", {})
- channels = manifest.modes.values()[mode].channels
- config.universe = universe
- config.channel = channel
- config.length = len(manifest.modes.values()[mode].channels)
- config.fixture_brand = manifest.info.brand
- config.fixture_name = manifest.info.name
- config.name = name
- config.uuid = uuid
- config.mode = mode
-
- if virtual_fixtures:
- for fixture in virtual_fixtures:
- Globals.nodes.virtual_fixtures.from(fixture, self)
-
- return self
-
-func set_color_rgb(r,g,b):
- if "ColorIntensityRed" in channels:
- compiled_dmx_data[int(channels.find("ColorIntensityRed")+config.channel)] = int(remap(r, 0.0, 1.0, 0.0, 255.0))
- if "ColorIntensityGreen" in channels:
- compiled_dmx_data[int(channels.find("ColorIntensityGreen")+config.channel)] = int(remap(g, 0.0, 1.0, 0.0, 255.0))
- if "ColorIntensityBlue" in channels:
- compiled_dmx_data[int(channels.find("ColorIntensityBlue")+config.channel)] = int(remap(b, 0.0, 1.0, 0.0, 255.0))
- config.universe.set_fixture_data(compiled_dmx_data)
-
- parameters.color = Color(r, g, b)
-
- update_virtual_fixtures()
-
-func update_virtual_fixtures():
- if not virtual_fixtures:return
-
- for fixture in virtual_fixtures:
- fixture.set_color_rgb(parameters.color)
-
-func add_virtual_fixture(node):
- virtual_fixtures.append(node)
-
-func remove_virtual_fixture(node):
- virtual_fixtures.erase(node)
-
-func delete():
- for virtual_fixture in virtual_fixtures:
- virtual_fixture.delete()
diff --git a/Scripts/Classes/Universe.gd b/Scripts/Classes/Universe.gd
deleted file mode 100644
index cfa47be..0000000
--- a/Scripts/Classes/Universe.gd
+++ /dev/null
@@ -1,163 +0,0 @@
-extends Object
-class_name Universe
-
-const Fixture = preload("res://Scripts/Classes/Fixture.gd")
-
-var universe = {
- "name": "New Universe",
- "uuid":Globals.new_uuid(),
- "fixtures": {
- },
- "inputs": {
- },
- "outputs": {
-
- },
- "fixture_data":{
-
- },
- "desk_data":{
-
- }
-}
-
-func set_universe_name(new_name):
- universe.name = new_name
-
-func get_universe_name():
- return universe.name
-
-func get_uuid():
- return universe.uuid
-
-func get_all_outputs():
- return universe.outputs
-
-func get_output(uuid=""):
- if uuid:
- return universe.outputs[uuid]
- return
-
-func new_output(type=EmptyOutput):
- var uuid = Globals.new_uuid()
- var new_output: DataIOPlugin = type.new()
- new_output.set_uuid(uuid)
- universe.outputs[uuid] = new_output
- return new_output
-
-func new_fixture(manifest, options):
- if options.channel in universe.fixtures:
- return false
-
- for i in range(options.quantity):
-
- var channel_index = options.channel + options.offset
- channel_index += (len(manifest.modes.values()[options.mode].channels)) * i
- var uuid = Globals.new_uuid()
- var new_fixture = Fixture.new().from(self, manifest, channel_index, options.mode, options.name, uuid, options.get("virtual_fixtures", []))
-
- universe.fixtures[channel_index] = new_fixture
-
- Globals.call_subscription("reload_fixtures")
-
-func remove_fixture(fixture):
- universe.fixtures.erase(fixture.config.channel)
- fixture.delete()
-
- var active_fixtures = Globals.get_value("active_fixtures")
- if active_fixtures:
- if fixture in active_fixtures:
- active_fixtures.erase(fixture)
- Globals.set_value("active_fixtures", active_fixtures)
-
-func get_fixtures():
- return universe.fixtures
-
-func get_fixture(fixture_uuid):
- pass
-
-func set_fixture_data(data):
- universe.fixture_data.merge(data, true)
- _compile_and_send()
-
-func remove_output(output):
- universe.outputs.erase(output.get_uuid())
- output.delete()
- output.free()
-
-func change_output_type(uuid, type):
- if not type: type == EmptyOutput
- universe.outputs[uuid].delete()
- universe.outputs[uuid].free()
- universe.outputs[uuid] = type.new()
- universe.outputs[uuid].set_uuid(uuid)
- return universe.outputs[uuid]
-
-func set_desk_data(dmx_data):
- universe.desk_data.merge(dmx_data)
- _compile_and_send()
-
-func get_desk_data():
- return universe.desk_data
-
-func _compile_and_send():
- var compiled_dmx_data = universe.fixture_data
- compiled_dmx_data.merge(universe.desk_data, true)
- for output in universe.outputs:
- universe.outputs[output].send_packet(compiled_dmx_data)
-
-func serialize():
- var serialized_outputs = {}
- var serialized_fixtures = {}
-
- for output_uuid in universe.outputs.keys():
- serialized_outputs[output_uuid] = universe.outputs[output_uuid].serialize()
-
- for fixture_channel in universe.fixtures.keys():
- var serialized_fixture = universe.fixtures[fixture_channel].serialize()
- if serialized_fixture:
- serialized_fixtures[fixture_channel] = serialized_fixture
-
-
- return {
- "name":universe.name,
- "uuid":universe.uuid,
- "fixtures":serialized_fixtures,
- "inputs":{},
- "outputs":serialized_outputs,
- "desk_data":universe.desk_data
- }
-
-func from(serialized_universe):
- universe.name = serialized_universe.name
- universe.uuid = serialized_universe.uuid
-
- for fixture_channel in serialized_universe.fixtures:
- var fixture = serialized_universe.fixtures[fixture_channel]
- var options = {
- "channel":int(fixture_channel),
- "mode":fixture.mode,
- "name":fixture.display_name,
- "quantity":1,
- "offset":0,
- "virtual_fixtures":fixture.get("virtual_fixtures", [])
- }
- new_fixture(Globals.fixtures[fixture.fixture_brand][fixture.fixture_name], options)
-
- for output_uuid in serialized_universe.outputs:
- var input = serialized_universe.outputs[output_uuid]
- match input.type:
- "Empty":
- universe.outputs[output_uuid] = EmptyInput.new()
- "Art-Net":
- universe.outputs[output_uuid] = Art_Net_Output.new()
- universe.outputs[output_uuid].from(input)
- universe.outputs[output_uuid].connect_to_host()
-
-func delete():
- for fixture in universe.fixtures.values():
- remove_fixture(fixture)
-
- for output in universe.outputs.values():
- remove_output(output)
-
diff --git a/Scripts/Global.gd b/Scripts/Global.gd
index 7d869d3..4c41b00 100644
--- a/Scripts/Global.gd
+++ b/Scripts/Global.gd
@@ -1,7 +1,5 @@
extends Node
-const uuid_util = preload('res://Scripts/Classes/Uuid.gd')
-const Universe = preload('res://Scripts/Classes/Universe.gd')
var node_path := "res://Nodes/"
var widget_path := "res://Widgets/"
@@ -35,7 +33,10 @@ var output_plugins : Dictionary
"accept_dialog":ResourceLoader.load("res://Components/Accept Dialog/Accept_dialog.tscn"),
"channel_slider":ResourceLoader.load("res://Components/Channel Slider/Channel_slider.tscn"),
"virtual_fixture":ResourceLoader.load("res://Components/Virtual Fixture/Virtual_fixture.tscn"),
- "window":ResourceLoader.load("res://Components/Window/Window.tscn")
+ "window":ResourceLoader.load("res://Components/Window/Window.tscn"),
+ "trigger_button":ResourceLoader.load("res://Components/Trigger Button/TriggerButton.tscn"),
+ "file_load_menu": ResourceLoader.load("res://Components/File Load Menu/FileLoadMenu.tscn"),
+ "file_save_menu": ResourceLoader.load("res://Components/File Save Menu/FileSaveMenu.tscn")
}
@onready var panels : Dictionary = {
@@ -52,49 +53,6 @@ var output_plugins : Dictionary
"window_control":ResourceLoader.load("res://Panels/Window Control/Window_control.tscn"),
}
-@onready var nodes := {
- ## General Nodes
- #"popup_window":get_tree().root.get_node("Main/Popups"),
- #"save_file_dialog":get_tree().root.get_node("Main/Save File Dialog"),
- #"add_node_popup":get_tree().root.get_node("Main/TabContainer/Node Editor/Node Editor/Add Node Popup"),
- #"add_widget_popup":get_tree().root.get_node("Main/TabContainer/Console/Console/Console Editor/Add Widget Popup"),
- #"widget_settings_menu":get_tree().root.get_node("Main/TabContainer/Console/Console/Widget Settings Menu"),
- #"edit_mode_toggle":get_tree().root.get_node("Main/Menu Buttons/Edit Mode"),
- #
- ##Node Editor
- #"node_editor":get_tree().root.get_node("Main/TabContainer/Node Editor/Node Editor"),
- #
- ##Console
- #"console_editor":get_tree().root.get_node("Main/TabContainer/Console/Console/Console Editor"),
- #
- #
- ## Functions Tab
- #"functions":get_tree().root.get_node("Main/TabContainer/Functions/Functions"),
- #"scenes_list":get_tree().root.get_node("Main/TabContainer/Functions/Functions/VBoxContainer/PanelContainer2/HBoxContainer/Scenes/ScrollContainer/VBoxContainer/Scenes"),
- #"effects_list":get_tree().root.get_node("Main/TabContainer/Functions/Functions/VBoxContainer/PanelContainer2/HBoxContainer/Effects/ScrollContainer/VBoxContainer/Effects"),
- #"cues_list":get_tree().root.get_node("Main/TabContainer/Functions/Functions/VBoxContainer/PanelContainer2/HBoxContainer/Cues/ScrollContainer/VBoxContainer/Cues"),
- #
-
- ## Fixtures Tab
- #"fixtures":get_tree().root.get_node("Main/TabContainer/Fixtures/Fixtures/"),
- #"physical_fixture_list":get_tree().root.get_node("Main/TabContainer/Fixtures/Fixtures/VBoxContainer/VSplitContainer/PanelContainer3/ScrollContainer/Physical Fixtures"),
- #"fixture_groups_list":get_tree().root.get_node("Main/TabContainer/Fixtures/Fixtures/VBoxContainer/VSplitContainer/PanelContainer2/ScrollContainer/Fixture Groups"),
- #
- ## Add Fixture Menu
- #"add_fixture_window":get_tree().root.get_node("Main/Add Fixture"),
- #"add_fixture_menu":get_tree().root.get_node("Main/Add Fixture/Add Fixture/"),
- #"fixture_tree":get_tree().root.get_node("Main/Add Fixture/Add Fixture/MarginContainer/HSplitContainer/Fixture Tree"),
- #"fixture_channel_list":get_tree().root.get_node("Main/Add Fixture/Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/Channel List"),
- #"fixture_modes_option":get_tree().root.get_node("Main/Add Fixture/Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer4/Modes"),
- #"fixture_universe_option":get_tree().root.get_node("Main/Add Fixture/Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer3/Fixture Universe Option"),
- #"add_fixture_button":get_tree().root.get_node("Main/Add Fixture/Add Fixture/MarginContainer/HSplitContainer/PanelContainer/VBoxContainer/HBoxContainer2/Add Fixture Button"),
- #
-
- ##Virtual Fixtures
- #"virtual_fixtures":get_tree().root.get_node("Main/TabContainer/Virtual Fixtures/HBoxContainer/Virtual Fixtures"),
- #"virtual_fixtures_sidebar":get_tree().root.get_node("Main/TabContainer/Virtual Fixtures/HBoxContainer/Sidebar"),
-}
-
@onready var icons := {
"menue":load("res://Assets/Icons/menu.svg"),
"center":load("res://Assets/Icons/Center.svg")
@@ -184,48 +142,48 @@ var output_plugins : Dictionary
}
func _ready() -> void:
- load_io_plugins()
- load_fixtures()
-
-func load_io_plugins() -> void:
- var output_plugin_folder : DirAccess = DirAccess.open(io_plugin_path + "Output Plugins")
- for plugin in output_plugin_folder.get_files():
- var uninitialized_plugin = ResourceLoader.load(io_plugin_path + "Output Plugins" + "/" + plugin)
- var initialized_plugin = uninitialized_plugin.new()
- var plugin_name: String = initialized_plugin.get_name()
-
- if plugin_name in output_plugins.keys():
- plugin_name = plugin_name + " " + new_uuid()
-
- output_plugins[plugin_name] = uninitialized_plugin
- initialized_plugin.free()
-
-func load_fixtures() -> void:
- var access = DirAccess.open(fixture_path)
+ pass
+ #load_fixtures()
- for fixture_folder in access.get_directories():
-
- for fixture in access.open(fixture_path+"/"+fixture_folder).get_files():
-
- var manifest_file = FileAccess.open(fixture_path+fixture_folder+"/"+fixture, FileAccess.READ)
- var manifest = JSON.parse_string(manifest_file.get_as_text())
-
- manifest.info.file_path = fixture_path+fixture_folder+"/"+fixture
-
- if fixtures.has(manifest.info.brand):
- fixtures[manifest.info.brand][manifest.info.name] = manifest
- else:
- fixtures[manifest.info.brand] = {manifest.info.name:manifest}
-
-func show_popup(content: Array[Dictionary] = []) -> void:
- for i in content:
- var node_to_add = components.warning.instantiate()
- node_to_add.get_node("HBoxContainer/VBoxContainer/Title").text = i.type.title
- node_to_add.get_node("HBoxContainer/VBoxContainer/Content").text = i.type.content + ". errcode: " + str(i.type.code) + ((" from: " + i.from) if i.has("from") else "")
- node_to_add.get_node("HBoxContainer/VBoxContainer/Time").text = Time.get_time_string_from_system()
- nodes.popup_window.get_node("VBoxContainer/PanelContainer/ScrollContainer/Content").add_child(node_to_add)
-
- nodes.popup_window.popup()
+#func load_io_plugins() -> void:
+ #var output_plugin_folder : DirAccess = DirAccess.open(io_plugin_path + "Output Plugins")
+ #for plugin in output_plugin_folder.get_files():
+ #var uninitialized_plugin = ResourceLoader.load(io_plugin_path + "Output Plugins" + "/" + plugin)
+ #var initialized_plugin = uninitialized_plugin.new()
+ #var plugin_name: String = initialized_plugin.get_name()
+ #
+ #if plugin_name in output_plugins.keys():
+ #plugin_name = plugin_name + " " + new_uuid()
+ #
+ #output_plugins[plugin_name] = uninitialized_plugin
+ #initialized_plugin.free()
+#
+#func load_fixtures() -> void:
+ #var access = DirAccess.open(fixture_path)
+ #
+ #for fixture_folder in access.get_directories():
+ #
+ #for fixture in access.open(fixture_path+"/"+fixture_folder).get_files():
+ #
+ #var manifest_file = FileAccess.open(fixture_path+fixture_folder+"/"+fixture, FileAccess.READ)
+ #var manifest = JSON.parse_string(manifest_file.get_as_text())
+ #
+ #manifest.info.file_path = fixture_path+fixture_folder+"/"+fixture
+ #
+ #if fixtures.has(manifest.info.brand):
+ #fixtures[manifest.info.brand][manifest.info.name] = manifest
+ #else:
+ #fixtures[manifest.info.brand] = {manifest.info.name:manifest}
+#
+#func show_popup(content: Array[Dictionary] = []) -> void:
+ #for i in content:
+ #var node_to_add = components.warning.instantiate()
+ #node_to_add.get_node("HBoxContainer/VBoxContainer/Title").text = i.type.title
+ #node_to_add.get_node("HBoxContainer/VBoxContainer/Content").text = i.type.content + ". errcode: " + str(i.type.code) + ((" from: " + i.from) if i.has("from") else "")
+ #node_to_add.get_node("HBoxContainer/VBoxContainer/Time").text = Time.get_time_string_from_system()
+ #nodes.popup_window.get_node("VBoxContainer/PanelContainer/ScrollContainer/Content").add_child(node_to_add)
+ #
+ #nodes.popup_window.popup()
func subscribe(value_name:String, callback:Callable) -> void:
if value_name in subscriptions:
@@ -250,50 +208,50 @@ func call_subscription(value_name:String) -> void:
if node_to_update.is_valid():
node_to_update.call()
-func new_uuid() -> String:
- return uuid_util.v4()
-
-func select_fixture(fixture_to_add:Fixture) -> Array:
-
- var active_fixtures = get_value("active_fixtures")
- if fixture_to_add not in active_fixtures:
- active_fixtures.append(fixture_to_add)
-
- set_value("active_fixtures", active_fixtures)
-
- return active_fixtures
-
-func deselect_fixture(fixture_to_remove:Fixture) -> Array:
- var active_fixtures = get_value("active_fixtures")
- active_fixtures.erase(fixture_to_remove)
-
- set_value("active_fixtures", active_fixtures)
- return active_fixtures
-
-func new_universe() -> Universe:
- var new_universe = Universe.new()
- universes[new_universe.get_uuid()] = new_universe
- return new_universe
-
-func delete_universe(universe:Universe) -> void:
- universe.delete()
- universes.erase(universe.get_uuid())
- universe.free()
-
- call_subscription("reload_universes")
- call_subscription("reload_fixtures")
-
-func serialize_universes() -> Dictionary:
- var serialized_universes = {}
- for universe_uuid in universes:
- serialized_universes[universe_uuid] = universes[universe_uuid].serialize()
- return serialized_universes
-
-func deserialize_universes(new_universes:Dictionary):
- for universe_uuid in new_universes:
- var universe_to_add = Universe.new()
- universe_to_add.from(new_universes[universe_uuid])
- universes[universe_uuid] = universe_to_add
+#func new_uuid() -> String:
+ #return UUID_Util.v4()
+#
+#func select_fixture(fixture_to_add:Fixture) -> Array:
+ #
+ #var active_fixtures = get_value("active_fixtures")
+ #if fixture_to_add not in active_fixtures:
+ #active_fixtures.append(fixture_to_add)
+#
+ #set_value("active_fixtures", active_fixtures)
+#
+ #return active_fixtures
+ #
+#func deselect_fixture(fixture_to_remove:Fixture) -> Array:
+ #var active_fixtures = get_value("active_fixtures")
+ #active_fixtures.erase(fixture_to_remove)
+ #
+ #set_value("active_fixtures", active_fixtures)
+ #return active_fixtures
+ #
+#func new_universe() -> Universe:
+ #var new_universe = Universe.new()
+ #universes[new_universe.get_uuid()] = new_universe
+ #return new_universe
+#
+#func delete_universe(universe:Universe) -> void:
+ #universe.delete()
+ #universes.erase(universe.get_uuid())
+ #universe.free()
+ #
+ #call_subscription("reload_universes")
+ #call_subscription("reload_fixtures")
+#
+#func serialize_universes() -> Dictionary:
+ #var serialized_universes = {}
+ #for universe_uuid in universes:
+ #serialized_universes[universe_uuid] = universes[universe_uuid].serialize()
+ #return serialized_universes
+#
+#func deserialize_universes(new_universes:Dictionary):
+ #for universe_uuid in new_universes:
+ #var universe_to_add = Universe.new()
+ #universe_to_add.from(new_universes[universe_uuid])
+ #universes[universe_uuid] = universe_to_add
func open_panel_in_window(panel_name:String) -> Variant:
if panel_name in panels:
diff --git a/Scripts/Menu_buttons.gd b/Scripts/Menu_buttons.gd
new file mode 100644
index 0000000..1ae91ea
--- /dev/null
+++ b/Scripts/Menu_buttons.gd
@@ -0,0 +1,29 @@
+extends HBoxContainer
+
+
+func _ready():
+ #Core.load(OS.get_environment("HOME") + "/Documents/Spectrum/Test1.spshow")
+ pass
+
+func _on_save_pressed() -> void:
+
+ var menu: FileDialog = Globals.components.file_save_menu.instantiate()
+
+ menu.confirmed.connect(
+ func():
+ print(Core.save(menu.current_file, menu.current_dir))
+ )
+
+ get_tree().root.add_child(menu)
+
+
+
+func _on_load_pressed() -> void:
+ var menu: FileDialog = Globals.components.file_load_menu.instantiate()
+
+ menu.confirmed.connect(
+ func():
+ Core.load(menu.current_path)
+ )
+
+ get_tree().root.add_child(menu)
diff --git a/Scripts/Node Config Editor/Code_edit.gd b/Scripts/Node Config Editor/Code_edit.gd
deleted file mode 100644
index e69de29..0000000
diff --git a/Scripts/Node Config Editor/Editor_window.gd b/Scripts/Node Config Editor/Editor_window.gd
deleted file mode 100644
index e5ba31d..0000000
--- a/Scripts/Node Config Editor/Editor_window.gd
+++ /dev/null
@@ -1,15 +0,0 @@
-extends Window
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready():
- pass # Replace with function body.
-
-
-# Called every frame. 'delta' is the elapsed time since the previous frame.
-func _process(delta):
- pass
-
-
-func _on_close_requested():
- self.hide()
diff --git a/Scripts/Node Config Editor/Node_config_editor.gd b/Scripts/Node Config Editor/Node_config_editor.gd
deleted file mode 100644
index 4960d10..0000000
--- a/Scripts/Node Config Editor/Node_config_editor.gd
+++ /dev/null
@@ -1,157 +0,0 @@
-extends TabContainer
-
-@onready var name_input = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/VBoxContainer/Name Input")
-@onready var template_node: GraphNode = self.get_node("Editor/Control/TemplateNode")
-@onready var list: ItemList = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/VBoxContainer/List")
-@onready var row_settings = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings")
-
-@onready var input_option_dropdown = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Options/Input Option")
-@onready var output_option_dropdown = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Options/Output Option")
-
-@onready var input_name_field = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Names/Input Name")
-@onready var output_name_field = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Names/Output Name")
-
-@onready var input_color_button = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Colors/Input Color")
-@onready var output_color_button = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Colors/Output Color")
-
-@onready var input_visable_button = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Visibility/Input Visable")
-
-@onready var input_slot_visable_button = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Slot Visibility/Input Slot Visable")
-@onready var output_slot_visable_button = self.get_node("Editor/PanelContainer/VBoxContainer/VSplitContainer/ScrollContainer/VBoxContainer/Row Settings/Slot Visibility/Output Slot Visable")
-
-var row_index = 0
-var row_select_index = 0
-var row_data = {}
-
-var input_types = {
- "none": null,
- "value": [[SpinBox,"Row$1 Value"]],
- "dmx_value": [[SpinBox, "Row$1 Channel"], [SpinBox,"Row$1 Value"]],
- "text": [[LineEdit, "Row$1 Text"]],
- "custom": []
-}
-# Called when the node enters the scene tree for the first time.
-func _ready():
- pass
-
-# Called every frame. 'delta' is the elapsed time since the previous frame.
-func _process(delta):
- pass
-
-func _on_name_input_text_changed(new_text):
- template_node.title = new_text
-
-func _on_new_row_pressed():
- var row_index = row_data.size() # Get the current size as the row index
- list.add_item("Row " + str(row_index))
-
- row_data[row_index] = {
- "input": false,
- "output": false,
- "input_type": "none",
- "output_type": "none",
- "input_name":"",
- "output_name":"",
- "input_visable":false,
- "input_slot_visable":true,
- "output_slot_visable":true,
- "row_name":""
- }
-
- update_rows()
-
-func _on_delete_row_pressed():
- var selected_items = list.get_selected_items()
- # Remove items in reverse order to avoid index issues
- for i in range(selected_items.size() - 1, -1, -1):
- row_data.erase(selected_items[i])
- list.remove_item(selected_items[i])
- #template_node.remove_child(template_node.get_child(selected_items[i]))
- update_rows()
-
-func update_rows():
- template_node.clear_all_slots()
- for n in template_node.get_children():
- template_node.remove_child(n)
- n.queue_free()
-
- for index in row_data:
- var new_row = HBoxContainer.new()
- new_row.name = "row" + str(index)
- #new_row.set_h_size_flags(2)
- template_node.add_child(new_row)
- var current_row = template_node.get_node("row"+str(index))
-
- template_node.set_slot(index, row_data[index].input,0,Color.WHITE,row_data[index].output, 0, Color.WHITE)
-
- if row_data[index].input_name:
- var node_to_add = Label.new()
- node_to_add.text = row_data[index].input_name
- node_to_add.set_h_size_flags(3)
- current_row.add_child(node_to_add)
-
- if row_data[index].input_type != "none":
- if row_data[index].input_visable:
- for node_to_add in input_types[row_data[index].input_type]:
- var new_node = node_to_add[0].new()
- new_node.set_name(node_to_add[1].replace("$1", str(index)))
- if row_data[index].input_name:
- new_node.set_h_size_flags(2)
- new_node.set_h_size_flags(8)
- else:
- new_node.set_h_size_flags(3)
- current_row.add_child(new_node)
-
- if row_data[index].output_name:
- var node_to_add = Label.new()
- node_to_add.text = row_data[index].output_name
- node_to_add.set_h_size_flags(2)
- node_to_add.set_h_size_flags(8)
- current_row.add_child(node_to_add)
-
-
-func _on_list_multi_selected(index, selected):
- row_select_index = index
- row_settings.visible = true
-
-func _on_input_option_item_selected(index):
- row_data[row_select_index].input_type = input_types.keys()[index]
- input_name_field.text = ""
- row_data[row_select_index].input_name = ""
- update_rows()
-
-func _on_output_option_item_selected(index):
- row_data[row_select_index].output_type = input_types.keys()[index]
- output_name_field.text = ""
- row_data[row_select_index].output_name = ""
- update_rows()
-
-func _on_input_name_text_changed(new_text):
- row_data[row_select_index].input_name = new_text
- update_rows()
-
-func _on_output_name_text_changed(new_text):
- row_data[row_select_index].output_name = new_text
- update_rows()
-
-func _on_input_color_color_changed(color):
- pass # Replace with function body.
-
-func _on_output_color_color_changed(color):
- pass # Replace with function body.
-
-func _on_input_slot_visable_toggled(toggled_on):
- row_data[row_select_index].input = toggled_on
- update_rows()
-
-func _on_output_slot_visable_toggled(toggled_on):
- row_data[row_select_index].output = toggled_on
- update_rows()
-
-func _on_input_visable_toggled(toggled_on):
- row_data[row_select_index].input_visable = toggled_on
- update_rows()
-
-func _on_output_visable_toggled(toggled_on):
- pass # Replace with function body.
-
diff --git a/Scripts/Node Config Editor/Template_node.gd b/Scripts/Node Config Editor/Template_node.gd
deleted file mode 100644
index 146c195..0000000
--- a/Scripts/Node Config Editor/Template_node.gd
+++ /dev/null
@@ -1,13 +0,0 @@
-extends GraphNode
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready():
- pass # Replace with function body.
-
-func _on_resize_request(new_minsize):
- size = new_minsize
-
-# Called every frame. 'delta' is the elapsed time since the previous frame.
-func _process(delta):
- pass
diff --git a/Scripts/Node Config Editor/What is this.txt b/Scripts/Node Config Editor/What is this.txt
deleted file mode 100644
index 0be2c65..0000000
--- a/Scripts/Node Config Editor/What is this.txt
+++ /dev/null
@@ -1 +0,0 @@
-If you're wondering what the 'Node Config Editor' is, it is a GUI that will allow you to create your own nodes using logic. Development on this has been postponed to a later release, but when that will be, no one knows.
diff --git a/Scripts/Sidebar.gd b/Scripts/Sidebar.gd
index 8d6f962..bca643d 100644
--- a/Scripts/Sidebar.gd
+++ b/Scripts/Sidebar.gd
@@ -16,4 +16,4 @@ func _on_button_pressed() -> void:
#contence.visible = false
#else:
#contence.visible = true
- is_open = not is_open
+ is_open = not contence.visible
diff --git a/Scripts/System.gd b/Scripts/System.gd
deleted file mode 100644
index bf02279..0000000
--- a/Scripts/System.gd
+++ /dev/null
@@ -1,143 +0,0 @@
-extends Control
-
-var current_file = ""
-var home_directory = OS.get_environment("HOME")
-var save_folder_path = home_directory + "/Documents/Spectrum/"
-var file_name = ""
-var save_file = {
- "nodes":{},
- "node_connections":{},
- "widgets":{}
- }
-
-func _ready():
- OS.set_low_processor_usage_mode(true)
-
-func _on_save_pressed():
- save()
-
-func save():
- save_nodes()
- save_widgets()
- save_file.universes = Globals.serialize_universes()
- Globals.nodes.save_file_dialog.popup()
-
-func save_nodes():
- for node_to_save in Globals.nodes.node_editor.get_children():
- var node_file_path = node_to_save.get_meta("node_file_path")
- if not node_file_path: continue
- var manifest_file = FileAccess.open(node_file_path+"manifest.json", FileAccess.READ)
- if not manifest_file:
- Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_MANIFEST,"from":node_file_path}])
- return
- var manifest = JSON.parse_string(manifest_file.get_as_text())
- save_file.nodes[node_to_save.name] = {
- "node_file_path":node_file_path,
- "uuid":manifest.uuid,
- "name":node_to_save.name,
- "position_offset":[node_to_save.position_offset.x,node_to_save.position_offset.y],
- "values":{
-
- }
- }
- for key in manifest.values.keys():
-
- if not node_to_save.get_node(manifest.values[key].node):
- Globals.show_popup([{"type":Globals.error.NODE_SAVE_MANIFEST_ERROR,"from":node_file_path}])
- return
- save_file.nodes[node_to_save.name].values[key] = node_to_save.get_node(manifest.values[key].node).get(manifest.values[key].content)
- save_file.node_connections = Globals.nodes.node_editor.connected_nodes
-
-func save_widgets():
- for widget_to_save in Globals.nodes.console_editor.get_children():
- var widget_file_path = widget_to_save.get_meta("widget_file_path")
- if not widget_file_path: continue
- var manifest_file = FileAccess.open(widget_file_path+"manifest.json", FileAccess.READ)
- if not manifest_file:
- Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_MANIFEST,"from":widget_file_path}])
- return
- var manifest = JSON.parse_string(manifest_file.get_as_text())
- save_file.widgets[widget_to_save.name] = {
- "widget_file_path":widget_file_path,
- "uuid":manifest.uuid,
- "name":widget_to_save.name,
- "position_offset":[widget_to_save.position_offset.x,widget_to_save.position_offset.y],
- "size":[widget_to_save.size.x,widget_to_save.size.y],
- "values":{
-
- }
- }
-
- for key in manifest.values.keys():
- if not widget_to_save.get_node(manifest.values[key].node):
- Globals.show_popup([{"type":Globals.error.NODE_SAVE_MANIFEST_ERROR,"from":widget_file_path}])
- return
- save_file.widgets[widget_to_save.name].values[key] = widget_to_save.get_node(manifest.values[key].node).get(manifest.values[key].content)
- save_file.node_connections = Globals.nodes.node_editor.connected_nodes
-
-func _on_save_file_dialog_file_selected(path):
- var input_file_name = Globals.nodes.save_file_dialog.current_file
-
- if not input_file_name:
- input_file_name = "New Save"
- file_name = input_file_name
-
- save_json_to_file(path, save_file, file_name)
-
-func save_json_to_file(folder_path, save_data, _file_name):
-
- var file_access = FileAccess.open(folder_path, FileAccess.WRITE)
-
- file_access.store_string(JSON.stringify(save_data, "\t"))
- file_access.close()
-
-func _on_load_pressed():
- get_node("Load File Dialog").popup()
-
-func load_save(file_path):
- # Check if save file is valid
- var manifest_file = FileAccess.open(file_path, FileAccess.READ)
- if manifest_file == null:
- Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_FILE,"from":file_path}])
- return
- var manifest = JSON.parse_string(manifest_file.get_as_text())
- if manifest == null:
- Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_MANIFEST,"from":file_path}])
- return
-
- # Add Nodes
- for node_to_add in manifest.nodes.values():
- var node_manifest_file = FileAccess.open(node_to_add.node_file_path + "manifest.json", FileAccess.READ)
- if manifest == null:
- Globals.show_popup([{"type":Globals.error.MISSING_NODES,"from":node_manifest_file}])
- return
- Globals.nodes.node_editor._add_node(node_to_add.node_file_path, {"position_offset":node_to_add.position_offset, "values":node_to_add.values})
- # Add node connections
- Globals.nodes.node_editor.generate_connected_nodes(manifest.node_connections)
-
- # Add Widgets
- for widget_to_add in manifest.widgets.values():
- var widget_manifest_file = FileAccess.open(widget_to_add.widget_file_path + "manifest.json", FileAccess.READ)
- if manifest == null:
- Globals.show_popup([{"type":Globals.error.MISSING_NODES,"from":widget_manifest_file}])
- return
- Globals.nodes.console_editor._add_widget(widget_to_add.widget_file_path, {"position_offset":widget_to_add.get("position_offset"), "values":widget_to_add.get("values"), "size":widget_to_add.get("size")})
-
- #Add Universes
- Globals.deserialize_universes(manifest.universes)
- Globals.call_subscription("reload_universes")
- Globals.call_subscription("reload_fixtures")
-
-func _on_load_file_dialog_file_selected(path):
- load_save(path)
-
-func _on_edit_mode_toggled(toggled_on):
- Globals.set_value("edit_mode", not toggled_on)
- if toggled_on:
- Globals.nodes.edit_mode_toggle.text = "Play Mode"
- else:
- Globals.nodes.edit_mode_toggle.text = "Edit Mode"
-
-
-func on_keypad_button_pressed(extra_arg_0):
- pass # Replace with function body.
diff --git a/core/components/EngineComponent.gd b/core/components/EngineComponent.gd
new file mode 100644
index 0000000..bb779a5
--- /dev/null
+++ b/core/components/EngineComponent.gd
@@ -0,0 +1,94 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name EngineComponent extends Object
+## Base class for an engine components, contains functions for storing metadata, and uuid's
+
+signal user_meta_changed(origin: EngineComponent, key: String, value: Variant) ## Emitted when an item is added, edited, or deleted from user_meta, if no value is present it meanes that the key has been deleted
+signal name_changed(new_name: String) ## Emitted when the name of this object has changed
+signal selected(is_selected: bool)
+signal delete_request(origin: EngineComponent)
+
+var uuid: String = "" ## Uuid of the current component
+var name: String = "": set = _set_name ## The name of this object, only use when displaying to users, do not use it as a reference
+var user_meta: Dictionary ## Infomation that can be stored by other scripts
+
+var is_selected: bool = false: set = set_selected
+
+
+func _init() -> void:
+
+ if not uuid:
+ uuid = UUID_Util.v4()
+
+
+func set_selected(state: bool) -> void:
+ ## Sets the selection state of this component
+
+ is_selected = state
+ selected.emit(state)
+
+
+func set_user_meta(key: String, value: Variant, no_signal: bool = false):
+ ## Sets user_meta from the given value
+
+ user_meta[key] = value
+
+ if not no_signal:
+ user_meta_changed.emit(self, key, value)
+
+
+func get_user_meta(key: String, default = null) -> Variant:
+ ## Returns user_meta from the given key, if the key is not found, default is returned
+
+ return user_meta.get(key, default)
+
+
+func delete_user_meta(key: String, no_signal: bool = false) -> bool:
+ ## Delets an item from user-meta, returning true if item was found and deleted, and false if not
+
+ if not no_signal:
+ user_meta_changed.emit(self, key)
+
+ return user_meta.erase(key)
+
+
+func _set_name(new_name) -> void:
+ name = new_name
+
+
+func change_name(new_name: String, no_signal: bool = false) -> void:
+ ## Changes the name of this object
+ name = new_name
+
+ if not no_signal:
+ name_changed.emit(new_name)
+
+
+func serialize() -> Dictionary:
+ ## When this object gets serialize all user metadata that is an Object, will be checked for a uuid propity.
+ ## If one is present it will be used as the serialized version of that object. If none is present, it will be ignored.
+ ## Other user_meta that is not an object will be directly added to the serialized data.
+
+ return {
+ "uuid":uuid,
+ "user_meta":serialize_meta()
+ }
+
+
+func serialize_meta() -> Dictionary:
+ ## Returnes serialized user_meta
+
+ var serialized_user_meta: Dictionary = {}
+
+ for key: String in user_meta:
+ if user_meta[key] is Object and "uuid" in user_meta[key]:
+ serialized_user_meta[key] = user_meta[key].uuid
+ else:
+ serialized_user_meta[key] = user_meta[key]
+
+ return serialized_user_meta
+
+
+func delete() -> void:
+ delete_request.emit(self)
diff --git a/core/components/Fixture.gd b/core/components/Fixture.gd
new file mode 100644
index 0000000..2bd8a86
--- /dev/null
+++ b/core/components/Fixture.gd
@@ -0,0 +1,140 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name Fixture extends EngineComponent
+## Engine class to control parameters of fixtures
+
+signal color_changed(color: Color) ## Emitted when the color of this fixture is changed
+signal mode_changed(mode: int) ## Emitted when the mode of the fixture is changed
+signal channel_changed(new_channel: int) ## Emitted when the channel of the fixture is changed
+
+## Contains metadata infomation about this fixture
+var meta: Dictionary = {
+ "fixture_brand":"",
+ "fixture_name":"",
+ "display_name":"",
+}
+
+var universe: Universe ## The universe this fixture is patched to
+var channel: int ## Universe channel of this fixture
+var length: int ## Channel length, from start channel, to end channel
+var mode: int ## Current mode
+var manifest: Dictionary ## Fixture manifest
+var channels: Array ## Channels this fixture uses, and what they do
+var channel_ranges: Dictionary ## What happenes at each channel, at each value
+
+var position: Vector2 = Vector2.ZERO
+
+
+## Contains all the parameters inputted by other function in spectrum, ie scenes, programmer, ect.
+## Each input it added to this dict with a id for each item, allowing for HTP and LTP calculations
+var current_input_data: Dictionary = {}
+
+var _compiled_dmx_data: Dictionary
+
+
+func _init(i: Dictionary = {}) -> void:
+ ## Init function to create a new fixture, from a set of prexisting infomation. If no infomation is passed a blank fixture is returne
+
+ if not i:
+ return
+
+ universe = i.universe as Universe
+ channel = i.channel as int
+ mode = i.mode as int
+ length = len(i.manifest.modes.values()[mode].channels)
+ manifest = i.manifest as Dictionary
+ channel_ranges = i.manifest.get("channels", {})
+ channels = i.manifest.modes.values()[mode].channels
+
+ var p = Utils.deserialize_variant(i.get("position", ""))
+ if p is Vector2:
+ position = p
+
+ meta.fixture_brand = i.manifest.info.brand
+ meta.fixture_name = i.manifest.info.name
+
+ self.name = i.manifest.info.brand + " | " + i.manifest.info.name
+
+ if "uuid" in i:
+ self.uuid = i.uuid
+
+ self.name_changed.connect(
+ func(new_name: String):
+ universe.fixture_name_changed.emit(self, new_name)
+ )
+
+ super._init()
+
+func serialize() -> Dictionary:
+ ## Returnes serialized infomation about this fixture
+ print(uuid)
+ return {
+ "universe":universe.uuid,
+ "channel":channel,
+ "mode":mode,
+ "position":Utils.serialize_variant(position),
+ "meta":meta,
+ "user_meta": serialize_meta(),
+ }
+
+
+func recompile_data() -> void:
+ ## Compiles dmx data from this fixture
+
+ var highest_valued_data: Dictionary = {}
+
+ for input_data_id in current_input_data:
+ for input_data in current_input_data[input_data_id]:
+ match input_data:
+ "color":
+ highest_valued_data["color"] = Utils.get_htp_color(highest_valued_data.get("color", Color()), current_input_data[input_data_id].color)
+
+ _set_color(highest_valued_data.get("color", Color.BLACK))
+
+ universe.set_data(_compiled_dmx_data)
+
+
+func delete() -> void:
+ delete_request.emit(self)
+
+ var empty_data: Dictionary = {}
+
+ for i in _compiled_dmx_data:
+ empty_data[i] = 0
+
+ universe.set_data(empty_data)
+
+
+func _set_color(color: Color) -> void:
+ if "ColorIntensityRed" in channels:
+ _compiled_dmx_data[int(channels.find("ColorIntensityRed") + channel)] = color.r8
+ if "ColorIntensityGreen" in channels:
+ _compiled_dmx_data[int(channels.find("ColorIntensityGreen") + channel)] = color.g8
+ if "ColorIntensityBlue" in channels:
+ _compiled_dmx_data[int(channels.find("ColorIntensityBlue") + channel)] = color.b8
+
+ color_changed.emit(color)
+
+
+func set_color(color: Color, id: String = "overide") -> void:
+ ## Sets the color of this fixture
+
+ if color == Color.BLACK:
+ _remove_current_input_data(id, "color")
+ else:
+ _add_current_input_data(id, "color", color)
+
+ recompile_data()
+
+
+func _add_current_input_data(id: String, key: String, value: Variant) -> void:
+ if id not in current_input_data:
+ current_input_data[id] = {}
+ current_input_data[id][key] = value
+
+
+func _remove_current_input_data(id: String, key: String) -> void:
+ current_input_data.get("id", {}).erase(key)
+ if not current_input_data.get("id", false):
+ current_input_data.erase(id)
diff --git a/core/components/Programmer.gd b/core/components/Programmer.gd
new file mode 100644
index 0000000..f54550a
--- /dev/null
+++ b/core/components/Programmer.gd
@@ -0,0 +1,33 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name Programmer extends EngineComponent
+## Engine class for programming lights, colors, positions, etc. This also handles conversion from Godots color system to dmx colors
+
+var save_data: Dictionary = {} ## Current data in the programmer
+
+var engine: CoreEngine ## The CoreEngine this programmer is atached to
+
+func set_color(fixtures: Array[Fixture], color: Color) -> void:
+ ## Sets the color of all the fixtures in fixtures, to color
+
+ for fixture: Fixture in fixtures:
+ fixture.set_color(color)
+
+ if fixture not in save_data:
+ save_data[fixture] = {}
+
+ save_data[fixture].color = color
+
+
+func save_to_scene(name: String = "New Scene") -> Scene:
+ ## Saves the current state of this programmer to a scene
+
+ var new_scene: Scene = Scene.new()
+
+ new_scene.set_save_data(save_data.duplicate(true))
+ new_scene.name = name
+
+ engine.new_scene(new_scene)
+
+ return new_scene
diff --git a/core/components/Scene.gd b/core/components/Scene.gd
new file mode 100644
index 0000000..997dd2f
--- /dev/null
+++ b/core/components/Scene.gd
@@ -0,0 +1,87 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name Scene extends EngineComponent
+## Engine class for creating and recalling saved data
+
+signal state_changed(is_enabled: bool) ## Emmitted when this scene is enabled or dissabled
+
+var fade_in_speed: int = 2 ## Fade in speed in seconds
+var fade_out_speed: int = 2 ## Fade out speed in seconds
+
+var engine: CoreEngine ## The CoreEngine this scene is a part of
+
+var enabled: bool = false: set = set_enabled ## The current state of this scene
+var save_data: Dictionary = {} ## Saved data for this scene
+
+
+func set_enabled(is_enabled: bool) -> void:
+ ## Enabled or dissables this scene
+
+ enabled = is_enabled
+
+ if is_enabled:
+ for fixture: Fixture in save_data:
+ Core.animate(func(color): fixture.set_color(color, uuid), Color.BLACK, save_data[fixture].color, fade_in_speed)
+ else:
+ for fixture: Fixture in save_data:
+ Core.animate(func(color): fixture.set_color(color, uuid), fixture.current_input_data[uuid].color, Color.BLACK, fade_out_speed)
+
+
+func set_save_data(saved_data: Dictionary) -> void:
+ save_data = saved_data
+
+ for fixture: Fixture in save_data.keys():
+ fixture.delete_request.connect(func(deleted_fixture: Fixture): save_data.erase(deleted_fixture))
+
+
+func serialize() -> Dictionary:
+ ## Serializes this scene and returnes it in a dictionary
+
+ return {
+ "name": self.name,
+ "fade_in_speed": fade_in_speed,
+ "fade_out_speed": fade_out_speed,
+ "save_data": serialize_save_data()
+ }
+
+
+func load_from(serialized_data: Dictionary) -> void:
+
+ self.name = serialized_data.get("name", "")
+
+ fade_in_speed = serialized_data.get("fade_in_speed", fade_in_speed)
+ fade_out_speed = serialized_data.get("fade_out_speed", fade_out_speed)
+
+ set_save_data(deserialize_save_data(serialized_data.get("save_data", {})))
+
+
+func serialize_save_data() -> Dictionary:
+ ## Serializes save_data and returnes as a dictionary
+
+ var serialized_save_data: Dictionary = {}
+
+ for fixture: Fixture in save_data:
+ serialized_save_data[fixture.uuid] = {}
+ for save_key in save_data[fixture]:
+ serialized_save_data[fixture.uuid][save_key] = Utils.serialize_variant(save_data[fixture][save_key])
+
+ return serialized_save_data
+
+
+func deserialize_save_data(serialized_data: Dictionary) -> Dictionary:
+ ## Deserializes save_data and returnes as a dictionary
+
+ var deserialized_save_data: Dictionary = {}
+
+ for fixture_uuid: String in serialized_data:
+ var fixture_save: Dictionary = serialized_data[fixture_uuid]
+
+ var deserialized_fixture_save = {}
+
+ for saved_property: String in fixture_save:
+ deserialized_fixture_save[saved_property] = Utils.deserialize_variant(fixture_save[saved_property])
+
+ deserialized_save_data[engine.fixtures[fixture_uuid]] = deserialized_fixture_save
+
+ return deserialized_save_data
diff --git a/core/components/Universe.gd b/core/components/Universe.gd
new file mode 100644
index 0000000..13b682b
--- /dev/null
+++ b/core/components/Universe.gd
@@ -0,0 +1,266 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name Universe extends EngineComponent
+## Enngie class for handling universes, and there outputs
+
+signal fixture_name_changed(fixture: Fixture, new_name: String)
+signal fixtures_added(fixtures: Array[Fixture])
+signal fixtures_deleted(fixture_uuids: Array)
+
+signal outputs_added(outputs: Array[DataIOPlugin])
+signal outputs_removed(output_uuids: Array[String])
+
+var fixtures: Dictionary = {} ## Dictionary containing all the fixtures in this universe
+var outputs: Dictionary = {} ## Dictionary containing all the outputs in this universe
+
+var dmx_data: Dictionary = {}
+
+var engine: CoreEngine ## The CoreEngine class this universe belongs to
+
+var last_call_time: float = 0.0
+
+func new_output(type = ArtNetOutput, no_signal: bool = false) -> DataIOPlugin:
+ ## Adds a new output of type to this universe
+
+ var new_output: DataIOPlugin = type.new()
+
+ engine.output_timer.connect(new_output.send_packet)
+
+ outputs[new_output.uuid] = new_output
+
+ if not no_signal:
+ outputs_added.emit([new_output])
+
+ return new_output
+
+
+func remove_output(output: DataIOPlugin, no_signal: bool = false) -> void:
+ ## Removes an output
+
+ var uuid = output.uuid
+ outputs.erase(uuid)
+
+ output.delete()
+ output.free()
+
+ if not no_signal:
+ outputs_removed.emit([uuid])
+
+
+func remove_outputs(outputs_to_remove: Array, no_signal: bool = false):
+ ## Remove more than one output at once
+
+ var uuids: Array[String] = []
+
+ for output: DataIOPlugin in outputs_to_remove:
+ uuids.append(output.uuid)
+ remove_output(output, true)
+
+ if not no_signal:
+ outputs_removed.emit(uuids)
+
+
+func new_fixture(manifest: Dictionary, mode:int, channel: int = -1, quantity:int = 1, offset:int = 0, uuid: String = "", no_signal: bool = false) -> bool:
+ ## Adds a new fixture to this universe, if the channels are already in use false it returned
+
+ if is_channel_used(range(channel, len(manifest.modes.values()[mode].channels))):
+ return false
+
+ var just_added_fixtures: Array[Fixture] = []
+
+ for i: int in range(quantity):
+ var channel_index = channel + offset
+ channel_index += (len(manifest.modes.values()[mode].channels)) * i
+
+ var new_fixture = Fixture.new({
+ "universe": self,
+ "channel": channel_index,
+ "mode": mode,
+ "manifest": manifest
+ })
+
+
+ fixtures[channel_index] = new_fixture
+ engine.fixtures[new_fixture.uuid] = new_fixture
+ just_added_fixtures.append(new_fixture)
+
+ if not no_signal:
+ fixtures_added.emit(just_added_fixtures)
+
+ return true
+
+func remove_fixture(fixture: Fixture, no_signal: bool = false):
+ ## Removes a fixture from this universe
+
+ var fixture_uuid: String = fixture.uuid
+
+ if fixture in engine.selected_fixtures:
+ engine.deselect_fixtures([fixture])
+
+ fixtures.erase(fixture.channel)
+ engine.fixtures.erase(fixture.uuid)
+ fixture.delete()
+ fixture.free()
+
+ if not no_signal:
+ fixtures_deleted.emit([fixture_uuid])
+
+
+func remove_fixtures(fixtures_to_remove: Array, no_signal: bool = false) -> void:
+ ## Removes mutiple fixtures at once
+
+ var uuids: Array = []
+
+ for fixture: Fixture in fixtures_to_remove:
+ uuids.append(fixture.uuid)
+ remove_fixture(fixture, true)
+
+ if not no_signal:
+ fixtures_deleted.emit(uuids)
+
+
+func is_channel_used(channels: Array) -> bool:
+ ## Checks if any of the channels in channels are used by another fixture
+ return false
+
+
+func delete():
+ ## Called when this universe is about to be deleted, it will remove all outputs and fixtures from this universe
+
+ remove_fixtures(fixtures.values())
+ remove_outputs(outputs.values())
+
+
+func set_data(data: Dictionary):
+ ## Set dmx data, layers will be added soom
+ dmx_data.merge(data, true)
+ _compile_and_send()
+
+
+func _compile_and_send():
+ #var current_time = Time.get_ticks_msec() / 1000.0 # Convert milliseconds to seconds
+ #
+ #if current_time - last_call_time >= Core.call_interval:
+ var compiled_dmx_data: Dictionary = dmx_data
+ for output in outputs.values():
+ output.set_data(compiled_dmx_data)
+
+
+ #last_call_time = current_time
+
+
+func serialize() -> Dictionary:
+ ## Serializes this universe
+
+ var serialized_outputs = {}
+ var serialized_fixtures = {}
+
+ for output: DataIOPlugin in outputs.values():
+ serialized_outputs[output.uuid] = output.serialize()
+
+ for fixture: Fixture in fixtures.values():
+ serialized_fixtures[fixture.uuid] = fixture.serialize()
+
+
+ return {
+ "name":name,
+ "fixtures":serialized_fixtures,
+ "outputs":serialized_outputs,
+ "user_meta":serialize_meta()
+ }
+
+
+func load_from(serialised_data: Dictionary) -> void:
+ ## Loads this universe from a serialised universe
+
+ self.name = serialised_data.get("name", "")
+
+ fixtures = {}
+ outputs = {}
+
+ for fixture_uuid: String in serialised_data.get("fixtures", {}):
+ var serialised_fixture: Dictionary = serialised_data.fixtures[fixture_uuid]
+
+ var fixture_brand: String = serialised_fixture.get("meta", {}).get("fixture_brand", "Generic")
+ var fixture_name: String = serialised_fixture.get("meta", {}).get("fixture_name", "Dimmer")
+
+ var fixture_manifest: Dictionary = Core.fixtures_definitions[fixture_brand][fixture_name]
+ var channel: int = serialised_fixture.get("channel", 1)
+
+ var new_fixture = Fixture.new({
+ "universe": self,
+ "channel": channel,
+ "mode": serialised_fixture.get("mode", 0),
+ "manifest": fixture_manifest,
+ "uuid": fixture_uuid
+ })
+
+
+ fixtures[channel] = new_fixture
+ engine.fixtures[new_fixture.uuid] = new_fixture
+
+
+ fixtures_added.emit(fixtures.values())
+
+
+ for output_uuid: String in serialised_data.get("outputs"):
+ var serialised_output: Dictionary = serialised_data.outputs[output_uuid]
+
+ var new_output: DataIOPlugin = engine.output_plugins[serialised_output.file].plugin.new(serialised_output)
+ new_output.uuid = output_uuid
+ engine.output_timer.connect(new_output.send_packet)
+
+
+ outputs[new_output.uuid] = new_output
+
+ outputs_added.emit(outputs.values())
+
+
+#func get_fixtures():
+ #return universe.fixtures
+ #
+#func get_fixture(fixture_uuid):
+ #pass
+#
+
+
+
+
+#
+#func set_desk_data(dmx_data):
+ #universe.desk_data.merge(dmx_data)
+ #_compile_and_send()
+#
+#func get_desk_data():
+ #return universe.desk_data
+#
+
+
+#
+#func from(serialized_universe):
+ #universe.name = serialized_universe.name
+ #universe.uuid = serialized_universe.uuid
+ #
+ #for fixture_channel in serialized_universe.fixtures:
+ #var fixture = serialized_universe.fixtures[fixture_channel]
+ #var options = {
+ #"channel":int(fixture_channel),
+ #"mode":fixture.mode,
+ #"name":fixture.display_name,
+ #"quantity":1,
+ #"offset":0,
+ #"virtual_fixtures":fixture.get("virtual_fixtures", [])
+ #}
+ #new_fixture(Globals.fixtures[fixture.fixture_brand][fixture.fixture_name], options)
+ #
+ #for output_uuid in serialized_universe.outputs:
+ #var input = serialized_universe.outputs[output_uuid]
+ #match input.type:
+ #"Empty":
+ #universe.outputs[output_uuid] = EmptyInput.new()
+ #"Art-Net":
+ #universe.outputs[output_uuid] = ArtNetOutput.new()
+ #universe.outputs[output_uuid].from(input)
+ #universe.outputs[output_uuid].connect_to_host()
+#
diff --git a/core/engine/Engine.gd b/core/engine/Engine.gd
new file mode 100644
index 0000000..b109c1a
--- /dev/null
+++ b/core/engine/Engine.gd
@@ -0,0 +1,361 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name CoreEngine extends Node
+## The core engine that powers Spectrum
+
+signal universe_name_changed(universe: Universe, new_name: String) ## Emitted when any of the universes in this engine have there name changed
+signal universes_added(universe: Array[Universe])
+signal universes_removed(universe_uuids: Array[String])
+signal universe_selection_changed(selected_universes: Array[Universe])
+
+signal fixture_name_changed(fixture: Fixture, new_name)
+signal fixture_added(fixture: Array[Fixture])
+signal fixture_removed(fixture_uuid: Array)
+signal fixture_selection_changed(selected_fixtures: Array[Fixture])
+
+signal scenes_added(scene: Array[Scene])
+signal scenes_removed(scene_uuids: Array)
+
+signal output_timer() ## Emited [call_interval] number of times per second
+
+var universes: Dictionary = {}
+var fixtures: Dictionary = {}
+var fixtures_definitions: Dictionary = {}
+var scenes: Dictionary = {}
+var selected_fixtures: Array[Fixture] = []
+var selected_universes: Array[Universe] = []
+
+var input_plugins: Dictionary = {}
+var output_plugins: Dictionary = {}
+
+const fixture_path: String = "res://core/fixtures/"
+const input_plugin_path: String = "res://core/io_plugins/input_plugins/"
+const output_plugin_path: String = "res://core/io_plugins/output_plugins/"
+
+var current_file_name: String = ""
+var current_file_path: String = ""
+
+var programmer = Programmer.new()
+
+var call_interval: float = 1.0 / 45.0 # 1 second divided by 45
+
+var accumulated_time: float = 0.0
+
+func _ready() -> void:
+ programmer.engine = self
+
+ OS.set_low_processor_usage_mode(true)
+ reload_io_plugins()
+ reload_fixtures()
+
+
+func _process(delta: float) -> void:
+ # Accumulate the time
+ accumulated_time += delta
+
+ # Check if enough time has passed since the last function call
+ if accumulated_time >= call_interval:
+ # Call the function
+ output_timer.emit()
+
+ # Subtract the interval from the accumulated time
+ accumulated_time -= call_interval
+
+
+#region Save Load
+func save(file_name: String = current_file_name, file_path: String = current_file_name) -> Error:
+ var save_file: Dictionary = {}
+
+ save_file.universes = serialize_universes()
+ save_file.scenes = serialize_scenes()
+
+ return Utils.save_json_to_file(file_path, file_name, save_file)
+
+
+func load(file_path) -> void:
+ ## Loads a save file and deserialize the data
+
+ var saved_file = FileAccess.open(file_path, FileAccess.READ)
+ var serialized_data: Dictionary = JSON.parse_string(saved_file.get_as_text())
+
+ ## Loops through each universe in the save file (if any), and loads them into the engine
+ for universe_uuid: String in serialized_data.get("universes", {}):
+ var serialized_universe: Dictionary = serialized_data.universes[universe_uuid]
+
+ var new_universe: Universe = new_universe(serialized_universe.name, false, serialized_universe, universe_uuid)
+ universes[new_universe.uuid] = new_universe
+
+ for scene_uuid: String in serialized_data.get("scenes", {}):
+ var serialized_scene: Dictionary = serialized_data.scenes[scene_uuid]
+
+ var new_scene: Scene = new_scene(Scene.new(), true, serialized_scene, scene_uuid)
+
+ scenes_added.emit(scenes)
+#endregion
+
+
+#region Universes
+func new_universe(name: String = "New Universe", no_signal: bool = false, serialised_data: Dictionary = {}, uuid: String = "") -> Universe:
+ ## Adds a new universe
+
+ var new_universe: Universe = Universe.new()
+
+ new_universe.engine = self
+
+ if serialised_data:
+ new_universe.load_from(serialised_data)
+ else:
+ new_universe.name = name
+
+ if uuid:
+ new_universe.uuid = uuid
+
+ universes[new_universe.uuid] = new_universe
+
+ if not no_signal:
+ universes_added.emit([new_universe])
+
+ _connect_universe_signals(new_universe)
+
+ return new_universe
+
+
+func _connect_universe_signals(universe: Universe):
+ ## Connects all the signals of the new universe to the signals of this engine
+
+ universe.name_changed.connect(
+ func(new_name: String):
+ universe_name_changed.emit(universe, new_name)
+ )
+
+ universe.fixture_name_changed.connect(
+ func(fixture: Fixture, new_name: String):
+ fixture_name_changed.emit(fixture, new_name)
+ )
+
+ universe.fixtures_added.connect(
+ func(fixtures: Array[Fixture]):
+ fixture_added.emit(fixtures)
+ )
+
+ universe.fixtures_deleted.connect(
+ func(fixture_uuids: Array):
+ fixture_removed.emit(fixture_uuids)
+ )
+
+
+func remove_universe(universe: Universe, no_signal: bool = false) -> bool:
+ ## Removes a universe
+
+ if universe in universes.values():
+
+ universe.delete()
+ universes.erase(universe.uuid)
+ selected_universes.erase(universe)
+
+ var uuid: String = universe.uuid
+
+ universe.free()
+
+ if not no_signal:
+ universes_removed.emit([uuid])
+
+ return true
+
+ else:
+ return false
+
+
+func remove_universes(universes_to_remove: Array, no_signal: bool = false) -> void:
+ ## Removes mutiple universes at once
+
+ var uuids: Array = []
+
+ for universe: Universe in universes_to_remove:
+ uuids.append(universe.uuid)
+ deselect_universes([universe], no_signal)
+ remove_universe(universe, true)
+
+ if not no_signal:
+ universes_removed.emit(uuids)
+
+
+func serialize_universes() -> Dictionary:
+ ## Serializes all universes and returnes them in a dictionary
+
+ var serialized_universes: Dictionary = {}
+
+ for universe: Universe in universes.values():
+ serialized_universes[universe.uuid] = universe.serialize()
+
+ return serialized_universes
+
+
+func select_universes(universes_to_select: Array, no_signal: bool = false) -> void:
+ ## Selects all the fixtures passed to this function
+
+ for universe: Universe in universes_to_select:
+ if universe not in selected_universes:
+ selected_universes.append(universe)
+ universe.set_selected(true)
+
+ if not no_signal:
+ universe_selection_changed.emit(selected_universes)
+
+
+func set_universe_selection(universes_to_select: Array) -> void:
+ ## Changes the selection to be the universes passed to this function
+
+ deselect_universes(selected_universes, true)
+ select_universes(universes_to_select)
+
+
+func deselect_universes(universes_to_deselect: Array, no_signal: bool = false) -> void:
+ ## Selects all the fixtures passed to this function
+
+ for universe: Universe in universes_to_deselect.duplicate():
+ if universe in selected_universes:
+ selected_universes.erase(universe)
+ universe.set_selected(false)
+
+ if not no_signal:
+ universe_selection_changed.emit(selected_universes)
+
+#endregion
+
+
+#region IO
+func reload_io_plugins() -> void:
+ ## Loads all output plugins from the folder
+
+ output_plugins = {}
+
+ var output_plugin_folder : DirAccess = DirAccess.open(output_plugin_path)
+
+ for plugin in output_plugin_folder.get_files():
+ var uninitialized_plugin = ResourceLoader.load(output_plugin_path + plugin)
+
+ var initialized_plugin: DataIOPlugin = uninitialized_plugin.new()
+ var plugin_name: String = initialized_plugin.name
+
+ if plugin_name in output_plugins.keys():
+ plugin_name = plugin_name + " " + UUID_Util.v4()
+
+ output_plugins[plugin] = {"plugin":uninitialized_plugin, "plugin_name":plugin_name}
+ initialized_plugin.free()
+#endregion
+
+
+#region Fixtures
+
+
+func reload_fixtures() -> void:
+ ## Loads fixture definition files from a folder
+
+ fixtures_definitions = {}
+
+ var access = DirAccess.open(fixture_path)
+
+ for fixture_folder in access.get_directories():
+
+ for fixture in access.open(fixture_path+"/"+fixture_folder).get_files():
+
+ var manifest_file = FileAccess.open(fixture_path+fixture_folder+"/"+fixture, FileAccess.READ)
+ var manifest = JSON.parse_string(manifest_file.get_as_text())
+
+ manifest.info.file_path = fixture_path+fixture_folder+"/"+fixture
+
+ if fixtures_definitions.has(manifest.info.brand):
+ fixtures_definitions[manifest.info.brand][manifest.info.name] = manifest
+ else:
+ fixtures_definitions[manifest.info.brand] = {manifest.info.name:manifest}
+
+
+func select_fixtures(fixtures: Array, no_signal: bool = false) -> void:
+ ## Selects all the fixtures passed to this function
+
+ for fixture: Fixture in fixtures:
+ if fixture not in selected_fixtures:
+ selected_fixtures.append(fixture)
+ fixture.set_selected(true)
+
+ if not no_signal:
+ fixture_selection_changed.emit(selected_fixtures)
+
+
+func set_fixture_selection(fixtures: Array) -> void:
+ ## Changes the selection to be the fixtures passed to this function
+
+ deselect_fixtures(selected_fixtures, true)
+ select_fixtures(fixtures)
+
+func deselect_fixtures(fixtures: Array, no_signal: bool = false) -> void:
+ ## Deselects all the fixtures pass to this function
+
+ for fixture: Fixture in fixtures.duplicate():
+ if fixture in selected_fixtures:
+ selected_fixtures.erase(fixture)
+ fixture.set_selected(false)
+
+ if not no_signal:
+ fixture_selection_changed.emit(selected_fixtures)
+#endregion
+
+
+#region Scenes
+
+func new_scene(scene: Scene = Scene.new(), no_signal: bool = false, serialized_data: Dictionary = {}, uuid: String = "") -> Scene:
+ ## Adds a scene to this engine, creats a new one if none is passed
+
+ if uuid:
+ scene.uuid = uuid
+
+ scene.engine = self
+
+ if serialized_data:
+ scene.load_from(serialized_data)
+
+
+ scenes[scene.uuid] = scene
+
+ if not no_signal:
+ scenes_added.emit([scene])
+
+ return scene
+
+
+func remove_scenes(scenes_to_remove: Array, no_signal: bool = false) -> void:
+ ## Removes a scene from this engine
+
+ var uuids: Array = []
+
+ for scene: Scene in scenes_to_remove:
+ uuids.append(scene.uuid)
+ scenes.erase(scene.uuid)
+
+ scene.delete()
+ scene.free()
+
+
+ if not no_signal:
+ scenes_removed.emit(uuids)
+
+
+func serialize_scenes() -> Dictionary:
+ ## Serializes all scenes and returnes them in a dictionary
+
+ var serialized_scenes: Dictionary = {}
+
+ for scene: Scene in scenes.values():
+ serialized_scenes[scene.uuid] = scene.serialize()
+
+ return serialized_scenes
+
+
+#endregion
+
+
+func animate(function: Callable, from: Variant, to: Variant, duration: int) -> void:
+ var animation = get_tree().create_tween()
+ animation.tween_method(function, from, to, duration)
diff --git a/core/engine/System.gd b/core/engine/System.gd
new file mode 100644
index 0000000..4632f7a
--- /dev/null
+++ b/core/engine/System.gd
@@ -0,0 +1,94 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name System extends Object
+## Engine class for the save load system
+
+var save_file: Dictionary = {}
+
+func save(engine: CoreEngine, file_name: String, file_path: String) -> Error:
+ ## Saves the current state of the given engine to a file
+
+ save_file.universes = engine.serialize_universes()
+ return Utils.save_json_to_file(file_path, file_name, save_file)
+
+#extends Object
+#
+#var current_file = ""
+#var home_directory = OS.get_environment("HOME")
+#var save_folder_path = home_directory + "/Documents/Spectrum/"
+#var file_name = ""
+#var save_file = {
+ #"nodes":{},
+ #"node_connections":{},
+ #"widgets":{}
+ #}
+#
+#func _ready():
+ #OS.set_low_processor_usage_mode(true)
+#
+#func _on_save_pressed():
+ #save()
+#
+#func save():
+ #save_nodes()
+ #save_widgets()
+ #save_file.universes = Globals.serialize_universes()
+ #Globals.nodes.save_file_dialog.popup()
+#
+#
+#func _on_save_file_dialog_file_selected(path):
+ #var input_file_name = Globals.nodes.save_file_dialog.current_file
+#
+ #if not input_file_name:
+ #input_file_name = "New Save"
+ #file_name = input_file_name
+#
+ #save_json_to_file(path, save_file, file_name)
+ #
+#func save_json_to_file(folder_path, save_data, _file_name):
+ #
+ #var file_access = FileAccess.open(folder_path, FileAccess.WRITE)
+ #
+ #file_access.store_string(JSON.stringify(save_data, "\t"))
+ #file_access.close()
+#
+#func _on_load_pressed():
+ #get_node("Load File Dialog").popup()
+ #
+#func load_save(file_path):
+ ## Check if save file is valid
+ #var manifest_file = FileAccess.open(file_path, FileAccess.READ)
+ #if manifest_file == null:
+ #Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_FILE,"from":file_path}])
+ #return
+ #var manifest = JSON.parse_string(manifest_file.get_as_text())
+ #if manifest == null:
+ #Globals.show_popup([{"type":Globals.error.UNABLE_TO_LOAD_MANIFEST,"from":file_path}])
+ #return
+ #
+ ## Add Nodes
+ #for node_to_add in manifest.nodes.values():
+ #var node_manifest_file = FileAccess.open(node_to_add.node_file_path + "manifest.json", FileAccess.READ)
+ #if manifest == null:
+ #Globals.show_popup([{"type":Globals.error.MISSING_NODES,"from":node_manifest_file}])
+ #return
+ #Globals.nodes.node_editor._add_node(node_to_add.node_file_path, {"position_offset":node_to_add.position_offset, "values":node_to_add.values})
+ ## Add node connections
+ #Globals.nodes.node_editor.generate_connected_nodes(manifest.node_connections)
+ #
+ ## Add Widgets
+ #for widget_to_add in manifest.widgets.values():
+ #var widget_manifest_file = FileAccess.open(widget_to_add.widget_file_path + "manifest.json", FileAccess.READ)
+ #if manifest == null:
+ #Globals.show_popup([{"type":Globals.error.MISSING_NODES,"from":widget_manifest_file}])
+ #return
+ #Globals.nodes.console_editor._add_widget(widget_to_add.widget_file_path, {"position_offset":widget_to_add.get("position_offset"), "values":widget_to_add.get("values"), "size":widget_to_add.get("size")})
+ #
+ ##Add Universes
+ #Globals.deserialize_universes(manifest.universes)
+ #Globals.call_subscription("reload_universes")
+ #Globals.call_subscription("reload_fixtures")
+ #
+#func _on_load_file_dialog_file_selected(path):
+ #load_save(path)
diff --git a/Scripts/Classes/Uuid.gd b/core/engine/UUID_Util.gd
similarity index 98%
rename from Scripts/Classes/Uuid.gd
rename to core/engine/UUID_Util.gd
index 6491260..8b4621a 100644
--- a/Scripts/Classes/Uuid.gd
+++ b/core/engine/UUID_Util.gd
@@ -2,7 +2,7 @@
# Note: The code might not be as pretty it could be, since it's written
# in a way that maximizes performance. Methods are inlined and loops are avoided.
-extends Node
+class_name UUID_Util extends Object
const BYTE_MASK: int = 0b11111111
diff --git a/core/engine/Utils.gd b/core/engine/Utils.gd
new file mode 100644
index 0000000..c7e22f6
--- /dev/null
+++ b/core/engine/Utils.gd
@@ -0,0 +1,60 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name Utils extends Object
+## Usefull function that would be annoying to write out each time
+
+static func save_json_to_file(file_path: String, file_name: String, json: Dictionary) -> Error:
+
+ var file_access: FileAccess = FileAccess.open(file_path+"/"+file_name, FileAccess.WRITE)
+
+ if FileAccess.get_open_error():
+ return FileAccess.get_open_error()
+
+ print(JSON.stringify(json, "\t"))
+ file_access.store_string(JSON.stringify(json, "\t"))
+ file_access.close()
+
+ return file_access.get_error()
+
+
+static func serialize_variant(variant: Variant) -> Variant:
+ return var_to_str(variant)
+ #match typeof(variant):
+ #TYPE_COLOR:
+ #return "#" + variant.to_html()
+ #
+ #return ERR_INVALID_DATA
+
+
+static func deserialize_variant(variant: Variant) -> Variant:
+ return str_to_var(variant)
+ #match typeof(variant):
+ #TYPE_STRING:
+ #match variant[0]:
+ #"#":
+ #print(variant.right(8))
+ #return Color.from_string(variant.right(8), Color.BLACK)
+ #
+ #return ERR_INVALID_DATA
+
+
+static func get_htp_color(color_1: Color, color_2: Color) -> Color:
+ # Calculate the intensity of each channel for color1
+ var intensity_1_r = color_1.r
+ var intensity_1_g = color_1.g
+ var intensity_1_b = color_1.b
+
+ # Calculate the intensity of each channel for color2
+ var intensity_2_r = color_2.r
+ var intensity_2_g = color_2.g
+ var intensity_2_b = color_2.b
+
+ # Compare the intensities for each channel and return the color with the higher intensity for each channel
+ var result_color = Color()
+ result_color.r = intensity_1_r if intensity_1_r > intensity_2_r else intensity_2_r
+ result_color.g = intensity_1_g if intensity_1_g > intensity_2_g else intensity_2_g
+ result_color.b = intensity_1_b if intensity_1_b > intensity_2_b else intensity_2_b
+
+ return result_color
+
diff --git a/Fixtures/generic/cmy-fader.json b/core/fixtures/generic/cmy-fader.json
similarity index 100%
rename from Fixtures/generic/cmy-fader.json
rename to core/fixtures/generic/cmy-fader.json
diff --git a/Fixtures/generic/cw-ww-fader.json b/core/fixtures/generic/cw-ww-fader.json
similarity index 100%
rename from Fixtures/generic/cw-ww-fader.json
rename to core/fixtures/generic/cw-ww-fader.json
diff --git a/core/fixtures/generic/dimmer.json b/core/fixtures/generic/dimmer.json
new file mode 100644
index 0000000..7eb31e6
--- /dev/null
+++ b/core/fixtures/generic/dimmer.json
@@ -0,0 +1,25 @@
+{
+ "schema_version": "1.0",
+ "info": {
+ "brand": "Generic",
+ "name": "Desk Channel",
+ "date": "21-07-2018",
+ "author": ["Flo Edelmann", "Liam Sherwin"],
+ "categories": ["Dimmer"],
+ "links": {
+ "Website": "https://open-fixture-library.org/generic/desk-channel",
+ "OFLLibrary": "https://open-fixture-library.org/generic/desk-channel"
+ }
+ },
+ "modes": {
+ "1 Channel": {
+ "channels": ["Intensity"]
+ },
+ "2 Channel": {
+ "channels": ["Intensity", "IntensityFine"]
+ },
+ "3 Channel": {
+ "channels": ["Intensity", "IntensityFine", "IntensityFine2"]
+ }
+ }
+}
diff --git a/Fixtures/generic/drgb-fader.json b/core/fixtures/generic/drgb-fader.json
similarity index 100%
rename from Fixtures/generic/drgb-fader.json
rename to core/fixtures/generic/drgb-fader.json
diff --git a/Fixtures/generic/drgbw-fader.json b/core/fixtures/generic/drgbw-fader.json
similarity index 100%
rename from Fixtures/generic/drgbw-fader.json
rename to core/fixtures/generic/drgbw-fader.json
diff --git a/Fixtures/generic/grbw-fader.json b/core/fixtures/generic/grbw-fader.json
similarity index 100%
rename from Fixtures/generic/grbw-fader.json
rename to core/fixtures/generic/grbw-fader.json
diff --git a/Fixtures/generic/pan-tilt.json b/core/fixtures/generic/pan-tilt.json
similarity index 100%
rename from Fixtures/generic/pan-tilt.json
rename to core/fixtures/generic/pan-tilt.json
diff --git a/Fixtures/generic/rgb-fader.json b/core/fixtures/generic/rgb-fader.json
similarity index 100%
rename from Fixtures/generic/rgb-fader.json
rename to core/fixtures/generic/rgb-fader.json
diff --git a/Fixtures/generic/rgba-fader.json b/core/fixtures/generic/rgba-fader.json
similarity index 100%
rename from Fixtures/generic/rgba-fader.json
rename to core/fixtures/generic/rgba-fader.json
diff --git a/Fixtures/generic/rgbawuv-fader.json b/core/fixtures/generic/rgbawuv-fader.json
similarity index 99%
rename from Fixtures/generic/rgbawuv-fader.json
rename to core/fixtures/generic/rgbawuv-fader.json
index ca4c738..3bc3622 100644
--- a/Fixtures/generic/rgbawuv-fader.json
+++ b/core/fixtures/generic/rgbawuv-fader.json
@@ -23,4 +23,4 @@
}
}
}
-
\ No newline at end of file
+
diff --git a/Fixtures/generic/rgbd-fader.json b/core/fixtures/generic/rgbd-fader.json
similarity index 100%
rename from Fixtures/generic/rgbd-fader.json
rename to core/fixtures/generic/rgbd-fader.json
diff --git a/Fixtures/generic/rgbw-fader.json b/core/fixtures/generic/rgbw-fader.json
similarity index 100%
rename from Fixtures/generic/rgbw-fader.json
rename to core/fixtures/generic/rgbw-fader.json
diff --git a/Fixtures/generic/rgbwauv-fader.json b/core/fixtures/generic/rgbwauv-fader.json
similarity index 100%
rename from Fixtures/generic/rgbwauv-fader.json
rename to core/fixtures/generic/rgbwauv-fader.json
diff --git a/Fixtures/generic/rgbww-fader.json b/core/fixtures/generic/rgbww-fader.json
similarity index 100%
rename from Fixtures/generic/rgbww-fader.json
rename to core/fixtures/generic/rgbww-fader.json
diff --git a/Fixtures/generic/strobe.json b/core/fixtures/generic/strobe.json
similarity index 100%
rename from Fixtures/generic/strobe.json
rename to core/fixtures/generic/strobe.json
diff --git a/core/io_plugins/DataIOPlugin.gd b/core/io_plugins/DataIOPlugin.gd
new file mode 100644
index 0000000..cc81656
--- /dev/null
+++ b/core/io_plugins/DataIOPlugin.gd
@@ -0,0 +1,18 @@
+# Copyright (c) 2024 Liam Sherwin
+# All rights reserved.
+
+class_name DataIOPlugin extends EngineComponent
+## Engine base class for all input and output plugins
+
+var type: String = "" ## Type of this plugin, either input or output
+
+func set_type(new_type:String) -> void:
+ type = new_type
+
+
+func get_type() -> String:
+ return type
+
+
+func delete() -> void:
+ return
diff --git a/IO Plugins/Output Plugins/Art Net.gd b/core/io_plugins/output_plugins/ArtNetOutput.gd
similarity index 66%
rename from IO Plugins/Output Plugins/Art Net.gd
rename to core/io_plugins/output_plugins/ArtNetOutput.gd
index 1b8f14e..caa3ad1 100644
--- a/IO Plugins/Output Plugins/Art Net.gd
+++ b/core/io_plugins/output_plugins/ArtNetOutput.gd
@@ -1,8 +1,9 @@
-extends DataIOPlugin
-class_name Art_Net_Output
+class_name ArtNetOutput extends DataIOPlugin
var _udp_peer = PacketPeerUDP.new()
+var _current_data: Dictionary = {}
+
var exposed_values = [
{
"name":"Ip Address",
@@ -10,7 +11,7 @@ var exposed_values = [
"signal":"text_submitted",
"function":self.set_ip_addr,
"parameters":{
- "placeholder_text":"172.0.0.1",
+ "placeholder_text":"127.0.0.1",
"text":self.get_ip_addr
}
},
@@ -38,76 +39,87 @@ var exposed_values = [
]
var config = {
- "ip":"172.0.0.1",
+ "ip":"127.0.0.1",
"port":6454,
"universe":0,
}
-func _init():
+func _init(serialised_data: Dictionary = {}):
self.set_type("output")
- self.set_name("Art-Net")
+
+ self.name = serialised_data.get("name", "Art Net Output")
+ config.ip = serialised_data.get("config", {}).get("ip", config.ip)
+ config.port = serialised_data.get("config", {}).get("port", config.port)
+ config.universe = serialised_data.get("config", {}).get("univeres", config.universe)
+
+ super._init()
+
+ connect_to_host()
+
func connect_to_host():
_udp_peer.close()
_udp_peer.connect_to_host(config.ip, config.port)
-func set_ip_addr(new_ip_address):
- config.ip = new_ip_address
- connect_to_host()
-func set_port(new_port):
- config.port = new_port
- connect_to_host()
-
-func set_universe(new_universe):
- config.universe = new_universe
- connect_to_host()
+func _disconnect():
+ _udp_peer.close()
-func get_ip_addr():
- return config.ip
-func get_port():
- return config.port
+func set_data(data) -> void :
+ _current_data = data
-func get_universe():
- return config.universe
func serialize():
- return config
+ return {
+ "config":{
+ "ip": config.ip,
+ "port": config.port,
+ "universe": config.universe,
+ },
+ "name": self.name,
+ "file": self.get_script().get_path().split("/")[-1],
+ "user_meta": self.serialize_meta()
+ }
+#
+#func from(serialized_data):
+ #config = serialized_data
-func from(serialized_data):
- config = serialized_data
-
-func _disconnect():
- _udp_peer.close()
func delete():
_disconnect()
-func send_packet(dmx_data) -> void:
+
+func send_packet() -> void:
+
+ if not _current_data:
+ return
+
+ print(_current_data)
+
# Construct Art-Net packet
var packet = PackedByteArray()
-
+
# Art-Net ID ('Art-Net')
packet.append_array([65, 114, 116, 45, 78, 101, 116, 0])
-
+
# OpCode: ArtDMX (0x5000)
packet.append_array([0, 80])
-
+
# Protocol Version: 14 (0x000e)
packet.append_array([0, 14])
-
+
# ArtDMX packet
# Sequence Number
packet.append(0)
-
+
# Physical Port (Set to 0 if not needed)
packet.append(0)
-
+
# Universe (16-bit)
packet.append(int(config.universe) % 256) # Lower 8 bits
packet.append(int(config.universe) / 256) # Upper 8 bits
-
+
# Length (16-bit)
# packet.append_array([512 % 256, int(512 / 255)])
packet.append(02)
@@ -115,9 +127,37 @@ func send_packet(dmx_data) -> void:
# DMX Channels
for channel in range(1, 513):
- packet.append(dmx_data.get(channel, 0))
-
+ packet.append(_current_data.get(channel, 0))
+
# Send the packet
# _udp_peer.set_dest_address(ip, port)
_udp_peer.put_packet(packet)
+ _current_data = {}
+
+
+func set_ip_addr(new_ip_address):
+ config.ip = new_ip_address
+ connect_to_host()
+
+
+func set_port(new_port):
+ config.port = new_port
+ connect_to_host()
+
+
+func set_universe(new_universe):
+ config.universe = new_universe
+ connect_to_host()
+
+
+func get_ip_addr():
+ return config.ip
+
+
+func get_port():
+ return config.port
+
+
+func get_universe():
+ return config.universe
diff --git a/export_presets.cfg b/export_presets.cfg
index 5ad396a..ccbec32 100644
--- a/export_presets.cfg
+++ b/export_presets.cfg
@@ -124,7 +124,7 @@ binary_format/architecture="universal"
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
-application/icon="res://icon.png"
+application/icon="res://Assets/Icon.png"
application/icon_interpolation=4
application/bundle_identifier="tech.liamsherwin.spectrum"
application/signature=""
diff --git a/project.godot b/project.godot
index ad55088..7d69aee 100644
--- a/project.godot
+++ b/project.godot
@@ -31,9 +31,11 @@ driver/enable_input=true
[autoload]
Globals="*res://Scripts/Global.gd"
+Core="*res://core/engine/Engine.gd"
[debug]
+gdscript/warnings/unused_parameter=0
gdscript/warnings/shadowed_variable=0
gdscript/warnings/shadowed_variable_base_class=0
gdscript/warnings/shadowed_global_identifier=0
@@ -81,4 +83,5 @@ miditest={
[rendering]
+renderer/rendering_method="gl_compatibility"
textures/vram_compression/import_etc2_astc=true