diff --git a/Examples/Nodify.Calculator/EditorView.xaml b/Examples/Nodify.Calculator/EditorView.xaml
index 4ca3320..9afd566 100644
--- a/Examples/Nodify.Calculator/EditorView.xaml
+++ b/Examples/Nodify.Calculator/EditorView.xaml
@@ -483,6 +483,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
_handlers.OfType().FirstOrDefault(),
+ StringConcatOperationViewModel => _handlers.OfType().FirstOrDefault(),
FunctionOperationViewModel => _handlers.OfType().FirstOrDefault(),
AuthOperationViewModel => _handlers.OfType().FirstOrDefault(),
TakeOperationViewModel => _handlers.OfType().FirstOrDefault(),
diff --git a/Examples/Nodify.Calculator/NodeHandlers/StringConcatHandler.cs b/Examples/Nodify.Calculator/NodeHandlers/StringConcatHandler.cs
new file mode 100644
index 0000000..bd5028f
--- /dev/null
+++ b/Examples/Nodify.Calculator/NodeHandlers/StringConcatHandler.cs
@@ -0,0 +1,62 @@
+using Nodify.Calculator.Models;
+using System.Drawing;
+using System.Linq;
+
+namespace Nodify.Calculator.NodeHandlers
+{
+ public class StringConcatHandler : INodeHandler
+ {
+ public string NodeTypeKey => "System";
+
+ public bool CanCreate(OperationInfoViewModel info)
+ => info.Type == OperationType.System && info.sysOp == SystemOperations.STRING_CONCAT;
+
+ public bool CanRestore(NodeData data)
+ => data.NodeType == "System" && data.SystemOp == nameof(SystemOperations.STRING_CONCAT);
+
+ public OperationViewModel Create(OperationInfoViewModel info)
+ {
+ var strColor = ConnectorViewModel.GetColorForType("string");
+ var op = new StringConcatOperationViewModel();
+
+ // Default two string inputs
+ op.Input.Add(new ConnectorViewModel { Title = "Str 1", Shape = ConnectorShape.Circle, ConnectorColor = strColor, DataType = "string" });
+ op.Input.Add(new ConnectorViewModel { Title = "Str 2", Shape = ConnectorShape.Circle, ConnectorColor = strColor, DataType = "string" });
+
+ // Single string output
+ op.Output.Add(new ConnectorViewModel
+ {
+ Title = "Result",
+ IsInput = false,
+ Shape = ConnectorShape.Circle,
+ ConnectorColor = strColor,
+ DataType = "string"
+ });
+
+ return op;
+ }
+
+ public OperationViewModel Restore(NodeData data)
+ {
+ var strColor = ConnectorViewModel.GetColorForType("string");
+ var op = new StringConcatOperationViewModel();
+
+ // Restore inputs from saved connectors
+ foreach (var ic in data.InputConnectors)
+ op.Input.Add(NodeHandlerRegistry.DeserializeConnector(ic, true));
+
+ // Restore output
+ foreach (var oc in data.OutputConnectors)
+ op.Output.Add(NodeHandlerRegistry.DeserializeConnector(oc, false));
+
+ op.Title = data.Title;
+ return op;
+ }
+
+ public void Save(OperationViewModel vm, NodeData data)
+ {
+ data.NodeType = "System";
+ data.SystemOp = nameof(SystemOperations.STRING_CONCAT);
+ }
+ }
+}
diff --git a/Examples/Nodify.Calculator/Operations/OperationFactory.cs b/Examples/Nodify.Calculator/Operations/OperationFactory.cs
index 7d5339a..8dafb6c 100644
--- a/Examples/Nodify.Calculator/Operations/OperationFactory.cs
+++ b/Examples/Nodify.Calculator/Operations/OperationFactory.cs
@@ -136,6 +136,14 @@ namespace Nodify.Calculator
assertNode.Input.Add("Actual");
assertNode.Input.Add("Expected");
+ var stringConcatNode = new OperationInfoViewModel()
+ {
+ Title = "String Concat",
+ Type = OperationType.System,
+ sysOp = SystemOperations.STRING_CONCAT,
+ IsFlowNode = false
+ };
+
systemNodes.Add(authNode);
systemNodes.Add(copynode);
systemNodes.Add(debugNode);
@@ -148,6 +156,7 @@ namespace Nodify.Calculator
systemNodes.Add(takeNode);
systemNodes.Add(forEachNode);
systemNodes.Add(assertNode);
+ systemNodes.Add(stringConcatNode);
return systemNodes;
}
diff --git a/Examples/Nodify.Calculator/StringConcatOperationViewModel.cs b/Examples/Nodify.Calculator/StringConcatOperationViewModel.cs
new file mode 100644
index 0000000..912870d
--- /dev/null
+++ b/Examples/Nodify.Calculator/StringConcatOperationViewModel.cs
@@ -0,0 +1,32 @@
+using System.Drawing;
+
+namespace Nodify.Calculator
+{
+ public class StringConcatOperationViewModel : SystemOperationViewModel
+ {
+ private const uint DefaultMinInput = 2;
+
+ public INodifyCommand AddInputCommand { get; }
+ public INodifyCommand RemoveInputCommand { get; }
+
+ public StringConcatOperationViewModel()
+ {
+ Title = "String Concat";
+ SystemOperationType = SystemOperations.STRING_CONCAT;
+
+ AddInputCommand = new RequeryCommand(
+ () => Input.Add(new ConnectorViewModel
+ {
+ Title = $"Str {Input.Count + 1}",
+ Shape = ConnectorShape.Circle,
+ ConnectorColor = ConnectorViewModel.GetColorForType("string"),
+ DataType = "string"
+ }),
+ () => true);
+
+ RemoveInputCommand = new RequeryCommand(
+ () => Input.RemoveAt(Input.Count - 1),
+ () => Input.Count > DefaultMinInput);
+ }
+ }
+}
diff --git a/Examples/Nodify.Calculator/SystemOperationViewModel.cs b/Examples/Nodify.Calculator/SystemOperationViewModel.cs
index c6e4c31..8bc0a5e 100644
--- a/Examples/Nodify.Calculator/SystemOperationViewModel.cs
+++ b/Examples/Nodify.Calculator/SystemOperationViewModel.cs
@@ -23,7 +23,8 @@ namespace Nodify.Calculator
NEW_OBJECT,
FOREACH,
ASSERT,
- KNOT
+ KNOT,
+ STRING_CONCAT
}
public class SystemOperationViewModel : OperationViewModel