349 lines
7.4 KiB
C#
349 lines
7.4 KiB
C#
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<ProgramNode, PackedScene> DSLNodes;
|
|
public NodeDisplay selectedNode;
|
|
public NodeDisplay highlightedNode;
|
|
|
|
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;
|
|
if (selectedNode == highlightedNode) highlightedNode = null;
|
|
editorWindow.RemoveChild(selectedNode);
|
|
selectedNode.QueueFree();
|
|
}
|
|
|
|
if (robot != null && Visible && robot.isExecuting)
|
|
{
|
|
UpdateCurrentNode();
|
|
}
|
|
}
|
|
|
|
private void UpdateCurrentNode()
|
|
{
|
|
if (robot.currentNode == null)
|
|
{
|
|
ClearHighlightedNode();
|
|
return;
|
|
}
|
|
|
|
if (highlightedNode != null)
|
|
{
|
|
if (IsCurrentRuntimeNode(highlightedNode))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearHighlightedNode();
|
|
}
|
|
|
|
foreach (Node child in editorWindow.GetChildren())
|
|
{
|
|
NodeDisplay nodeDisplay = child as NodeDisplay;
|
|
if (nodeDisplay == null) continue;
|
|
|
|
if (IsCurrentRuntimeNode(nodeDisplay))
|
|
{
|
|
nodeDisplay.SetHighlighted(true);
|
|
highlightedNode = nodeDisplay;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsCurrentRuntimeNode(NodeDisplay nodeDisplay)
|
|
{
|
|
return robot.currentNode.EditorNodeId != null
|
|
&& robot.currentNode.EditorNodeId.Length > 0
|
|
&& nodeDisplay.Name.ToString() == robot.currentNode.EditorNodeId;
|
|
}
|
|
|
|
private void ClearHighlightedNode()
|
|
{
|
|
if (highlightedNode == null) return;
|
|
|
|
highlightedNode.SetHighlighted(false);
|
|
highlightedNode = null;
|
|
}
|
|
|
|
public void OnMapToggled(bool toggledOn)
|
|
{
|
|
if (robot == null) return;
|
|
robot.showOnMap = toggledOn;
|
|
}
|
|
|
|
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<string> 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.TooltipText, nodeListButton);
|
|
};
|
|
nodeListButton.MouseExited += () =>
|
|
{
|
|
nodeTooltip.HideTooltip();
|
|
};
|
|
codeBlocks.AddChild(nodeListButton);
|
|
}
|
|
}
|
|
|
|
private void AddEditorNode(ProgramNode node)
|
|
{
|
|
NodeDisplay editorDisplay = DSLNodes[node].Instantiate<NodeDisplay>();
|
|
editorDisplay.PositionOffset = GetVisibleGraphCenter() - editorDisplay.Size / 2f;
|
|
editorWindow.AddChild(editorDisplay);
|
|
}
|
|
|
|
private Vector2 GetVisibleGraphCenter()
|
|
{
|
|
return (editorWindow.ScrollOffset + editorWindow.Size / 2f) / editorWindow.Zoom;
|
|
}
|
|
|
|
public void ClearWindow()
|
|
{
|
|
ClearHighlightedNode();
|
|
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<ProgramNode> 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;
|
|
ProgramNode rootNode = robot.programStartNode ?? robot.currentNode;
|
|
if (rootNode == null) return;
|
|
|
|
RunningProgramGraphBuilder builder = new RunningProgramGraphBuilder(
|
|
DSLNodes,
|
|
AddLoadedNode,
|
|
ConnectNodes
|
|
);
|
|
builder.Load(rootNode);
|
|
|
|
scriptName.Text = robot.currentProgram ?? "";
|
|
UpdateCurrentNode();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|