Completed the split node functionality
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
@@ -24,6 +25,9 @@ namespace Nodify.Calculator
|
|||||||
c.Input.Value = c.Output.Value;
|
c.Input.Value = c.Output.Value;
|
||||||
|
|
||||||
c.Output.ValueObservers.Add(c.Input);
|
c.Output.ValueObservers.Add(c.Input);
|
||||||
|
|
||||||
|
// Dynamic Split node: populate outputs when a model connects to the Square input
|
||||||
|
HandleSplitNodeConnected(c);
|
||||||
})
|
})
|
||||||
.WhenRemoved(c =>
|
.WhenRemoved(c =>
|
||||||
{
|
{
|
||||||
@@ -41,6 +45,9 @@ namespace Nodify.Calculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.Output.ValueObservers.Remove(c.Input);
|
c.Output.ValueObservers.Remove(c.Input);
|
||||||
|
|
||||||
|
// Dynamic Split node: clear outputs when model disconnects
|
||||||
|
HandleSplitNodeDisconnected(c);
|
||||||
});
|
});
|
||||||
|
|
||||||
Operations.WhenAdded(x =>
|
Operations.WhenAdded(x =>
|
||||||
@@ -112,7 +119,16 @@ namespace Nodify.Calculator
|
|||||||
source.Shape == target.Shape &&
|
source.Shape == target.Shape &&
|
||||||
!source.IsConnected &&
|
!source.IsConnected &&
|
||||||
!target.IsConnected &&
|
!target.IsConnected &&
|
||||||
source.IsInput != target.IsInput);
|
source.IsInput != target.IsInput &&
|
||||||
|
AreDataTypesCompatible(source.DataType, target.DataType));
|
||||||
|
|
||||||
|
private static bool AreDataTypesCompatible(string sourceType, string targetType)
|
||||||
|
{
|
||||||
|
// If either side has no DataType set, allow connection (generic/untyped connector)
|
||||||
|
if (string.IsNullOrEmpty(sourceType) || string.IsNullOrEmpty(targetType))
|
||||||
|
return true;
|
||||||
|
return string.Equals(sourceType, targetType, System.StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
internal void OpenGetSetVariable(Point TargetLocation, string className)
|
internal void OpenGetSetVariable(Point TargetLocation, string className)
|
||||||
{
|
{
|
||||||
@@ -179,6 +195,91 @@ namespace Nodify.Calculator
|
|||||||
GroupSize = new Size(bounding.Width, bounding.Height)
|
GroupSize = new Size(bounding.Width, bounding.Height)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleSplitNodeConnected(ConnectionViewModel c)
|
||||||
|
{
|
||||||
|
// Determine which side is the Split node's input (Square connector)
|
||||||
|
var splitInput = c.Input.Shape == ConnectorShape.Square ? c.Input : null;
|
||||||
|
if (splitInput == null) splitInput = c.Output.Shape == ConnectorShape.Square ? c.Output : null;
|
||||||
|
if (splitInput == null) return;
|
||||||
|
|
||||||
|
var splitOp = splitInput.Operation;
|
||||||
|
if (splitOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.SPLIT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The other end is the source — find which model class it comes from
|
||||||
|
var sourceConnector = (c.Input == splitInput) ? c.Output : c.Input;
|
||||||
|
var sourceOp = sourceConnector.Operation;
|
||||||
|
|
||||||
|
// Determine class name from the source operation title (e.g. "GET ClassName" or "SET ClassName")
|
||||||
|
string className = null;
|
||||||
|
if (sourceOp is SystemOperationViewModel srcSys)
|
||||||
|
{
|
||||||
|
var title = srcSys.Title ?? "";
|
||||||
|
if (title.StartsWith("GET ")) className = title.Substring(4).Trim();
|
||||||
|
else if (title.StartsWith("SET ")) className = title.Split(' ').ElementAtOrDefault(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(className)) return;
|
||||||
|
|
||||||
|
// Read model .cs file and parse properties
|
||||||
|
var customModelDir = "CustomModels";
|
||||||
|
var filePath = Path.Combine(customModelDir, $"{className}.cs");
|
||||||
|
if (!File.Exists(filePath)) return;
|
||||||
|
|
||||||
|
var fileContent = File.ReadAllText(filePath);
|
||||||
|
var properties = OperationFactory.GetPropertiesFromClassPublic(fileContent);
|
||||||
|
if (properties == null || properties.Count == 0) return;
|
||||||
|
|
||||||
|
// Remove any existing dynamic outputs (except flow connectors)
|
||||||
|
var toRemove = splitOp.Output.Where(o => o.Shape != ConnectorShape.Triangle).ToList();
|
||||||
|
toRemove.ForEach(o =>
|
||||||
|
{
|
||||||
|
DisconnectConnector(o);
|
||||||
|
splitOp.Output.Remove(o);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add property outputs
|
||||||
|
foreach (var prop in properties)
|
||||||
|
{
|
||||||
|
var propColor = ConnectorViewModel.GetColorForType(prop.Type);
|
||||||
|
splitOp.Output.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{prop.Name} ({prop.Type})",
|
||||||
|
IsInput = false,
|
||||||
|
Shape = ConnectorShape.Circle,
|
||||||
|
ConnectorColor = propColor,
|
||||||
|
DataType = prop.Type.ToLower()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sysVm.Title = $"Split {className}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleSplitNodeDisconnected(ConnectionViewModel c)
|
||||||
|
{
|
||||||
|
var splitInput = c.Input.Shape == ConnectorShape.Square ? c.Input : null;
|
||||||
|
if (splitInput == null) splitInput = c.Output.Shape == ConnectorShape.Square ? c.Output : null;
|
||||||
|
if (splitInput == null) return;
|
||||||
|
|
||||||
|
var splitOp = splitInput.Operation;
|
||||||
|
if (splitOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.SPLIT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check if the Square input still has any connection
|
||||||
|
var stillConnected = Connections.Any(con => con.Input == splitInput || con.Output == splitInput);
|
||||||
|
if (stillConnected) return;
|
||||||
|
|
||||||
|
// Remove all dynamic output connectors and their connections
|
||||||
|
var toRemove = splitOp.Output.Where(o => o.Shape != ConnectorShape.Triangle).ToList();
|
||||||
|
toRemove.ForEach(o =>
|
||||||
|
{
|
||||||
|
DisconnectConnector(o);
|
||||||
|
splitOp.Output.Remove(o);
|
||||||
|
});
|
||||||
|
|
||||||
|
sysVm.Title = "Split";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,5 +96,37 @@ namespace Nodify.Calculator
|
|||||||
[Newtonsoft.Json.JsonIgnore]
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
[BsonIgnore]
|
[BsonIgnore]
|
||||||
public List<ConnectorViewModel> ValueObservers { get; } = new List<ConnectorViewModel>();
|
public List<ConnectorViewModel> ValueObservers { get; } = new List<ConnectorViewModel>();
|
||||||
|
|
||||||
|
private string _dataType = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// The underlying C# type of this connector (e.g. "string", "int", "double").
|
||||||
|
/// Used to enforce type-safe connections.
|
||||||
|
/// </summary>
|
||||||
|
public string DataType
|
||||||
|
{
|
||||||
|
get => _dataType;
|
||||||
|
set => SetProperty(ref _dataType, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a color based on the C# data type.
|
||||||
|
/// </summary>
|
||||||
|
public static System.Drawing.Color GetColorForType(string typeName)
|
||||||
|
{
|
||||||
|
return (typeName ?? "").ToLower() switch
|
||||||
|
{
|
||||||
|
"string" => System.Drawing.Color.CornflowerBlue,
|
||||||
|
"int" => System.Drawing.Color.LightGreen,
|
||||||
|
"double" => System.Drawing.Color.Orange,
|
||||||
|
"float" => System.Drawing.Color.Gold,
|
||||||
|
"bool" => System.Drawing.Color.Tomato,
|
||||||
|
"decimal" => System.Drawing.Color.MediumOrchid,
|
||||||
|
"long" => System.Drawing.Color.YellowGreen,
|
||||||
|
"datetime" => System.Drawing.Color.DeepSkyBlue,
|
||||||
|
"object" => System.Drawing.Color.Silver,
|
||||||
|
"list<object>" => System.Drawing.Color.Plum,
|
||||||
|
_ => System.Drawing.Color.LightGray,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
67
Examples/Nodify.Calculator/CreateModelDialog.xaml
Normal file
67
Examples/Nodify.Calculator/CreateModelDialog.xaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<Window x:Class="Nodify.Calculator.CreateModelDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Create New Model"
|
||||||
|
Width="500"
|
||||||
|
Height="420"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
Background="#2D2D30"
|
||||||
|
Foreground="White">
|
||||||
|
<Grid Margin="15">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Model/Class Name -->
|
||||||
|
<TextBlock Text="Model / Class Name:" Grid.Row="0" Margin="0 0 0 4" />
|
||||||
|
<TextBox x:Name="ModelNameBox" Grid.Row="1" Margin="0 0 0 10"
|
||||||
|
Background="#3E3E42" Foreground="White" Padding="4" />
|
||||||
|
|
||||||
|
<!-- Properties -->
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Row="2" Margin="0 0 0 4">
|
||||||
|
<TextBlock Text="Properties:" FontWeight="Bold" />
|
||||||
|
<Button Content="➕ Add" Margin="10 0 0 0" Padding="8 2" Click="AddProperty_Click"
|
||||||
|
Background="#3E3E42" Foreground="White" Cursor="Hand" />
|
||||||
|
</StackPanel>
|
||||||
|
<DataGrid x:Name="PropertiesGrid" Grid.Row="3" Margin="0 0 0 10"
|
||||||
|
AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True"
|
||||||
|
Background="#3E3E42" Foreground="White" RowBackground="#3E3E42"
|
||||||
|
AlternatingRowBackground="#333337" GridLinesVisibility="None"
|
||||||
|
HeadersVisibility="Column" BorderBrush="#555">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
|
||||||
|
<DataGridComboBoxColumn Header="Type" SelectedItemBinding="{Binding Type}" Width="120">
|
||||||
|
<DataGridComboBoxColumn.ItemsSource>
|
||||||
|
<x:Array Type="sys:String" xmlns:sys="clr-namespace:System;assembly=System.Runtime">
|
||||||
|
<sys:String>string</sys:String>
|
||||||
|
<sys:String>int</sys:String>
|
||||||
|
<sys:String>double</sys:String>
|
||||||
|
<sys:String>bool</sys:String>
|
||||||
|
<sys:String>float</sys:String>
|
||||||
|
<sys:String>decimal</sys:String>
|
||||||
|
<sys:String>long</sys:String>
|
||||||
|
<sys:String>DateTime</sys:String>
|
||||||
|
<sys:String>object</sys:String>
|
||||||
|
<sys:String>List<string></sys:String>
|
||||||
|
<sys:String>List<int></sys:String>
|
||||||
|
<sys:String>List<object></sys:String>
|
||||||
|
</x:Array>
|
||||||
|
</DataGridComboBoxColumn.ItemsSource>
|
||||||
|
</DataGridComboBoxColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="4">
|
||||||
|
<Button Content="Create" Width="80" Margin="0 0 10 0" Padding="4"
|
||||||
|
Click="OnCreateClick" IsDefault="True" />
|
||||||
|
<Button Content="Cancel" Width="80" Padding="4"
|
||||||
|
Click="OnCancelClick" IsCancel="True" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
55
Examples/Nodify.Calculator/CreateModelDialog.xaml.cs
Normal file
55
Examples/Nodify.Calculator/CreateModelDialog.xaml.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Nodify.Calculator
|
||||||
|
{
|
||||||
|
public class ModelPropertyInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Type { get; set; } = "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class CreateModelDialog : Window
|
||||||
|
{
|
||||||
|
public string ModelName { get; private set; } = string.Empty;
|
||||||
|
public List<ModelPropertyInfo> Properties { get; } = new List<ModelPropertyInfo>();
|
||||||
|
|
||||||
|
private readonly ObservableCollection<ModelPropertyInfo> _properties = new ObservableCollection<ModelPropertyInfo>();
|
||||||
|
|
||||||
|
public CreateModelDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
PropertiesGrid.ItemsSource = _properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProperty_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_properties.Add(new ModelPropertyInfo { Name = $"Property{_properties.Count + 1}", Type = "string" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreateClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ModelNameBox.Text))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please enter a model name.", "Validation", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_properties.Count == 0)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Please add at least one property.", "Validation", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelName = ModelNameBox.Text.Trim();
|
||||||
|
Properties.AddRange(_properties);
|
||||||
|
DialogResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -678,8 +678,12 @@
|
|||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
<!-- Separator -->
|
<!-- Separator -->
|
||||||
<TextBlock Text="Models" FontWeight="Bold" Margin="0 8 0 4"
|
<StackPanel Orientation="Horizontal" Margin="0 8 0 4">
|
||||||
Visibility="{Binding Calculator.OperationsMenu.AvailableModels.Count, Converter={shared:BooleanToVisibilityConverter}}" />
|
<TextBlock Text="Models" FontWeight="Bold" VerticalAlignment="Center" />
|
||||||
|
<Button Content="➕" Margin="8 0 0 0" Padding="6 1" FontSize="10" Cursor="Hand"
|
||||||
|
Command="{Binding Calculator.OperationsMenu.CreateModelCommand}"
|
||||||
|
Background="#3E3E42" Foreground="White" ToolTip="Create New Model" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Class Model Variables -->
|
<!-- Class Model Variables -->
|
||||||
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableModels}"
|
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableModels}"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace Nodify.Calculator
|
|||||||
IsFlowNode = false
|
IsFlowNode = false
|
||||||
};
|
};
|
||||||
splitNode.Input.Add("");
|
splitNode.Input.Add("");
|
||||||
splitNode.Output.Add("");
|
// No default output - outputs are added dynamically on connection
|
||||||
|
|
||||||
var takeNode = new OperationInfoViewModel()
|
var takeNode = new OperationInfoViewModel()
|
||||||
{
|
{
|
||||||
@@ -330,6 +330,19 @@ namespace Nodify.Calculator
|
|||||||
SystemOperationType = info.sysOp
|
SystemOperationType = info.sysOp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (info.sysOp == SystemOperations.SPLIT)
|
||||||
|
{
|
||||||
|
sysOp.Title = info.Title;
|
||||||
|
// Square input to accept model/class objects
|
||||||
|
foreach (var item in input)
|
||||||
|
{
|
||||||
|
item.Shape = ConnectorShape.Square;
|
||||||
|
item.ConnectorColor = Color.MediumPurple;
|
||||||
|
sysOp.Input.Add(item);
|
||||||
|
}
|
||||||
|
return sysOp;
|
||||||
|
}
|
||||||
|
|
||||||
if (info.sysOp == SystemOperations.GET_SET && info.IsModelNode && !info.IsSimpleVariable)
|
if (info.sysOp == SystemOperations.GET_SET && info.IsModelNode && !info.IsSimpleVariable)
|
||||||
{
|
{
|
||||||
if (info.Title == "GET")
|
if (info.Title == "GET")
|
||||||
@@ -345,44 +358,100 @@ namespace Nodify.Calculator
|
|||||||
var flpath = System.IO.Path.Combine(customModelDir, info.ClassName + ".cs");
|
var flpath = System.IO.Path.Combine(customModelDir, info.ClassName + ".cs");
|
||||||
if (File.Exists(flpath))
|
if (File.Exists(flpath))
|
||||||
{
|
{
|
||||||
//Read all the properties from the file
|
|
||||||
var fileContent = File.ReadAllText(flpath);
|
var fileContent = File.ReadAllText(flpath);
|
||||||
|
|
||||||
// Parse and analyze properties
|
|
||||||
var properties = GetPropertiesFromClass(fileContent);
|
var properties = GetPropertiesFromClass(fileContent);
|
||||||
|
|
||||||
// Print out the extracted properties and their types
|
|
||||||
Console.WriteLine("Properties found:");
|
Console.WriteLine("Properties found:");
|
||||||
foreach (var property in properties)
|
foreach (var property in properties)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Property Name: {property.Name}, Type: {property.Type}");
|
Console.WriteLine($"Property Name: {property.Name}, Type: {property.Type}");
|
||||||
info.Output.Add(property.Name);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
info.IsFlowNode = true;
|
||||||
|
|
||||||
|
// Build the node with flow connectors first
|
||||||
|
var flTitle = $"{info.Title} {info.ClassName}";
|
||||||
|
sysOp.Title = flTitle;
|
||||||
|
|
||||||
|
// Flow connectors
|
||||||
|
sysOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||||
|
sysOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||||
|
|
||||||
|
// Input: Square connector for the class object
|
||||||
|
foreach (var item in input)
|
||||||
|
{
|
||||||
|
item.Shape = ConnectorShape.Square;
|
||||||
|
item.ConnectorColor = Color.MediumPurple;
|
||||||
|
sysOp.Input.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs: Circle connectors per property, colored by type
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var propColor = ConnectorViewModel.GetColorForType(property.Type);
|
||||||
|
var propType = property.Type.ToLower();
|
||||||
|
sysOp.Output.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = $"{property.Name} ({property.Type})",
|
||||||
|
IsInput = false,
|
||||||
|
Shape = ConnectorShape.Circle,
|
||||||
|
ConnectorColor = propColor,
|
||||||
|
DataType = propType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sysOp;
|
||||||
|
}
|
||||||
|
|
||||||
info.IsFlowNode = true;
|
info.IsFlowNode = true;
|
||||||
}
|
}
|
||||||
var flTitle = $"{info.Title} {info.ClassName}";
|
var flTitle2 = $"{info.Title} {info.ClassName}";
|
||||||
sysOp.Title = flTitle;
|
sysOp.Title = flTitle2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.sysOp == SystemOperations.GET_SET && info.IsSimpleVariable)
|
if (info.sysOp == SystemOperations.GET_SET && info.IsSimpleVariable)
|
||||||
{
|
{
|
||||||
var varLabel = $"{info.Title} {info.ClassName} ({info.VariableType})";
|
var varLabel = $"{info.Title} {info.ClassName} ({info.VariableType})";
|
||||||
sysOp.Title = varLabel;
|
sysOp.Title = varLabel;
|
||||||
|
var varType = info.VariableType.ToLower();
|
||||||
|
var varColor = ConnectorViewModel.GetColorForType(varType);
|
||||||
|
|
||||||
if (info.Title == "GET")
|
if (info.Title == "GET")
|
||||||
{
|
{
|
||||||
// GET variable: output the value, no flow needed
|
// GET variable: output the value, no flow needed
|
||||||
info.Output.Add("Value");
|
|
||||||
info.IsFlowNode = false;
|
info.IsFlowNode = false;
|
||||||
|
|
||||||
|
// Flow connectors not needed, output connector with type color
|
||||||
|
sysOp.Output.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = "Value",
|
||||||
|
IsInput = false,
|
||||||
|
Shape = ConnectorShape.Circle,
|
||||||
|
ConnectorColor = varColor,
|
||||||
|
DataType = varType
|
||||||
|
});
|
||||||
|
|
||||||
|
return sysOp;
|
||||||
}
|
}
|
||||||
else if (info.Title == "SET")
|
else if (info.Title == "SET")
|
||||||
{
|
{
|
||||||
// SET variable: input connector for the value, flow node
|
// SET variable: input connector for the value, flow node
|
||||||
info.Input.Add("Value");
|
|
||||||
info.IsFlowNode = true;
|
info.IsFlowNode = true;
|
||||||
|
|
||||||
|
// Flow connectors
|
||||||
|
sysOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
|
||||||
|
sysOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
|
||||||
|
|
||||||
|
// Data input with type color
|
||||||
|
sysOp.Input.Add(new ConnectorViewModel
|
||||||
|
{
|
||||||
|
Title = "Value",
|
||||||
|
Shape = ConnectorShape.Circle,
|
||||||
|
ConnectorColor = varColor,
|
||||||
|
DataType = varType
|
||||||
|
});
|
||||||
|
|
||||||
|
return sysOp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,6 +534,8 @@ namespace Nodify.Calculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<CustomProperty> GetPropertiesFromClassPublic(string classContent) => GetPropertiesFromClass(classContent);
|
||||||
|
|
||||||
static List<CustomProperty> GetPropertiesFromClass(string classContent)
|
static List<CustomProperty> GetPropertiesFromClass(string classContent)
|
||||||
{
|
{
|
||||||
// Parse the C# class content using Roslyn
|
// Parse the C# class content using Roslyn
|
||||||
@@ -500,7 +571,7 @@ namespace Nodify.Calculator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomProperty // <- Renamed to avoid conflicts with System.Reflection.PropertyInfo
|
public class CustomProperty // <- Renamed to avoid conflicts with System.Reflection.PropertyInfo
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ namespace Nodify.Calculator
|
|||||||
ImportSwaggerCommand = new DelegateCommand(ImportSwagger);
|
ImportSwaggerCommand = new DelegateCommand(ImportSwagger);
|
||||||
AddVariableCommand = new DelegateCommand(AddVariable);
|
AddVariableCommand = new DelegateCommand(AddVariable);
|
||||||
CreateFunctionCommand = new DelegateCommand(CreateFunction);
|
CreateFunctionCommand = new DelegateCommand(CreateFunction);
|
||||||
|
CreateModelCommand = new DelegateCommand(CreateModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -184,6 +185,46 @@ namespace Nodify.Calculator
|
|||||||
public INodifyCommand ImportSwaggerCommand { get; }
|
public INodifyCommand ImportSwaggerCommand { get; }
|
||||||
public INodifyCommand AddVariableCommand { get; }
|
public INodifyCommand AddVariableCommand { get; }
|
||||||
public INodifyCommand CreateFunctionCommand { get; }
|
public INodifyCommand CreateFunctionCommand { get; }
|
||||||
|
public INodifyCommand CreateModelCommand { get; }
|
||||||
|
|
||||||
|
private void CreateModel()
|
||||||
|
{
|
||||||
|
var dialog = new CreateModelDialog();
|
||||||
|
dialog.Owner = System.Windows.Application.Current.MainWindow;
|
||||||
|
if (dialog.ShowDialog() != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var className = dialog.ModelName;
|
||||||
|
|
||||||
|
if (AvailableModels.Any(m => m.Title == className))
|
||||||
|
{
|
||||||
|
MessageBox.Show($"A model named '{className}' already exists.", "Duplicate Model", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate .cs file
|
||||||
|
var customModelDir = "CustomModels";
|
||||||
|
Directory.CreateDirectory(customModelDir);
|
||||||
|
var sb = new System.Text.StringBuilder();
|
||||||
|
sb.AppendLine($"public class {className}");
|
||||||
|
sb.AppendLine("{");
|
||||||
|
foreach (var prop in dialog.Properties)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"\tpublic {prop.Type} {prop.Name} {{ get; set; }}");
|
||||||
|
}
|
||||||
|
sb.AppendLine("}");
|
||||||
|
File.WriteAllText(Path.Combine(customModelDir, $"{className}.cs"), sb.ToString());
|
||||||
|
|
||||||
|
var modelInfo = new OperationInfoViewModel
|
||||||
|
{
|
||||||
|
Title = className,
|
||||||
|
IsModelNode = true,
|
||||||
|
Type = OperationType.System,
|
||||||
|
sysOp = SystemOperations.GET_SET,
|
||||||
|
ClassName = className
|
||||||
|
};
|
||||||
|
AvailableModels.Add(modelInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private void CreateFunction()
|
private void CreateFunction()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user