Răsfoiți Sursa

Added settings window with custom theme options

Jonathan Higgins 7 luni în urmă
părinte
comite
4ee125f3c7

+ 10 - 10
README.md

@@ -23,28 +23,28 @@ SoundThread is currently in Alpha and as such there are some bugs, missing featu
 - Node based patching system with support for patching parallel processes and mixing outputs
 - Node based patching system with support for patching parallel processes and mixing outputs
 - A selection of popular CDP time domain and frequency domain processes:
 - A selection of popular CDP time domain and frequency domain processes:
   - [Distort](https://www.composersdesktop.com/docs/html/ccdpndex.htm#DISTORT) - Average, Clip, Click (Reform), Divide, Fractal, Interpolate, Multiply, Power Factor (Quirk), Replace, Square (Reform), and Triangle (Reform)
   - [Distort](https://www.composersdesktop.com/docs/html/ccdpndex.htm#DISTORT) - Average, Clip, Click (Reform), Divide, Fractal, Interpolate, Multiply, Power Factor (Quirk), Replace, Square (Reform), and Triangle (Reform)
-  - [Extend](https://www.composersdesktop.com/docs/html/ccdpndex.htm#EXTEND) -  Drunk, Loop, Scramble, Shrink, and Zigzag
+  - [Extend](https://www.composersdesktop.com/docs/html/ccdpndex.htm#EXTEND) -  Drunk, Loop, Scramble, and Zigzag
   - [Filter](https://www.composersdesktop.com/docs/html/ccdpndex.htm#FILTER) - Low Pass/High Pass, Filter Bank Harmonic Series, Filter Bank Odd, Filter Bank Linear Spacing, and Filter Bank Pitched Intervals
   - [Filter](https://www.composersdesktop.com/docs/html/ccdpndex.htm#FILTER) - Low Pass/High Pass, Filter Bank Harmonic Series, Filter Bank Odd, Filter Bank Linear Spacing, and Filter Bank Pitched Intervals
   - [Granulate (Brassage)](https://www.composersdesktop.com/docs/html/cgromody.htm#BRASSAGE) - Brassage, Granulate, Pitch Shift, Scramble, and Time Stretch
   - [Granulate (Brassage)](https://www.composersdesktop.com/docs/html/cgromody.htm#BRASSAGE) - Brassage, Granulate, Pitch Shift, Scramble, and Time Stretch
-  - Misc - Accelerate/Decelerate, Gain, Reverse, Stack, and Varispeed
-  - [PVOC](https://www.composersdesktop.com/docs/html/cspecndx.htm) - Analaysis/Resynthesis, Accumulate, Blur, Chorus, Gain, Invert, Stretch, Scatter, Trace (hilite), and Waver
+  - Misc - Accelerate/Decelerate, Append Silence, Gain, Reverse, Stack, and Varispeed
+  - [PVOC](https://www.composersdesktop.com/docs/html/cspecndx.htm) - Analysis/Resynthesis, Accumulate, Blur, Chorus, Gain, Invert, Stretch, Scatter, Trace (hilite), and Waver
 - Automation of values using automatically generated [Breakpoint Files](https://www.composersdesktop.com/docs/html//filestxt.htm#BREAKPOINTFILES) based on drawn in automation data
 - Automation of values using automatically generated [Breakpoint Files](https://www.composersdesktop.com/docs/html//filestxt.htm#BREAKPOINTFILES) based on drawn in automation data
-- Mac and Windows builds
+- Windows, Mac and Linux builds
 - Accepts stereo or mono input files (splits and merges files as needed to run the full processing Thread)
 - Accepts stereo or mono input files (splits and merges files as needed to run the full processing Thread)
 - Threads can be saved and loaded for reuse
 - Threads can be saved and loaded for reuse
 - Small suite of built in getting started tutorials
 - Small suite of built in getting started tutorials
 - Help tooltips and detailed help files throughout
 - Help tooltips and detailed help files throughout
 - Recycle output button to reuse output file for further processing 
 - Recycle output button to reuse output file for further processing 
-- Optional: automatic clean up of intermediate files
+- Optional automatic clean up of intermediate files
+- Customisable colour schemes
  
  
 ## What doesn't work?
 ## What doesn't work?
-A number of things are not yet implemented or supported. Not all features of CDP will likely be implemented in SoundThread, as not all processes work well with the node based system. For access to all features of CDP I reccomend [SoundLoom, Soundshaper](https://www.composersdesktop.com/docs/html/cdphome.htm#GUIS) or using the command line directly.
+A number of things are not yet implemented or supported. Not all features of CDP will likely be implemented in SoundThread, as not all processes work well with the node based system. For access to all features of CDP I recommend [SoundLoom, Soundshaper](https://www.composersdesktop.com/docs/html/cdphome.htm#GUIS) or using the command line directly.
 ### Main missing features:
 ### Main missing features:
 - Text files other than simple value/pair breakpoint files and PVOC analysis files
 - Text files other than simple value/pair breakpoint files and PVOC analysis files
-- Support for multiple input files and therefore all processes which require more than one input file
+- Support for multiple input files and therefore all processes which require more than one input file and those that really benefit from multiple input files (e.g. Texture processes)
 - Support for audio files with more than 2 channels
 - Support for audio files with more than 2 channels
 - Support for audio formats other than WAV
 - Support for audio formats other than WAV
-- Nodes for many CDP processes have not yet been made
-- Linux build is not yet tested (should work fine in theory just needs testing)
+- Many CDP processes have not yet been implemented
 
 
-If you find any bugs or have feature ideas, please raise a ticket in [issues](https://github.com/j-p-higgins/SoundThread/issues).
+If you find any bugs or have user interface feature ideas, please raise a ticket in [issues](https://github.com/j-p-higgins/SoundThread/issues). If you would like to request specific CDP processes be added to SoundThread, please comment on [this community discussion](https://github.com/j-p-higgins/SoundThread/discussions/59).

+ 2 - 0
config_handler.gd

@@ -14,6 +14,8 @@ func _ready():
 	ensure_setting("interface_settings", "disable_pvoc_warning", false)
 	ensure_setting("interface_settings", "disable_pvoc_warning", false)
 	ensure_setting("interface_settings", "auto_close_console", false)
 	ensure_setting("interface_settings", "auto_close_console", false)
 	ensure_setting("interface_settings", "console_on_top", true)
 	ensure_setting("interface_settings", "console_on_top", true)
+	ensure_setting("interface_settings", "theme", 0)
+	ensure_setting("interface_settings", "theme_custom_colour", "#865699")
 	ensure_setting("audio_settings", "device", "Default")
 	ensure_setting("audio_settings", "device", "Default")
 
 
 	# Only save if we added anything new
 	# Only save if we added anything new

+ 20 - 20
examples/navigating.thd

@@ -21,7 +21,7 @@
 			"id": 2,
 			"id": 2,
 			"name": "notes3",
 			"name": "notes3",
 			"notes": {
 			"notes": {
-				"CodeEdit": "Click and drag on the background to select multiple nodes at once.\n\nPressing Backspace/Delete will remove selected nodes. Note: \"Input File\" and \"Output File\" can't be deleted.\n\nSoundThread allows you to undo most major changes with ctrl/cmd+z. Please note this not yet fully implemented."
+				"CodeEdit": "Click and drag on the background to select multiple nodes at once.\n\nPressing Backspace/Delete will remove selected nodes. Note: \"Input File\" and \"Output File\" can't be deleted.\n\nSoundThread allows you to undo most major changes with ctrl/cmd and Z. Note: this not yet fully implemented."
 			},
 			},
 			"offset": {
 			"offset": {
 				"x": 964.444641113281,
 				"x": 964.444641113281,
@@ -31,24 +31,9 @@
 
 
 			}
 			}
 		},
 		},
-		{
-			"command": "notes",
-			"id": 3,
-			"name": "notes2",
-			"notes": {
-				"CodeEdit": "Right click anywhere in SoundThread to bring up the main menu.\n\nThis menu lists the available processes in SoundThread. Click the + next to a process to add it to your Thread.\n\nNodes can be dragged around to rearrange and you can copy and paste nodes with ctrl/cmd+c and ctrl/cmd+v."
-			},
-			"offset": {
-				"x": 531.111267089844,
-				"y": 162.222259521484
-			},
-			"slider_values": {
-
-			}
-		},
 		{
 		{
 			"command": "inputfile",
 			"command": "inputfile",
-			"id": 4,
+			"id": 3,
 			"name": "inputfile",
 			"name": "inputfile",
 			"notes": {
 			"notes": {
 
 
@@ -58,7 +43,7 @@
 				"y": 164.444458007813
 				"y": 164.444458007813
 			},
 			},
 			"slider_values": {
 			"slider_values": {
-				"AudioPlayer/FileDialog/@VBoxContainer@2060/@MarginContainer@2100/Tree/@Popup@2086/@VBoxContainer@2087/@HSlider@2095": {
+				"AudioPlayer/FileDialog/@VBoxContainer@2892/@MarginContainer@2932/Tree/@Popup@2918/@VBoxContainer@2919/@HSlider@2927": {
 					"editable": true,
 					"editable": true,
 					"meta": {
 					"meta": {
 
 
@@ -69,7 +54,7 @@
 		},
 		},
 		{
 		{
 			"command": "outputfile",
 			"command": "outputfile",
-			"id": 5,
+			"id": 4,
 			"name": "outputfile",
 			"name": "outputfile",
 			"notes": {
 			"notes": {
 
 
@@ -79,7 +64,7 @@
 				"y": 393.333435058594
 				"y": 393.333435058594
 			},
 			},
 			"slider_values": {
 			"slider_values": {
-				"AudioPlayer/FileDialog/@VBoxContainer@2169/@MarginContainer@2209/Tree/@Popup@2195/@VBoxContainer@2196/@HSlider@2204": {
+				"AudioPlayer/FileDialog/@VBoxContainer@3001/@MarginContainer@3041/Tree/@Popup@3027/@VBoxContainer@3028/@HSlider@3036": {
 					"editable": true,
 					"editable": true,
 					"meta": {
 					"meta": {
 
 
@@ -87,6 +72,21 @@
 					"value": 0.0
 					"value": 0.0
 				}
 				}
 			}
 			}
+		},
+		{
+			"command": "notes",
+			"id": 5,
+			"name": "notes2",
+			"notes": {
+				"CodeEdit": "Press ctrl/cmd and E to browse all processes in SoundThread or, right click anywhere in empty space to search and add nodes.\n\nNodes can be dragged around to rearrange and you can copy and paste nodes with ctrl/cmd+c and ctrl/cmd+v."
+			},
+			"offset": {
+				"x": 531.111267089844,
+				"y": 166.666702270508
+			},
+			"slider_values": {
+
+			}
 		}
 		}
 	]
 	]
 }
 }

+ 21 - 183
scenes/main/control.gd

@@ -40,6 +40,7 @@ func _ready() -> void:
 	$AudioSettings.hide()
 	$AudioSettings.hide()
 	$AudioDevicePopup.hide()
 	$AudioDevicePopup.hide()
 	$SearchMenu.hide()
 	$SearchMenu.hide()
+	$Settings.hide()
 	
 	
 	$SaveDialog.access = FileDialog.ACCESS_FILESYSTEM
 	$SaveDialog.access = FileDialog.ACCESS_FILESYSTEM
 	$SaveDialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
 	$SaveDialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
@@ -49,6 +50,7 @@ func _ready() -> void:
 	$LoadDialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
 	$LoadDialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
 	$LoadDialog.filters = ["*.thd"]
 	$LoadDialog.filters = ["*.thd"]
 	
 	
+	
 	#Goes through all nodes in scene and checks for buttons in the make_node_buttons group
 	#Goes through all nodes in scene and checks for buttons in the make_node_buttons group
 	#Associates all buttons with the _on_button_pressed fuction and passes the button as an argument
 	#Associates all buttons with the _on_button_pressed fuction and passes the button as an argument
 	for child in get_tree().get_nodes_in_group("make_node_buttons"):
 	for child in get_tree().get_nodes_in_group("make_node_buttons"):
@@ -58,6 +60,8 @@ func _ready() -> void:
 	get_node("SearchMenu").make_node.connect(_make_node_from_search_menu)
 	get_node("SearchMenu").make_node.connect(_make_node_from_search_menu)
 	get_node("mainmenu").make_node.connect(_make_node_from_search_menu)
 	get_node("mainmenu").make_node.connect(_make_node_from_search_menu)
 	get_node("mainmenu").open_help.connect(show_help_for_node)
 	get_node("mainmenu").open_help.connect(show_help_for_node)
+	get_node("Settings").open_cdp_location.connect(show_cdp_location)
+	get_node("Settings").console_on_top.connect(change_console_settings)
 	
 	
 	check_user_preferences()
 	check_user_preferences()
 	get_tree().set_auto_accept_quit(false) #disable closing the app with the x and instead handle it internally
 	get_tree().set_auto_accept_quit(false) #disable closing the app with the x and instead handle it internally
@@ -156,15 +160,23 @@ func check_user_preferences():
 	var interface_settings = ConfigHandler.load_interface_settings()
 	var interface_settings = ConfigHandler.load_interface_settings()
 	var audio_settings = ConfigHandler.load_audio_settings()
 	var audio_settings = ConfigHandler.load_audio_settings()
 	var audio_devices = AudioServer.get_output_device_list()
 	var audio_devices = AudioServer.get_output_device_list()
-	$MenuBar/SettingsButton.set_item_checked(1, interface_settings.disable_pvoc_warning)
-	$MenuBar/SettingsButton.set_item_checked(2, interface_settings.auto_close_console)
-	$MenuBar/SettingsButton.set_item_checked(3, interface_settings.console_on_top)
 	$Console.always_on_top = interface_settings.console_on_top
 	$Console.always_on_top = interface_settings.console_on_top
 	if audio_devices.has(audio_settings.device):
 	if audio_devices.has(audio_settings.device):
 		AudioServer.set_output_device(audio_settings.device)
 		AudioServer.set_output_device(audio_settings.device)
 	else:
 	else:
 		$AudioDevicePopup.popup_centered()
 		$AudioDevicePopup.popup_centered()
-
+	
+	match interface_settings.theme:
+		0:
+			RenderingServer.set_default_clear_color(Color("#2f4f4e"))
+		1:
+			RenderingServer.set_default_clear_color(Color("#000807"))
+		2:
+			RenderingServer.set_default_clear_color(Color("#98d4d2"))
+		3:
+			RenderingServer.set_default_clear_color(Color(interface_settings.theme_custom_colour))
+func show_cdp_location():
+	$CdpLocationDialog.show()
 	
 	
 func check_cdp_location_set():
 func check_cdp_location_set():
 	#checks if the location has been set and prompts user to set it
 	#checks if the location has been set and prompts user to set it
@@ -1375,33 +1387,10 @@ func _on_settings_button_index_pressed(index: int) -> void:
 	
 	
 	match index:
 	match index:
 		0:
 		0:
-			$CdpLocationDialog.show()
+			$Settings.popup_centered()
 		1:
 		1:
-			if interface_settings.disable_pvoc_warning == false:
-				$MenuBar/SettingsButton.set_item_checked(index, true)
-				ConfigHandler.save_interface_settings("disable_pvoc_warning", true)
-			else:
-				$MenuBar/SettingsButton.set_item_checked(index, false)
-				ConfigHandler.save_interface_settings("disable_pvoc_warning", false)
-		2:
-			if interface_settings.auto_close_console == false:
-				$MenuBar/SettingsButton.set_item_checked(index, true)
-				ConfigHandler.save_interface_settings("auto_close_console", true)
-			else:
-				$MenuBar/SettingsButton.set_item_checked(index, false)
-				ConfigHandler.save_interface_settings("auto_close_console", false)
-		3:
-			if interface_settings.console_on_top == false:
-				$MenuBar/SettingsButton.set_item_checked(index, true)
-				ConfigHandler.save_interface_settings("console_on_top", true)
-				$Console.always_on_top = true
-			else:
-				$MenuBar/SettingsButton.set_item_checked(index, false)
-				ConfigHandler.save_interface_settings("console_on_top", false)
-				$Console.always_on_top = false
-		4:
 			$AudioSettings.popup_centered()
 			$AudioSettings.popup_centered()
-		5:
+		2:
 			if $Console.is_visible():
 			if $Console.is_visible():
 				$Console.hide()
 				$Console.hide()
 				await get_tree().process_frame  # Wait a frame to allow hide to complete
 				await get_tree().process_frame  # Wait a frame to allow hide to complete
@@ -1605,160 +1594,6 @@ func load_graph_edit(path: String):
 	print("Graph loaded.")
 	print("Graph loaded.")
 	get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
 	get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
 
 
-#func save_graph_edit(path: String):
-	#var file = FileAccess.open(path, FileAccess.WRITE)
-	#if file == null:
-		#print("Failed to open file for saving")
-		#return
-#
-	#var node_data_list = []
-	#var connection_data_list = []
-#
-	#for node in graph_edit.get_children():
-		#if node is GraphNode:
-			#var offset = node.position_offset
-			#var node_data = {
-				#"name": node.name,
-				#"command": node.get_meta("command"),
-				#"offset": { "x": offset.x, "y": offset.y },
-				#"slider_values": {},
-				#"notes":{}
-			#}
-#
-			#for child in node.find_children("*", "Slider", true, false):
-				#var relative_path = node.get_path_to(child)
-				#var path_str = str(relative_path)
-#
-				## Save slider value
-				#node_data["slider_values"][path_str] = {
-					#"value": child.value,
-					#"editable": child.editable,
-					#"meta": {}
-				#}
-#
-				## Save all metadata
-				#for key in child.get_meta_list():
-					#node_data["slider_values"][path_str]["meta"][str(key)] = child.get_meta(key)
-				#
-			#for child in node.find_children("*", "CodeEdit", true, false):
-				#node_data["notes"][child.name] = child.text
-#
-			#node_data_list.append(node_data)
-#
-	#for conn in graph_edit.get_connection_list():
-		#connection_data_list.append({
-			#"from_node": conn["from_node"],
-			#"from_port": conn["from_port"],
-			#"to_node": conn["to_node"],
-			#"to_port": conn["to_port"]
-		#})
-#
-	#var graph_data = {
-		#"nodes": node_data_list,
-		#"connections": connection_data_list
-	#}
-#
-	#var json = JSON.new()
-	#var json_string = json.stringify(graph_data, "\t")
-	#file.store_string(json_string)
-	#file.close()
-	#print("Graph saved.")
-	#changesmade = false
-	#get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
-#
-#
-#func load_graph_edit(path: String):
-	#var file = FileAccess.open(path, FileAccess.READ)
-	#if file == null:
-		#print("Failed to open file for loading")
-		#return
-#
-	#var json_text = file.get_as_text()
-	#file.close()
-#
-	#var json = JSON.new()
-	#if json.parse(json_text) != OK:
-		#print("Error parsing JSON")
-		#return
-#
-	#var graph_data = json.get_data()
-	#graph_edit.clear_connections()
-#
-	#for node in graph_edit.get_children():
-		#if node is GraphNode:
-			#node.queue_free()
-#
-	#await get_tree().process_frame  # Ensure nodes are cleared
-#
-	#for node_data in graph_data["nodes"]:
-		#var command_name = node_data.get("command", "")
-		#var template = Nodes.get_node_or_null(command_name)
-		#if not template:
-			#print("Template not found for command:", command_name)
-			#continue
-#
-		#var new_node: GraphNode = template.duplicate()
-		#new_node.name = node_data["name"]
-		#new_node.position_offset = Vector2(node_data["offset"]["x"], node_data["offset"]["y"])
-		#new_node.set_meta("command", command_name)
-		#graph_edit.add_child(new_node)
-		#_register_node_movement() #link nodes for tracking position changes for changes tracking
-#
-		## Restore sliders
-		#for slider_path_str in node_data["slider_values"]:
-			#var slider = new_node.get_node_or_null(slider_path_str)
-			#if slider and (slider is HSlider or slider is VSlider):
-				#var slider_info = node_data["slider_values"][slider_path_str]
-				#
-				#if typeof(slider_info) == TYPE_DICTIONARY:
-					#slider.value = slider_info.get("value", slider.value)
-					#
-					## Restore enabled/disabled
-					#if slider_info.has("editable"):
-						#slider.editable = slider_info["editable"]
-					#
-					## Restore metadata
-					#if slider_info.has("meta"):
-						#for key in slider_info["meta"]:
-							#var value = slider_info["meta"][key]
-#
-							## Convert arrays of stringified Vector2s back to real Vector2s
-							#if key == "brk_data" and typeof(value) == TYPE_ARRAY:
-								#var new_array: Array = []
-								#for item in value:
-									#if typeof(item) == TYPE_STRING:
-										## Extract values from "(x, y)"
-										#var numbers: PackedStringArray = item.strip_edges().trim_prefix("(").trim_suffix(")").split(",")
-										#if numbers.size() == 2:
-											#var x = float(numbers[0])
-											#var y = float(numbers[1])
-											#new_array.append(Vector2(x, y))
-								#value = new_array
-#
-							#slider.set_meta(key, value)
-				#else:
-					## Legacy support: just value
-					#slider.value = slider_info
-			#
-	#
-				#
-		## Restore notes
-		#for codeedit_name in node_data["notes"]:
-			#var codeedit = new_node.find_child(codeedit_name, true, false)
-			#if codeedit and (codeedit is CodeEdit):
-				#codeedit.text = node_data["notes"][codeedit_name]
-		#_register_inputs_in_node(new_node) #link sliders for changes tracking
-	## Restore connections
-	#for conn in graph_data["connections"]:
-		#graph_edit.connect_node(
-			#conn["from_node"], conn["from_port"],
-			#conn["to_node"], conn["to_port"]
-		#)
-	#link_output()
-	#print("Graph loaded.")
-	#get_window().title = "SoundThread - " + path.get_file().trim_suffix(".thd")
-
-
 func _on_save_dialog_file_selected(path: String) -> void:
 func _on_save_dialog_file_selected(path: String) -> void:
 	save_graph_edit(path) #save file
 	save_graph_edit(path) #save file
 	#check what the user was trying to do before save and do that action
 	#check what the user was trying to do before save and do that action
@@ -1974,3 +1809,6 @@ func open_explore():
 	#position and show the menu
 	#position and show the menu
 	$mainmenu.position = Vector2(clamped_x, clamped_y)
 	$mainmenu.position = Vector2(clamped_x, clamped_y)
 	$mainmenu.popup()
 	$mainmenu.popup()
+	
+func change_console_settings(toggled: bool):
+	$Console.always_on_top = toggled

+ 9 - 14
scenes/main/control.tscn

@@ -1,4 +1,4 @@
-[gd_scene load_steps=8 format=3 uid="uid://bcs87y7ptx3ke"]
+[gd_scene load_steps=9 format=3 uid="uid://bcs87y7ptx3ke"]
 
 
 [ext_resource type="Script" uid="uid://bdlfvuljckmu1" path="res://scenes/main/control.gd" id="1_2f0aq"]
 [ext_resource type="Script" uid="uid://bdlfvuljckmu1" path="res://scenes/main/control.gd" id="1_2f0aq"]
 [ext_resource type="Script" uid="uid://l2yejnjysupr" path="res://scenes/main/graph_edit.gd" id="2_3ioqo"]
 [ext_resource type="Script" uid="uid://l2yejnjysupr" path="res://scenes/main/graph_edit.gd" id="2_3ioqo"]
@@ -7,6 +7,7 @@
 [ext_resource type="PackedScene" uid="uid://dta7rfalv4uvd" path="res://scenes/main/audio_settings.tscn" id="5_dtf4o"]
 [ext_resource type="PackedScene" uid="uid://dta7rfalv4uvd" path="res://scenes/main/audio_settings.tscn" id="5_dtf4o"]
 [ext_resource type="Script" uid="uid://dlcbmyu3s2phc" path="res://scenes/menu/search_menu.gd" id="6_fyarh"]
 [ext_resource type="Script" uid="uid://dlcbmyu3s2phc" path="res://scenes/menu/search_menu.gd" id="6_fyarh"]
 [ext_resource type="Script" uid="uid://b6r7k326k3vif" path="res://scenes/Nodes/check_for_updates.gd" id="7_1kc3g"]
 [ext_resource type="Script" uid="uid://b6r7k326k3vif" path="res://scenes/Nodes/check_for_updates.gd" id="7_1kc3g"]
+[ext_resource type="PackedScene" uid="uid://c1a6elrpk4eks" path="res://scenes/main/settings.tscn" id="8_16l5g"]
 
 
 [node name="Control" type="Control"]
 [node name="Control" type="Control"]
 layout_mode = 3
 layout_mode = 3
@@ -243,22 +244,13 @@ item_3/id = 1
 
 
 [node name="SettingsButton" type="PopupMenu" parent="MenuBar"]
 [node name="SettingsButton" type="PopupMenu" parent="MenuBar"]
 title = "Settings"
 title = "Settings"
-item_count = 6
-item_0/text = "Change CDP Folder Location"
+item_count = 3
+item_0/text = "SoundThread Settings"
 item_0/id = 0
 item_0/id = 0
-item_1/text = "Disable PVOC Multi Input Warning"
-item_1/checkable = 1
+item_1/text = "Audio Settings"
 item_1/id = 1
 item_1/id = 1
-item_2/text = "Auto Close Console"
-item_2/checkable = 1
+item_2/text = "Open the Console"
 item_2/id = 2
 item_2/id = 2
-item_3/text = "Console Always on Top"
-item_3/checkable = 1
-item_3/id = 5
-item_4/text = "Audio Settings"
-item_4/id = 4
-item_5/text = "Open the Console"
-item_5/id = 3
 
 
 [node name="HelpButton" type="PopupMenu" parent="MenuBar"]
 [node name="HelpButton" type="PopupMenu" parent="MenuBar"]
 auto_translate_mode = 1
 auto_translate_mode = 1
@@ -402,6 +394,9 @@ offset_right = 382.0
 offset_bottom = 100.0
 offset_bottom = 100.0
 text = "Get the update"
 text = "Get the update"
 
 
+[node name="Settings" parent="." instance=ExtResource("8_16l5g")]
+visible = false
+
 [connection signal="connection_request" from="GraphEdit" to="." method="_on_graph_edit_connection_request"]
 [connection signal="connection_request" from="GraphEdit" to="." method="_on_graph_edit_connection_request"]
 [connection signal="delete_nodes_request" from="GraphEdit" to="." method="_on_graph_edit_delete_nodes_request"]
 [connection signal="delete_nodes_request" from="GraphEdit" to="." method="_on_graph_edit_delete_nodes_request"]
 [connection signal="disconnection_request" from="GraphEdit" to="." method="_on_graph_edit_disconnection_request"]
 [connection signal="disconnection_request" from="GraphEdit" to="." method="_on_graph_edit_disconnection_request"]

+ 58 - 0
scenes/main/settings.gd

@@ -0,0 +1,58 @@
+extends Window
+signal open_cdp_location
+signal console_on_top
+var interface_settings
+
+# Called when the node enters the scene tree for the first time.
+func _ready() -> void:
+	pass
+
+
+func _on_change_cdp_button_down() -> void:
+	self.hide()
+	open_cdp_location.emit()
+	
+
+func _on_close_requested() -> void:
+	self.hide()
+
+
+func _on_about_to_popup() -> void:
+	interface_settings = ConfigHandler.load_interface_settings()
+	$VBoxContainer/HBoxContainer5/ThemeList.select(interface_settings.theme, true)
+	$VBoxContainer/HBoxContainer/CustomColourPicker.color = Color(interface_settings.theme_custom_colour)
+	$VBoxContainer/HBoxContainer2/PvocWarning.button_pressed = interface_settings.disable_pvoc_warning
+	$VBoxContainer/HBoxContainer3/AutoCloseConsole.button_pressed = interface_settings.auto_close_console
+	$VBoxContainer/HBoxContainer4/ConsoleAlwaysOnTop.button_pressed = interface_settings.console_on_top
+	
+
+func _on_pvoc_warning_toggled(toggled_on: bool) -> void:
+	ConfigHandler.save_interface_settings("disable_pvoc_warning", toggled_on)
+
+
+func _on_auto_close_console_toggled(toggled_on: bool) -> void:
+	ConfigHandler.save_interface_settings("auto_close_console", toggled_on)
+	
+
+func _on_console_always_on_top_toggled(toggled_on: bool) -> void:
+	ConfigHandler.save_interface_settings("console_on_top", toggled_on)
+	console_on_top.emit(toggled_on)
+
+
+func _on_theme_list_item_selected(index: int) -> void:
+	ConfigHandler.save_interface_settings("theme", index)
+	match index:
+		0:
+			RenderingServer.set_default_clear_color(Color("#2f4f4e"))
+		1:
+			RenderingServer.set_default_clear_color(Color("#000807"))
+		2:
+			RenderingServer.set_default_clear_color(Color("#98d4d2"))
+		3:
+			RenderingServer.set_default_clear_color(Color(interface_settings.theme_custom_colour))
+
+
+func _on_custom_colour_picker_color_changed(color: Color) -> void:
+	ConfigHandler.save_interface_settings("theme_custom_colour", color.to_html(false))
+	if $VBoxContainer/HBoxContainer5/ThemeList.is_selected(3):
+		RenderingServer.set_default_clear_color(color)

+ 1 - 0
scenes/main/settings.gd.uid

@@ -0,0 +1 @@
+uid://co12pspac25gq

+ 125 - 0
scenes/main/settings.tscn

@@ -0,0 +1,125 @@
+[gd_scene load_steps=2 format=3 uid="uid://c1a6elrpk4eks"]
+
+[ext_resource type="Script" uid="uid://co12pspac25gq" path="res://scenes/main/settings.gd" id="1_uey6c"]
+
+[node name="Settings" type="Window"]
+auto_translate_mode = 1
+title = "SoundThread Settings"
+initial_position = 2
+size = Vector2i(500, 380)
+transient = true
+unresizable = true
+always_on_top = true
+script = ExtResource("1_uey6c")
+
+[node name="ColorRect" type="ColorRect" parent="."]
+offset_right = 604.0
+offset_bottom = 382.0
+color = Color(0.101961, 0.101961, 0.101961, 0.6)
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+offset_left = 12.0
+offset_top = 6.0
+offset_right = 490.0
+offset_bottom = 367.0
+
+[node name="WindowTitle" type="Label" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_font_sizes/font_size = 25
+text = "SoundThread Settings"
+
+[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_constants/margin_bottom = 3
+
+[node name="Label" type="Label" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_font_sizes/font_size = 18
+text = "Composers Desktop Project"
+
+[node name="ChangeCDP" type="Button" parent="VBoxContainer"]
+layout_mode = 2
+text = "Change location for cdprogs folder"
+
+[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_constants/margin_bottom = 7
+
+[node name="Label2" type="Label" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_font_sizes/font_size = 18
+text = "User Interface"
+
+[node name="HBoxContainer5" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer5"]
+custom_minimum_size = Vector2(183, 100)
+layout_mode = 2
+text = "Theme: "
+
+[node name="ThemeList" type="ItemList" parent="VBoxContainer/HBoxContainer5"]
+layout_mode = 2
+size_flags_horizontal = 3
+auto_height = true
+item_count = 4
+item_0/text = "Classic"
+item_1/text = "Dark"
+item_2/text = "Light"
+item_3/text = "Custom"
+
+[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
+layout_mode = 2
+text = "Custom theme colour: "
+
+[node name="CustomColourPicker" type="ColorPickerButton" parent="VBoxContainer/HBoxContainer"]
+custom_minimum_size = Vector2(0, 25)
+layout_mode = 2
+size_flags_horizontal = 3
+color = Color(0.184314, 0.309804, 0.305882, 1)
+edit_alpha = false
+
+[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"]
+layout_mode = 2
+text = "Disable frequency domain multiple input warning:"
+
+[node name="PvocWarning" type="CheckButton" parent="VBoxContainer/HBoxContainer2"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer3"]
+layout_mode = 2
+text = "Auto close console when thread is complete: "
+
+[node name="AutoCloseConsole" type="CheckButton" parent="VBoxContainer/HBoxContainer3"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="HBoxContainer4" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer4"]
+layout_mode = 2
+text = "Console always on top: "
+
+[node name="ConsoleAlwaysOnTop" type="CheckButton" parent="VBoxContainer/HBoxContainer4"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[connection signal="about_to_popup" from="." to="." method="_on_about_to_popup"]
+[connection signal="close_requested" from="." to="." method="_on_close_requested"]
+[connection signal="button_down" from="VBoxContainer/ChangeCDP" to="." method="_on_change_cdp_button_down"]
+[connection signal="item_selected" from="VBoxContainer/HBoxContainer5/ThemeList" to="." method="_on_theme_list_item_selected"]
+[connection signal="color_changed" from="VBoxContainer/HBoxContainer/CustomColourPicker" to="." method="_on_custom_colour_picker_color_changed"]
+[connection signal="toggled" from="VBoxContainer/HBoxContainer2/PvocWarning" to="." method="_on_pvoc_warning_toggled"]
+[connection signal="toggled" from="VBoxContainer/HBoxContainer3/AutoCloseConsole" to="." method="_on_auto_close_console_toggled"]
+[connection signal="toggled" from="VBoxContainer/HBoxContainer4/ConsoleAlwaysOnTop" to="." method="_on_console_always_on_top_toggled"]