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