Implemented animations on the nodes while executing
Some checks failed
Build / build (push) Has been cancelled
Some checks failed
Build / build (push) Has been cancelled
This commit is contained in:
@@ -103,6 +103,15 @@ namespace Nodify.Calculator
|
||||
GraphSerializer.Load(firstEditor.Calculator, firstEditor.Calculator.OperationsMenu);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// When the log panel is closed, clear any lingering execution animation state
|
||||
LogPanel.PanelClosed += () =>
|
||||
{
|
||||
foreach (var editor in Editors)
|
||||
{
|
||||
editor.Calculator?.ClearExecutionState();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void OnOpenInnerCalculator(EditorViewModel parentEditor, CalculatorViewModel calculator)
|
||||
|
||||
@@ -149,6 +149,14 @@ namespace Nodify.Calculator
|
||||
Operations.Add(knot);
|
||||
}
|
||||
|
||||
public void ClearExecutionState()
|
||||
{
|
||||
foreach (var op in Operations)
|
||||
op.ExecutionState = ExecutionState.None;
|
||||
foreach (var con in Connections)
|
||||
con.IsActiveInExecution = false;
|
||||
}
|
||||
|
||||
public INodifyCommand StartConnectionCommand { get; }
|
||||
public INodifyCommand CreateConnectionCommand { get; }
|
||||
public INodifyCommand DisconnectConnectorCommand { get; }
|
||||
|
||||
@@ -32,5 +32,12 @@
|
||||
set { _outputNodeId = value; }
|
||||
}
|
||||
|
||||
private bool _isActiveInExecution;
|
||||
public bool IsActiveInExecution
|
||||
{
|
||||
get => _isActiveInExecution;
|
||||
set => SetProperty(ref _isActiveInExecution, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +99,27 @@
|
||||
Target="{Binding Input.Anchor}"
|
||||
Foreground="{Binding Input.Color}"
|
||||
Stroke="{Binding Input.Color}"
|
||||
StrokeThickness="2"
|
||||
/>
|
||||
StrokeThickness="2">
|
||||
<nodify:CircuitConnection.Style>
|
||||
<Style TargetType="{x:Type nodify:CircuitConnection}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsActiveInExecution}" Value="True">
|
||||
<Setter Property="Stroke" Value="#FFFFEB3B" />
|
||||
<Setter Property="StrokeThickness" Value="5" />
|
||||
<Setter Property="StrokeDashArray" Value="4 3" />
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard RepeatBehavior="Forever">
|
||||
<DoubleAnimation Storyboard.TargetProperty="StrokeDashOffset"
|
||||
From="20" To="0" Duration="0:0:0.6" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</nodify:CircuitConnection.Style>
|
||||
</nodify:CircuitConnection>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="PendingConnectionTemplate"
|
||||
@@ -126,6 +145,36 @@
|
||||
Value="{Binding BorderBrush, Source={StaticResource AnimatedBorderPlaceholder}}" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="2" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
||||
<Setter Property="BorderBrush" Value="#FFFFC107" />
|
||||
<Setter Property="BorderThickness" Value="3" />
|
||||
<DataTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard RepeatBehavior="Forever" AutoReverse="True">
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||
From="1.0" To="0.55" Duration="0:0:0.5" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.EnterActions>
|
||||
<DataTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||
To="1.0" Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</DataTrigger.ExitActions>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Completed">
|
||||
<Setter Property="BorderBrush" Value="#FF4CAF50" />
|
||||
<Setter Property="BorderThickness" Value="3" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Error">
|
||||
<Setter Property="BorderBrush" Value="#FFF44336" />
|
||||
<Setter Property="BorderThickness" Value="3" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<SolidColorBrush x:Key="SquareConnectorColor" Color="MediumSlateBlue"></SolidColorBrush>
|
||||
<SolidColorBrush x:Key="TriangleConnectorColor" Color="White"></SolidColorBrush>
|
||||
|
||||
10
Examples/Nodify.Calculator/ExecutionState.cs
Normal file
10
Examples/Nodify.Calculator/ExecutionState.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Nodify.Calculator
|
||||
{
|
||||
public enum ExecutionState
|
||||
{
|
||||
None,
|
||||
Running,
|
||||
Completed,
|
||||
Error
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,9 @@ namespace Nodify.Calculator
|
||||
static Dictionary<string, string> outputs = new Dictionary<string, string>();
|
||||
static Dictionary<string, dynamic> variables = new Dictionary<string, dynamic>();
|
||||
|
||||
// Animation delay between node steps (milliseconds) while executing
|
||||
private const int AnimationDelayMs = 600;
|
||||
|
||||
// Auth configuration populated from the Auth node
|
||||
private string _authBaseUrl = string.Empty;
|
||||
private string _authType = string.Empty;
|
||||
@@ -76,6 +79,9 @@ namespace Nodify.Calculator
|
||||
var allnodes = calcModel.Operations;
|
||||
var allConnections = calcModel.Connections;
|
||||
|
||||
// Reset any previous execution animation state
|
||||
ResetExecutionState(calcModel);
|
||||
|
||||
// Resolve Auth node configuration before execution
|
||||
ResolveAuthNode(allnodes);
|
||||
|
||||
@@ -97,6 +103,36 @@ namespace Nodify.Calculator
|
||||
return errorString;
|
||||
}
|
||||
|
||||
private static void RunOnUI(Action action)
|
||||
{
|
||||
var dispatcher = System.Windows.Application.Current?.Dispatcher;
|
||||
if (dispatcher == null) { action(); return; }
|
||||
if (dispatcher.CheckAccess()) action();
|
||||
else dispatcher.Invoke(action);
|
||||
}
|
||||
|
||||
private void ResetExecutionState(CalculatorViewModel calc)
|
||||
{
|
||||
RunOnUI(() =>
|
||||
{
|
||||
foreach (var op in calc.Operations)
|
||||
op.ExecutionState = ExecutionState.None;
|
||||
foreach (var con in calc.Connections)
|
||||
con.IsActiveInExecution = false;
|
||||
});
|
||||
}
|
||||
|
||||
private void SetNodeState(OperationViewModel node, ExecutionState state)
|
||||
{
|
||||
RunOnUI(() => node.ExecutionState = state);
|
||||
}
|
||||
|
||||
private void SetConnectionActive(ConnectionViewModel conn, bool active)
|
||||
{
|
||||
if (conn == null) return;
|
||||
RunOnUI(() => conn.IsActiveInExecution = active);
|
||||
}
|
||||
|
||||
public static string GenerateClassFromJson(string json, string className)
|
||||
{
|
||||
// Parse the JSON to understand its structure
|
||||
@@ -947,12 +983,30 @@ namespace Nodify.Calculator
|
||||
|
||||
if (isExecute)
|
||||
{
|
||||
// Mark current node as running and animate
|
||||
SetNodeState(currentNode, ExecutionState.Running);
|
||||
System.Threading.Thread.Sleep(AnimationDelayMs / 2);
|
||||
|
||||
StartExecution(currentNode, connections);
|
||||
|
||||
// Mark current node as completed
|
||||
SetNodeState(currentNode, ExecutionState.Completed);
|
||||
|
||||
// Animate the flow connection leading to the next node
|
||||
SetConnectionActive(connection, true);
|
||||
System.Threading.Thread.Sleep(AnimationDelayMs);
|
||||
SetConnectionActive(connection, false);
|
||||
}
|
||||
|
||||
// Recursively check the next node; stop on the first failure
|
||||
if (TraverseChain(nextNode, endNodeTitle, connections, visitedNodes, isExecute))
|
||||
{
|
||||
if (isExecute)
|
||||
{
|
||||
// Mark the terminal "end" node as completed too
|
||||
if (nextNode.Title?.Equals(endNodeTitle, StringComparison.OrdinalIgnoreCase) == true)
|
||||
SetNodeState(nextNode, ExecutionState.Completed);
|
||||
}
|
||||
return true; // If a valid path to the "end" is found, exit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,17 @@ namespace Nodify.Calculator
|
||||
public bool IsOpen
|
||||
{
|
||||
get => _isOpen;
|
||||
set => SetProperty(ref _isOpen, value);
|
||||
set
|
||||
{
|
||||
var wasOpen = _isOpen;
|
||||
if (SetProperty(ref _isOpen, value) && wasOpen && !value)
|
||||
{
|
||||
PanelClosed?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action PanelClosed;
|
||||
|
||||
private bool _isRunning;
|
||||
public bool IsRunning
|
||||
|
||||
@@ -72,6 +72,15 @@ namespace Nodify.Calculator
|
||||
|
||||
public bool IsReadOnly { get; set; }
|
||||
|
||||
private ExecutionState _executionState;
|
||||
[BsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public ExecutionState ExecutionState
|
||||
{
|
||||
get => _executionState;
|
||||
set => SetProperty(ref _executionState, value);
|
||||
}
|
||||
|
||||
[BsonIgnore]
|
||||
private IOperation? _operation;
|
||||
[BsonIgnore]
|
||||
|
||||
Reference in New Issue
Block a user