using Godot; using Godot.Collections; using System.Collections.Generic; public partial class CodingWindow : PanelContainer { private Robot robot; [Export] VBoxContainer codeBlocks; [Export] GraphEdit editorWindow; [Export] OptionButton availableScripts; [Export] LineEdit scriptName; [Export] LineEdit nameInput; [Export] NodeTooltip nodeTooltip; public System.Collections.Generic.Dictionary DSLNodes; public NodeDisplay selectedNode; public override void _Ready() { DSLNodes = ResourceLoader.LoadDSLNodes(); GenerateCodingBlocks(); } public override void _Notification(int id) { if (id == NotificationVisibilityChanged) { if (Visible) LoadWindow(); } } public override void _Process(double delta) { if (Input.IsActionJustPressed("delete_node")) { Control focused = GetViewport().GuiGetFocusOwner(); if (focused is LineEdit || focused is TextEdit) return; if (selectedNode == null) return; editorWindow.RemoveChild(selectedNode); selectedNode.QueueFree(); } } public void OnNodeSelect(NodeDisplay node) { selectedNode = node; } public void OnNodeDeselect(NodeDisplay node) { selectedNode = null; } private void LoadWindow() { if (robot == null) return; nameInput.Text = robot.Name; SetupScriptOptions(); ClearWindow(); LoadTemporaryProgram(); } private void SetupScriptOptions() { availableScripts.Clear(); availableScripts.AddItem("Select script to load..."); List scripts = FileHandler.LoadProgramNames(); scripts.Sort((a, b) => a.CompareTo(b)); foreach (string script in scripts) { availableScripts.AddItem(script); } } public void SaveRobotName() { if (robot == null) return; robot.Name = nameInput.Text; } public void CloseWindow() { Hide(); } public void GenerateCodingBlocks() { foreach (ProgramNode nodeTemplate in DSLNodes.Keys) { Button nodeListButton = new Button { Name = nodeTemplate.DisplayText, Text = nodeTemplate.DisplayText }; nodeListButton.Pressed += () => { AddEditorNode(nodeTemplate); }; nodeListButton.MouseEntered += () => { nodeTooltip.ShowTooltip(nodeTemplate.DisplayText, nodeTemplate.DisplayText, nodeListButton); }; nodeListButton.MouseExited += () => { nodeTooltip.HideTooltip(); }; codeBlocks.AddChild(nodeListButton); } } private void AddEditorNode(ProgramNode node) { NodeDisplay editorDisplay = DSLNodes[node].Instantiate(); editorDisplay.PositionOffset = GetVisibleGraphCenter() - editorDisplay.Size / 2f; editorWindow.AddChild(editorDisplay); } private Vector2 GetVisibleGraphCenter() { return (editorWindow.ScrollOffset + editorWindow.Size / 2f) / editorWindow.Zoom; } public void ClearWindow() { DisconnectAllNodes(); RemoveEditorNodes(); scriptName.Text = ""; } private void DisconnectAllNodes() { foreach (Dictionary connection in editorWindow.GetConnectionList()) { editorWindow.DisconnectNode( connection["from_node"].AsStringName(), (int)connection["from_port"], connection["to_node"].AsStringName(), (int)connection["to_port"] ); } } private void RemoveEditorNodes() { foreach (Node child in editorWindow.GetChildren()) { if (child is not GraphNode) continue; editorWindow.RemoveChild(child); child.QueueFree(); } } public void CompileProgram() { if (robot == null) return; ScriptGraphCompiler compiler = new ScriptGraphCompiler(editorWindow); string errorMessage; List nodes = compiler.BuildProgram(out errorMessage); if (errorMessage.Length > 0) { robot.StopExecution(errorMessage); return; } if (nodes.Count > 0) robot.SetupExecution(nodes); robot.currentProgram = GetCurrentScriptName(); } private string GetCurrentScriptName() { if (scriptName.Text.Length > 0) return scriptName.Text; return $"Script{availableScripts.ItemCount}"; } public void SetRobot(Robot robot) { this.robot = robot; } public void LoadProgram(int index) { if (index <= 0) return; ClearWindow(); string scriptContent = FileHandler.LoadProgram(availableScripts.GetItemText(index)); CreateSerializer().Load(scriptContent); scriptName.Text = availableScripts.GetItemText(index); availableScripts.Select(0); } private void AddLoadedNode(NodeDisplay nodeDisplay) { editorWindow.AddChild(nodeDisplay); } public void LoadTemporaryProgram() { if (robot == null) return; if (robot.currentNode == null) return; RunningProgramGraphBuilder builder = new RunningProgramGraphBuilder( DSLNodes, AddLoadedNode, ConnectNodes ); builder.Load(robot.currentNode); scriptName.Text = robot.currentProgram ?? ""; } public void DeleteProgram() { string filename = scriptName.Text; int selectedIndex = availableScripts.GetSelectedId(); if (selectedIndex > 0) { filename = availableScripts.GetItemText(selectedIndex); } if (filename.Length <= 0) return; if (!FileHandler.DeleteProgram(filename)) return; ClearWindow(); SetupScriptOptions(); } public void SaveProgram() { string result = CreateSerializer().Save(); if (result.Length <= 0) return; FileHandler.SaveProgram(GetCurrentScriptName(), result); SetupScriptOptions(); } public void OnNodeConnect(StringName from, int fromPort, StringName to, int toPort) { if (to == from) return; ConnectNodes(from, fromPort, to, toPort); } private void ConnectNodes(StringName from, int fromPort, StringName to, int toPort) { if (HasOutputConnection(from, fromPort)) return; editorWindow.ConnectNode(from, fromPort, to, toPort); } private bool HasOutputConnection(StringName from, int fromPort) { foreach (Dictionary connection in editorWindow.GetConnectionList()) { if (connection["from_node"].AsStringName() != from) continue; if ((int)connection["from_port"] != fromPort) continue; return true; } return false; } private ScriptGraphSerializer CreateSerializer() { return new ScriptGraphSerializer( editorWindow, DSLNodes, AddLoadedNode, ConnectNodes ); } public void OnNodeDisconnect(StringName from, int fromPort, StringName to, int toPort) { editorWindow.DisconnectNode(from, fromPort, to, toPort); } }