From afb1882fbd9ce6a0b78a0155350959540525d6de Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Sat, 4 Jan 2025 22:25:42 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=BF=9E=E6=8E=A5=E6=97=B6?=
=?UTF-8?q?=E4=B8=B4=E6=97=B6=E7=BA=BF=E7=9A=84=E8=A1=A8=E7=8E=B0=E8=83=BD?=
=?UTF-8?q?=E5=8A=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Api/INodeContainerControl.cs | 32 ++
Serein.Workbench.Avalonia/Api/INodeControl.cs | 24 +-
.../Node/ViewModels/ActionNodeViewModel.cs | 1 +
.../Node/ViewModels/NodeViewModelBase.cs | 119 ++++++-
.../Custom/Node/Views/ActionNodeView.axaml | 13 +-
.../Custom/Node/Views/ActionNodeView.axaml.cs | 15 +-
.../Custom/Node/Views/NodeControlBase.cs | 57 ++++
.../Custom/Views/ConnectionLineShape.cs | 75 +++--
.../Custom/Views/NodeConnectionLineView.cs | 279 ++++++++++++++++
.../Custom/Views/NodeContainerView.axaml.cs | 6 +-
.../Custom/Views/NodeJunctionView.axaml.cs | 267 ++++++++++------
.../Views/ParameterDetailsInfoView.axaml | 18 +-
.../LibraryMethodInfoDataTemplate.cs | 8 +-
.../Model/ConnectingData.cs | 69 ++--
.../Model/NodeConnectionLine.cs | 41 +++
.../Serein.Workbench.Avalonia.csproj | 1 +
.../Services/NodeOperationService.cs | 301 +++++++++++++++---
.../Views/MainView.axaml | 2 +-
18 files changed, 1075 insertions(+), 253 deletions(-)
create mode 100644 Serein.Workbench.Avalonia/Api/INodeContainerControl.cs
create mode 100644 Serein.Workbench.Avalonia/Custom/Node/Views/NodeControlBase.cs
create mode 100644 Serein.Workbench.Avalonia/Custom/Views/NodeConnectionLineView.cs
create mode 100644 Serein.Workbench.Avalonia/Model/NodeConnectionLine.cs
diff --git a/Serein.Workbench.Avalonia/Api/INodeContainerControl.cs b/Serein.Workbench.Avalonia/Api/INodeContainerControl.cs
new file mode 100644
index 0000000..b826be2
--- /dev/null
+++ b/Serein.Workbench.Avalonia/Api/INodeContainerControl.cs
@@ -0,0 +1,32 @@
+using Serein.Workbench.Avalonia.Custom.Node.Views;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Avalonia.Api
+{
+ ///
+ /// 约束具有容器功能的节点控件应该有什么方法
+ ///
+ public interface INodeContainerControl
+ {
+ ///
+ /// 放置一个节点
+ ///
+ ///
+ bool PlaceNode(NodeControlBase nodeControl);
+
+ ///
+ /// 取出一个节点
+ ///
+ ///
+ bool TakeOutNode(NodeControlBase nodeControl);
+
+ ///
+ /// 取出所有节点(用于删除容器)
+ ///
+ void TakeOutAll();
+ }
+}
diff --git a/Serein.Workbench.Avalonia/Api/INodeControl.cs b/Serein.Workbench.Avalonia/Api/INodeControl.cs
index ed1158f..06770d2 100644
--- a/Serein.Workbench.Avalonia/Api/INodeControl.cs
+++ b/Serein.Workbench.Avalonia/Api/INodeControl.cs
@@ -7,17 +7,17 @@ using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Api
{
- internal interface INodeControl
- {
- ///
- /// 对应的节点实体
- ///
- NodeModelBase NodeModelBase { get; }
+ //internal interface INodeControl
+ //{
+ // ///
+ // /// 对应的节点实体
+ // ///
+ // NodeModelBase NodeModelBase { get; }
- ///
- /// 初始化使用的方法,设置节点实体
- ///
- ///
- void SetNodeModel(NodeModelBase nodeModel);
- }
+ // ///
+ // /// 初始化使用的方法,设置节点实体
+ // ///
+ // ///
+ // void SetNodeModel(NodeModelBase nodeModel);
+ //}
}
diff --git a/Serein.Workbench.Avalonia/Custom/Node/ViewModels/ActionNodeViewModel.cs b/Serein.Workbench.Avalonia/Custom/Node/ViewModels/ActionNodeViewModel.cs
index 0429f57..9ef41a3 100644
--- a/Serein.Workbench.Avalonia/Custom/Node/ViewModels/ActionNodeViewModel.cs
+++ b/Serein.Workbench.Avalonia/Custom/Node/ViewModels/ActionNodeViewModel.cs
@@ -15,6 +15,7 @@ namespace Serein.Workbench.Avalonia.Custom.Node.ViewModels
[ObservableProperty]
private SingleActionNode? nodeMoel;
+
internal override NodeModelBase NodeModelBase
{ get => NodeMoel ?? throw new NotImplementedException(); set => NodeMoel = (SingleActionNode)value; }
diff --git a/Serein.Workbench.Avalonia/Custom/Node/ViewModels/NodeViewModelBase.cs b/Serein.Workbench.Avalonia/Custom/Node/ViewModels/NodeViewModelBase.cs
index 27b878f..d55f24f 100644
--- a/Serein.Workbench.Avalonia/Custom/Node/ViewModels/NodeViewModelBase.cs
+++ b/Serein.Workbench.Avalonia/Custom/Node/ViewModels/NodeViewModelBase.cs
@@ -1,5 +1,10 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using Avalonia.Controls;
+using Avalonia.Media;
+using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
+using Serein.Workbench.Avalonia.Api;
+using Serein.Workbench.Avalonia.Custom.Node.Views;
+using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
@@ -15,5 +20,117 @@ namespace Serein.Workbench.Avalonia.Custom.Node.ViewModels
internal abstract class NodeViewModelBase : ViewModelBase
{
internal abstract NodeModelBase NodeModelBase { get; set; }
+
+ private Canvas NodeCanvas;
+
+ ///
+ /// 如果该节点放置在了某个容器节点,就会记录这个容器节点
+ ///
+ private INodeContainerControl NodeContainerControl { get; }
+
+ public NodeModelBase NodeModel { get; set; }
+
+ ///
+ /// 记录与该节点控件有关的所有连接
+ ///
+ private readonly List connectionControls = new List();
+
+ //public NodeControlViewModelBase ViewModel { get; set; }
+
+
+
+ public void SetNodeModel(NodeModelBase nodeModel) => this.NodeModel = nodeModel;
+
+
+ ///
+ /// 添加与该节点有关的连接后,记录下来
+ ///
+ ///
+ public void AddCnnection(NodeConnectionLineView connection)
+ {
+ connectionControls.Add(connection);
+ }
+
+ ///
+ /// 删除了连接之后,还需要从节点中的记录移除
+ ///
+ ///
+ public void RemoveConnection(NodeConnectionLineView connection)
+ {
+ connectionControls.Remove(connection);
+ //connection.Remote();
+ }
+
+ ///
+ /// 删除所有连接
+ ///
+ public void RemoveAllConection()
+ {
+ foreach (var connection in this.connectionControls)
+ {
+ //connection.Remote();
+ }
+ }
+
+ ///
+ /// 更新与该节点有关的数据
+ ///
+ public void UpdateLocationConnections()
+ {
+ foreach (var connection in this.connectionControls)
+ {
+ //connection.RefreshLine(); // 主动更新连线位置
+ }
+ }
+
+
+ ///
+ /// 设置绑定:
+ /// Canvas.X and Y : 画布位置
+ ///
+ public void SetBinding()
+ {
+ /* // 绑定 Canvas.Left
+ Binding leftBinding = new Binding("X")
+ {
+ Source = ViewModel.NodeModel.Position, // 如果 X 属性在当前 DataContext 中
+ Mode = BindingMode.TwoWay
+ };
+ BindingOperations.Apply(this, Canvas.LeftProperty, leftBinding);
+
+ // 绑定 Canvas.Top
+ Binding topBinding = new Binding("Y")
+ {
+ Source = ViewModel.NodeModel.Position, // 如果 Y 属性在当前 DataContext 中
+ Mode = BindingMode.TwoWay
+ };
+ BindingOperations.SetBinding(this, Canvas.TopProperty, topBinding);*/
+ }
+
+ ///
+ /// 穿透视觉树获取指定类型的第一个元素
+ ///
+ ///
+ ///
+ ///
+ //protected T FindVisualChild(DependencyObject parent) where T : DependencyObject
+ //{
+ // for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
+ // {
+ // var child = VisualTreeHelper.GetChild(parent, i);
+ // if (child is T typedChild)
+ // {
+ // return typedChild;
+ // }
+
+ // var childOfChild = FindVisualChild(child);
+ // if (childOfChild != null)
+ // {
+ // return childOfChild;
+ // }
+ // }
+ // return null;
+ //}
+
}
}
diff --git a/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml b/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml
index e24738a..372387a 100644
--- a/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml
+++ b/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml
@@ -1,15 +1,16 @@
-
@@ -17,7 +18,7 @@
-
+
@@ -42,8 +43,8 @@
-
-
+
+
-
+
diff --git a/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml.cs b/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml.cs
index 099852e..c669790 100644
--- a/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml.cs
+++ b/Serein.Workbench.Avalonia/Custom/Node/Views/ActionNodeView.axaml.cs
@@ -2,25 +2,22 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Serein.Library;
+using Serein.NodeFlow.Model;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.ViewModels;
namespace Serein.Workbench.Avalonia.Custom.Node.Views;
-public partial class ActionNodeView : UserControl, INodeControl
+public partial class ActionNodeView : NodeControlBase
{
private ActionNodeViewModel _vm;
+
+
public ActionNodeView()
{
InitializeComponent();
- _vm = App.GetService();
- DataContext = _vm;
+ //_vm = App.GetService();
+ //DataContext = _vm;
}
- NodeModelBase INodeControl.NodeModelBase => _vm.NodeModelBase ?? throw new System.NotImplementedException(); // ڵ
-
- void INodeControl.SetNodeModel(NodeModelBase nodeModel) // ڵ
- {
- _vm.NodeModelBase = nodeModel;
- }
}
\ No newline at end of file
diff --git a/Serein.Workbench.Avalonia/Custom/Node/Views/NodeControlBase.cs b/Serein.Workbench.Avalonia/Custom/Node/Views/NodeControlBase.cs
new file mode 100644
index 0000000..151799d
--- /dev/null
+++ b/Serein.Workbench.Avalonia/Custom/Node/Views/NodeControlBase.cs
@@ -0,0 +1,57 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Media;
+using Serein.Library;
+using Serein.Workbench.Avalonia.Api;
+using Serein.Workbench.Avalonia.Custom.Views;
+using Serein.Workbench.Avalonia.Model;
+using Serein.Workbench.Avalonia.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Avalonia.Custom.Node.Views
+{
+ public class NodeControlBase : UserControl
+ {
+ protected NodeControlBase()
+ {
+ this.Background = Brushes.Transparent;
+ }
+
+ ///
+ /// 放置在某个节点容器中
+ ///
+ public void PlaceToContainer(INodeContainerControl nodeContainerControl)
+ {
+ //this.nodeContainerControl = nodeContainerControl;
+ //NodeCanvas.Children.Remove(this); // 临时从画布上移除
+ //var result = nodeContainerControl.PlaceNode(this);
+ //if (!result) // 检查是否放置成功,如果不成功,需要重新添加回来
+ //{
+ // NodeCanvas.Children.Add(this); // 从画布上移除
+
+ //}
+ }
+
+ ///
+ /// 从某个节点容器取出
+ ///
+ public void TakeOutContainer()
+ {
+ //var result = nodeContainerControl.TakeOutNode(this); // 从控件取出
+ //if (result) // 移除成功时才添加到画布上
+ //{
+ // NodeCanvas.Children.Add(this); // 重新添加到画布上
+ // if (nodeContainerControl is NodeControlBase containerControl)
+ // {
+ // NodeModel.Position.X = NodeModel.Position.X + containerControl.Width + 10;
+ // NodeModel.Position.Y = NodeModel.Position.Y;
+ // }
+ //}
+
+ }
+ }
+}
diff --git a/Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs b/Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs
index 60f822f..bf79df9 100644
--- a/Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs
+++ b/Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs
@@ -23,23 +23,22 @@ namespace Serein.Workbench.Avalonia.Custom.Views
{
private readonly double strokeThickness;
-
///
- /// 确定起始坐标和目标坐标、外光样式的曲线
+ /// 确定起始坐标和目标坐标、外观样式的曲线
///
- /// 起始坐标
- /// 结束坐标
+ /// 起始坐标
+ /// 结束坐标
/// 颜色
/// 是否为虚线
- public ConnectionLineShape(Point start,
- Point end,
+ public ConnectionLineShape(Point left,
+ Point right,
Brush brush,
bool isDotted = false,
bool isTop = false)
{
this.brush = brush;
- startPoint = start;
- endPoint = end;
+ this.leftPoint = left;
+ this.rightPoint = right;
this.strokeThickness = 4;
InitElementPoint(isDotted, isTop);
InvalidateVisual(); // 触发重绘
@@ -50,9 +49,10 @@ namespace Serein.Workbench.Avalonia.Custom.Views
{
//hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
//hitVisiblePen.Freeze(); // Freeze以提高性能
+
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
opacity = 1.0d;
- var dashStyle = new DashStyle();
+ //var dashStyle = new DashStyle();
if (isDotted)
{
@@ -71,31 +71,44 @@ namespace Serein.Workbench.Avalonia.Custom.Views
///
/// 更新线条落点位置
///
- ///
- ///
- public void UpdatePoints(Point start, Point end)
+ ///
+ ///
+ public void UpdatePoint(Point left, Point right, Brush? brush = null)
{
- startPoint = start;
- endPoint = end;
+ if(brush is not null)
+ {
+ visualPen = new Pen(brush, 3.0); // 默认可视化Pen
+ }
+ this.leftPoint = left;
+ this.rightPoint = right;
InvalidateVisual(); // 触发重绘
}
///
/// 更新线条落点位置
///
- ///
- public void UpdateEndPoints(Point point)
+ ///
+ public void UpdateRightPoint(Point right, Brush? brush = null)
{
- endPoint = point;
+ if (brush is not null)
+ {
+ visualPen = new Pen(brush, 3.0); // 默认可视化Pen
+ }
+ this.rightPoint = right;
InvalidateVisual(); // 触发重绘
}
+
///
/// 更新线条起点位置
///
- ///
- public void UpdateStartPoints(Point point)
+ ///
+ public void UpdateLeftPoints(Point left, Brush? brush = null)
{
- startPoint = point;
+ if (brush is not null)
+ {
+ visualPen = new Pen(brush, 3.0); // 默认可视化Pen
+ }
+ this.leftPoint = left;
InvalidateVisual(); // 触发重绘
}
@@ -106,7 +119,7 @@ namespace Serein.Workbench.Avalonia.Custom.Views
public override void Render(DrawingContext drawingContext)
{
// 刷新线条显示位置
- DrawBezierCurve(drawingContext, startPoint, endPoint);
+ DrawBezierCurve(drawingContext, leftPoint, rightPoint);
}
#region 重绘
@@ -116,8 +129,8 @@ namespace Serein.Workbench.Avalonia.Custom.Views
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
//private Pen hitVisiblePen; // 初始化碰撞检测线
private Pen visualPen; // 默认可视化Pen
- private Point startPoint; // 连接线的起始节点
- private Point endPoint; // 连接线的终点
+ private Point leftPoint; // 连接线的起始节点
+ private Point rightPoint; // 连接线的终点
private Brush brush; // 线条颜色
private double opacity; // 透明度
@@ -135,8 +148,8 @@ namespace Serein.Workbench.Avalonia.Custom.Views
private Vector startToEnd;
private int i = 0;
private void DrawBezierCurve(DrawingContext drawingContext,
- Point start,
- Point end)
+ Point left,
+ Point right)
{
// 控制点的计算逻辑
double power = 140; // 控制贝塞尔曲线的“拉伸”强度
@@ -144,7 +157,7 @@ namespace Serein.Workbench.Avalonia.Custom.Views
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
- startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
+ startToEnd = (right.ToVector() - left.ToVector()).NormalizeTo();
@@ -163,10 +176,10 @@ namespace Serein.Workbench.Avalonia.Custom.Views
pow = pow > 0 ? 0 : pow;
var k = 1 - pow;
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
- var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
+ var bias = left.X > right.X ? Math.Abs(left.X - right.X) * 0.25 : 0;
// 控制点的实际计算
- c0 = new Point(+(power + bias) * k + start.X, start.Y);
- c1 = new Point(-(power + bias) * k + end.X, end.Y);
+ c0 = new Point(+(power + bias) * k + left.X, left.Y);
+ c1 = new Point(-(power + bias) * k + right.X, right.Y);
// 准备StreamGeometry以用于绘制曲线
// why can't clearValue()?
@@ -204,8 +217,8 @@ namespace Serein.Workbench.Avalonia.Custom.Views
// streamGeometry.ClearValue("AvaloniaProperty");
using (var context = streamGeometry.Open())
{
- context.BeginFigure(start, true); // start point of the bezier-line
- context.CubicBezierTo(c0, c1, end, true); // drawing bezier-line
+ context.BeginFigure(left, true); // start point of the bezier-line
+ context.CubicBezierTo(c0, c1, right, true); // drawing bezier-line
}
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
diff --git a/Serein.Workbench.Avalonia/Custom/Views/NodeConnectionLineView.cs b/Serein.Workbench.Avalonia/Custom/Views/NodeConnectionLineView.cs
new file mode 100644
index 0000000..b491f2e
--- /dev/null
+++ b/Serein.Workbench.Avalonia/Custom/Views/NodeConnectionLineView.cs
@@ -0,0 +1,279 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.VisualTree;
+using Serein.Library;
+using Serein.Script.Node;
+using Serein.Workbench.Avalonia.Extension;
+using Serein.Workbench.Avalonia.Services;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using Color = Avalonia.Media.Color;
+using Point = Avalonia.Point;
+
+namespace Serein.Workbench.Avalonia.Custom.Views
+{
+
+
+
+ public class NodeConnectionLineView
+ {
+ ///
+ /// 线条类别(方法调用)
+ ///
+ public ConnectionInvokeType ConnectionInvokeType { get; set; } = ConnectionInvokeType.IsSucceed;
+ ///
+ /// 线条类别(参数传递)
+ ///
+ public ConnectionArgSourceType ConnectionArgSourceType { get; set; } = ConnectionArgSourceType.GetOtherNodeData;
+
+
+ ///
+ /// 画布
+ ///
+ private Canvas Canvas;
+ ///
+ /// 连接线的起点
+ ///
+ private NodeJunctionView? LeftNodeJunctionView;
+ ///
+ /// 连接线的终点
+ ///
+ private NodeJunctionView? RightNodeJunctionView;
+
+ ///
+ /// 连接时显示的线
+ ///
+ public ConnectionLineShape? ConnectionLineShape { get; private set; }
+
+ public NodeConnectionLineView(Canvas canvas,
+ NodeJunctionView? leftNodeJunctionView,
+ NodeJunctionView? rightNodeJunctionView)
+ {
+ this.Canvas = canvas;
+ this.LeftNodeJunctionView = leftNodeJunctionView;
+ this.RightNodeJunctionView = rightNodeJunctionView;
+ }
+
+ ///
+ /// 连接到终点
+ ///
+ ///
+ public void ToEnd(NodeJunctionView endNodeJunctionView)
+ {
+ if((endNodeJunctionView.JunctionType == JunctionType.NextStep
+ || endNodeJunctionView.JunctionType == JunctionType.ReturnData)
+ && RightNodeJunctionView is not null
+ /*&& LeftNodeJunctionView is null*/
+ /*&& !LeftNodeJunctionView.Equals(endNodeJunctionView)*/)
+ {
+ LeftNodeJunctionView = endNodeJunctionView;
+ RefreshLineDsiplay();
+ return;
+ }
+ else if ((endNodeJunctionView.JunctionType == JunctionType.Execute
+ || endNodeJunctionView.JunctionType == JunctionType.ArgData)
+ && LeftNodeJunctionView is not null
+ /*&& RightNodeJunctionView is null*/
+ /*&& !RightNodeJunctionView.Equals(endNodeJunctionView)*/)
+ {
+ RightNodeJunctionView = endNodeJunctionView;
+ RefreshLineDsiplay();
+ return;
+ }
+
+
+ //
+
+ //var leftPoint = GetPoint(LeftNodeJunctionView);
+ //var rightPoint = GetPoint(RightNodeJunctionView);
+ //var brush = GetBackgrounp();
+ //ConnectionLineShape.UpdatePoint(leftPoint, rightPoint);
+ //CreateLineShape(startPoint, endPoint, brush);
+ }
+
+ ///
+ /// 刷新线的显示
+ ///
+ public void RefreshLineDsiplay()
+ {
+ if(LeftNodeJunctionView is null || RightNodeJunctionView is null)
+ {
+ return;
+ }
+ var leftPoint = GetPoint(LeftNodeJunctionView);
+ var rightPoint = GetPoint(RightNodeJunctionView);
+ if (ConnectionLineShape is null)
+ {
+ Debug.WriteLine("创建");
+ CreateLineShape(leftPoint, rightPoint, GetBackgrounp());
+ }
+ else
+ {
+ Debug.WriteLine("刷新");
+ var brush = GetBackgrounp();
+ ConnectionLineShape.UpdatePoint( leftPoint, rightPoint, brush);
+ }
+ }
+
+
+ ///
+ /// 刷新临时线的显示
+ ///
+ public void RefreshRightPointOfTempLineDsiplay(Point rightPoint)
+ {
+ if(ConnectionLineShape is not null)
+ {
+ var brush = GetBackgrounp();
+ ConnectionLineShape.UpdateRightPoint(rightPoint, brush);
+ return;
+ }
+
+ if (LeftNodeJunctionView is not null)
+ {
+ var leftPoint = GetPoint(LeftNodeJunctionView);
+ var brush = GetBackgrounp();
+ CreateLineShape(leftPoint, rightPoint, brush);
+ }
+ }
+ ///
+ /// 刷新临时线的显示
+ ///
+ public void RefreshLeftPointOfTempLineDsiplay(Point leftPoint)
+ {
+ if(ConnectionLineShape is not null)
+ {
+ var brush = GetBackgrounp();
+ ConnectionLineShape.UpdateLeftPoints(leftPoint, brush);
+ return;
+ }
+
+ if (RightNodeJunctionView is not null)
+ {
+ var rightPoint = GetPoint(RightNodeJunctionView);
+ var brush = GetBackgrounp();
+ CreateLineShape(leftPoint, rightPoint, brush);
+ }
+ }
+
+
+
+ private static Point defaultPoint = new Point(0, 0);
+ int count;
+ private Point GetPoint(NodeJunctionView nodeJunctionView)
+ {
+
+ var junctionSize = nodeJunctionView.GetTransformedBounds()!.Value.Bounds.Size;
+ Point junctionPoint;
+ if (nodeJunctionView.JunctionType == JunctionType.ArgData || nodeJunctionView.JunctionType == JunctionType.Execute)
+ {
+ junctionPoint = new Point(junctionSize.Width / 2 - 11, junctionSize.Height / 2); // 选择左侧
+ }
+ else
+ {
+ junctionPoint = new Point(junctionSize.Width / 2 + 11, junctionSize.Height / 2); // 选择右侧
+ }
+ if (nodeJunctionView.TranslatePoint(junctionPoint, Canvas) is Point point)
+ {
+ //myData.StartPoint = point;
+ return point;
+ }
+ else
+ {
+ return defaultPoint;
+ }
+
+ //var point = nodeJunctionView.TranslatePoint(defaultPoint , Canvas);
+ //if(point is null)
+ //{
+ // return defaultPoint;
+ //}
+ //else
+ //{
+ // return point.Value;
+ // }
+ }
+
+ private void CreateLineShape(Point leftPoint, Point rightPoint, Brush brush)
+ {
+ ConnectionLineShape = new ConnectionLineShape(leftPoint, rightPoint, brush);
+ Canvas.Children.Add(ConnectionLineShape);
+ }
+
+ private JunctionOfConnectionType GetConnectionType()
+ {
+ return LeftNodeJunctionView.JunctionType.ToConnectyionType();
+ }
+
+
+ ///
+ /// 获取背景颜色
+ ///
+ ///
+ public Brush GetBackgrounp()
+ {
+
+ if(LeftNodeJunctionView is null || RightNodeJunctionView is null)
+ {
+ return new SolidColorBrush(Color.Parse("#FF0000")); // 没有终点
+ }
+
+ // 判断连接控制点是否匹配
+ if (!IsCanConnected())
+ {
+ return new SolidColorBrush(Color.Parse("#FF0000"));
+ }
+
+
+ if (GetConnectionType() == JunctionOfConnectionType.Invoke)
+ {
+ return ConnectionInvokeType.ToLineColor(); // 调用
+ }
+ else
+ {
+ return ConnectionArgSourceType.ToLineColor(); // 参数
+ }
+
+ }
+
+ public bool IsCanConnected()
+ {
+ if (LeftNodeJunctionView is null
+ || RightNodeJunctionView is null)
+ {
+ return false;
+ }
+ if (LeftNodeJunctionView?.MyNode is null
+ || LeftNodeJunctionView.MyNode.Equals(RightNodeJunctionView.MyNode))
+ return false;
+
+ if (LeftNodeJunctionView.JunctionType.IsCanConnection(RightNodeJunctionView.JunctionType))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 移除线
+ ///
+ public void Remove()
+ {
+ if(ConnectionLineShape is null)
+ {
+ return;
+ }
+ Canvas.Children.Remove(ConnectionLineShape);
+ }
+ }
+}
diff --git a/Serein.Workbench.Avalonia/Custom/Views/NodeContainerView.axaml.cs b/Serein.Workbench.Avalonia/Custom/Views/NodeContainerView.axaml.cs
index 198a3f6..1c8e6d5 100644
--- a/Serein.Workbench.Avalonia/Custom/Views/NodeContainerView.axaml.cs
+++ b/Serein.Workbench.Avalonia/Custom/Views/NodeContainerView.axaml.cs
@@ -98,6 +98,7 @@ public partial class NodeContainerView : UserControl
{
IsCanvasDragging = false;
IsControlDragging = false;
+ nodeOperationService.ConnectingData.Reset();
}
};
#endregion
@@ -190,12 +191,11 @@ public partial class NodeContainerView : UserControl
private void NodeContainerView_PointerMoved(object? sender, PointerEventArgs e)
{
-
+ // Ƿ
var myData = nodeOperationService.ConnectingData;
if (myData.IsCreateing)
{
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsLeftButtonPressed);
- //Debug.WriteLine("canvas ispass = " + isPass);
if (isPass)
{
if (myData.Type == JunctionOfConnectionType.Invoke)
@@ -208,7 +208,9 @@ public partial class NodeContainerView : UserControl
_vm.IsConnectionArgSourceNode = true; // ӽڵĵùϵ
}
var currentPoint = e.GetPosition(PART_NodeContainer);
+ //myData.CurrentJunction?.InvalidateVisual();
myData.UpdatePoint(new Point(currentPoint.X - 5, currentPoint.Y - 5));
+ e.Handled = true;
return;
}
diff --git a/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs b/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs
index dbf6ee2..18fe1ff 100644
--- a/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs
+++ b/Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs
@@ -1,18 +1,14 @@
using Avalonia;
-using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using Serein.Library;
-using Serein.Workbench.Avalonia.Views;
-using System.Drawing;
+using Serein.Library.Api;
+using Serein.Workbench.Avalonia.Api;
+using Serein.Workbench.Avalonia.Extension;
using System;
using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
-using System.Diagnostics;
-using Avalonia.Threading;
-using Serein.Workbench.Avalonia.Api;
-using Serein.Workbench.Avalonia.Extension;
namespace Serein.Workbench.Avalonia.Custom.Views;
@@ -21,55 +17,6 @@ namespace Serein.Workbench.Avalonia.Custom.Views;
///
public class NodeJunctionView : TemplatedControl
{
- private readonly INodeOperationService nodeOperationService;
-
- ///
- /// RenderпԻ
- ///
- protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
-
- ///
- /// ڲ鿴
- ///
- private bool IsPreviewing;
-
- public NodeJunctionView()
- {
- nodeOperationService = App.GetService();
- this.PointerMoved += NodeJunctionView_PointerMoved;
- this.PointerExited += NodeJunctionView_PointerExited;
-
- this.PointerPressed += NodeJunctionView_PointerPressed;
- this.PointerReleased += NodeJunctionView_PointerReleased;
- }
-
- private void NodeJunctionView_PointerReleased(object? sender, PointerReleasedEventArgs e)
- {
- nodeOperationService.ConnectingData.IsCreateing = false;
- }
-
- private void NodeJunctionView_PointerPressed(object? sender, PointerPressedEventArgs e)
- {
- nodeOperationService.TryCreateConnectionOnJunction(this); // Կʼ
- Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
- }
-
-
-
- ///
- /// ȡؼϢ
- ///
- ///
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- base.OnApplyTemplate(e);
- //if (e.NameScope.Find("PART_FlipflopMethodInfos") is ListBox p_fm)
- //{
- // //p_fm.SelectionChanged += ListBox_SelectionChanged;
- // //p_fm.PointerExited += ListBox_PointerExited;
- //}
- }
-
public static readonly DirectProperty JunctionTypeProperty =
AvaloniaProperty.RegisterDirect(nameof(JunctionType), o => o.JunctionType, (o, v) => o.JunctionType = v);
@@ -88,6 +35,160 @@ public class NodeJunctionView : TemplatedControl
get { return myNode; }
set { SetAndRaise(MyNodeProperty, ref myNode, value); }
}
+
+ public static readonly DirectProperty ArgIndexProperty =
+ AvaloniaProperty.RegisterDirect(nameof(ArgIndex), o => o.ArgIndex, (o, v) => o.ArgIndex = v);
+ private int argIndex;
+ public int ArgIndex
+ {
+ get { return argIndex; }
+ set { SetAndRaise(ArgIndexProperty, ref argIndex, value); }
+ }
+
+
+
+ private readonly INodeOperationService nodeOperationService;
+ private readonly IFlowEnvironment flowEnvironment;
+
+ ///
+ /// RenderпԻ
+ ///
+ protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
+
+
+
+ #region ¼
+
+
+ public NodeJunctionView()
+ {
+ nodeOperationService = App.GetService();
+ flowEnvironment = App.GetService();
+ //this.PointerExited += NodeJunctionView_PointerExited;
+ this.PointerMoved += NodeJunctionView_PointerMoved;
+ this.PointerPressed += NodeJunctionView_PointerPressed;
+ this.PointerReleased += NodeJunctionView_PointerReleased;
+ }
+
+
+ public bool IsPreviewing { get; set; }
+ private Guid Guid = Guid.NewGuid();
+
+ private void NodeJunctionView_PointerMoved(object? sender, PointerEventArgs e)
+ {
+ if (!nodeOperationService.ConnectingData.IsCreateing)
+ return;
+ if (nodeOperationService.MainCanvas is not InputElement inputElement)
+ return;
+ var currentPoint = e.GetPosition(nodeOperationService.MainCanvas);
+ if (inputElement.InputHitTest(currentPoint) is NodeJunctionView junctionView)
+ {
+ RefreshDisplay(junctionView);
+ }
+ else
+ {
+ var oldNj = nodeOperationService.ConnectingData.CurrentJunction;
+ if (oldNj is not null)
+ {
+ oldNj.IsPreviewing = false;
+ oldNj.InvalidateVisual();
+ }
+ }
+ }
+
+ private void RefreshDisplay(NodeJunctionView junctionView)
+ {
+ var oldNj = nodeOperationService.ConnectingData.CurrentJunction;
+ if (oldNj is not null )
+ {
+ if (junctionView.Equals(oldNj))
+ {
+ return;
+ }
+ oldNj.IsPreviewing = false;
+ oldNj.InvalidateVisual();
+ }
+ nodeOperationService.ConnectingData.CurrentJunction = junctionView;
+ if (!this.Equals(junctionView))
+ {
+
+ nodeOperationService.ConnectingData.TempLine?.ToEnd(junctionView);
+ }
+ junctionView.IsPreviewing = true;
+ junctionView.InvalidateVisual();
+ }
+
+
+
+ ///
+ /// Կʼ
+ ///
+ ///
+ ///
+ private void NodeJunctionView_PointerPressed(object? sender, PointerPressedEventArgs e)
+ {
+ nodeOperationService.TryCreateConnectionOnJunction(this); // Կʼ
+ }
+ private void NodeJunctionView_PointerReleased(object? sender, PointerReleasedEventArgs e)
+ {
+ CheckJunvtion();
+ nodeOperationService.ConnectingData.Reset();
+ }
+
+ private void CheckJunvtion()
+ {
+ var myData = nodeOperationService.ConnectingData;
+ if(myData.StartJunction is null || myData.CurrentJunction is null)
+ {
+ return;
+ }
+ if(myData.StartJunction.MyNode is null || myData.CurrentJunction.MyNode is null)
+ {
+ return;
+ }
+ if (!myData.IsCanConnected())
+ {
+ return;
+ }
+
+ var canvas = nodeOperationService.MainCanvas;
+
+ #region ùϵ
+ if (myData.Type == JunctionOfConnectionType.Invoke)
+ {
+ flowEnvironment.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
+ myData.StartJunction.JunctionType,
+ myData.CurrentJunction.JunctionType,
+ myData.ConnectionInvokeType);
+ }
+ #endregion
+
+ #region Դϵ
+ else if (myData.Type == JunctionOfConnectionType.Arg)
+ {
+ var argIndex = 0;
+ if (myData.StartJunction.JunctionType == JunctionType.ArgData)
+ {
+ argIndex = myData.StartJunction.ArgIndex;
+ }
+ else if (myData.CurrentJunction.JunctionType == JunctionType.ArgData)
+ {
+ argIndex = myData.CurrentJunction.ArgIndex;
+ }
+
+ flowEnvironment.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
+ myData.StartJunction.JunctionType,
+ myData.CurrentJunction.JunctionType,
+ myData.ConnectionArgSourceType,
+ argIndex);
+ }
+ #endregion
+
+
+ }
+
+ #endregion
+
#region ػUIӾ
@@ -101,7 +202,8 @@ public class NodeJunctionView : TemplatedControl
double width = 44;
double height = 26;
var background = GetBackgrounp();
- var pen = new Pen(Brushes.Black, 1);
+ var pen = new Pen(Brushes.Transparent, 1);
+ //var pen = nodeOperationService.ConnectingData.IsCreateing ? new Pen(background, 1) : new Pen(Brushes.Black, 1);
// ı
var connectorRect = new Rect(0, 0, width, height);
@@ -132,32 +234,10 @@ public class NodeJunctionView : TemplatedControl
context.LineTo(new Point(triangleCenterX, triangleCenterY + t), true);
context.LineTo(new Point(triangleCenterX, triangleCenterY - t), true);
}
- drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
+ drawingContext.DrawGeometry(background, pen, pathGeometry);
}
- #region ¼
-
- private void NodeJunctionView_PointerExited(object? sender, PointerEventArgs e)
- {
- if (IsPreviewing)
- {
- IsPreviewing = false;
- Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
- }
- }
-
- private void NodeJunctionView_PointerMoved(object? sender, PointerEventArgs e)
- {
- if (!IsPreviewing)
- {
- IsPreviewing = true;
- Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
- }
-
- }
-
- #endregion
///
@@ -167,38 +247,25 @@ public class NodeJunctionView : TemplatedControl
protected IBrush GetBackgrounp()
{
var myData = nodeOperationService.ConnectingData;
- if (!myData.IsCreateing)
+ if (IsPreviewing == false || !myData.IsCreateing )
{
- //Debug.WriteLine($"return color is {Brushes.BurlyWood}");
return new SolidColorBrush(Color.Parse("#76ABEE"));
}
- if (myData.IsCanConnected)
+ if (!myData.IsCanConnected())
{
- if (myData.Type == JunctionOfConnectionType.Invoke)
- {
- return myData.ConnectionInvokeType.ToLineColor();
- }
- else
- {
- return myData.ConnectionArgSourceType.ToLineColor();
- }
- }
- else
- {
- return Brushes.Red;
+ return new SolidColorBrush(Color.Parse("#FF0000"));
}
- if (IsPreviewing)
+ if (myData.Type == JunctionOfConnectionType.Invoke)
{
- //return new SolidColorBrush(Color.Parse("#04FC10"));
-
+ return myData.ConnectionInvokeType.ToLineColor(); //
}
else
{
- //Debug.WriteLine($"return color is {Brushes.BurlyWood}");
- return new SolidColorBrush(Color.Parse("#76ABEE"));
+ return myData.ConnectionArgSourceType.ToLineColor(); //
}
+
}
#endregion
diff --git a/Serein.Workbench.Avalonia/Custom/Views/ParameterDetailsInfoView.axaml b/Serein.Workbench.Avalonia/Custom/Views/ParameterDetailsInfoView.axaml
index 785860c..f9b9064 100644
--- a/Serein.Workbench.Avalonia/Custom/Views/ParameterDetailsInfoView.axaml
+++ b/Serein.Workbench.Avalonia/Custom/Views/ParameterDetailsInfoView.axaml
@@ -22,7 +22,7 @@
-
+
-
+
+
+
-
+
@@ -45,12 +47,16 @@
-
-
+
+
+
+ MinWidth="120" MaxWidth="300"
+ HorizontalAlignment="Left" VerticalAlignment="Center">
diff --git a/Serein.Workbench.Avalonia/DataTemplates/LibraryMethodInfoDataTemplate.cs b/Serein.Workbench.Avalonia/DataTemplates/LibraryMethodInfoDataTemplate.cs
index 0e0b883..b513421 100644
--- a/Serein.Workbench.Avalonia/DataTemplates/LibraryMethodInfoDataTemplate.cs
+++ b/Serein.Workbench.Avalonia/DataTemplates/LibraryMethodInfoDataTemplate.cs
@@ -30,12 +30,7 @@ namespace Serein.Workbench.Avalonia.DataTemplates
textBlock.Margin = new Thickness(2d, -6d, 2d, -6d);
textBlock.FontSize = 12;
textBlock.PointerPressed += TextBlock_PointerPressed;
- //var stackPanel = new StackPanel();
- //stackPanel.Children.Add(textBlock);
- //ToolTip toolTip = new ToolTip();
- //toolTip.FontSize = 12;
- //toolTip.Content = mdInfo.MethodAnotherName;
- //textBlock.Tag = mdInfo;
+ textBlock.Tag = mdInfo;
return textBlock;
}
else
@@ -43,7 +38,6 @@ namespace Serein.Workbench.Avalonia.DataTemplates
var textBlock = new TextBlock() { Text = $"Binding 类型不为预期的[MethodDetailsInfo],而是[{param?.GetType()}]" };
textBlock.Margin = new Thickness(2d, -6d, 2d, -6d);
textBlock.FontSize = 12;
- textBlock.PointerPressed += TextBlock_PointerPressed;
return textBlock;
}
diff --git a/Serein.Workbench.Avalonia/Model/ConnectingData.cs b/Serein.Workbench.Avalonia/Model/ConnectingData.cs
index 26e945f..24e63ea 100644
--- a/Serein.Workbench.Avalonia/Model/ConnectingData.cs
+++ b/Serein.Workbench.Avalonia/Model/ConnectingData.cs
@@ -1,8 +1,10 @@
using Avalonia;
+using Avalonia.Threading;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.Views;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -34,7 +36,7 @@ namespace Serein.Workbench.Avalonia.Model
///
/// 线条样式
///
- public MyLine? TempLine { get; set; }
+ public NodeConnectionLineView? TempLine { get; set; }
///
/// 线条类别(方法调用)
@@ -49,59 +51,56 @@ namespace Serein.Workbench.Avalonia.Model
/// 判断当前连接类型
///
public JunctionOfConnectionType? Type => StartJunction?.JunctionType.ToConnectyionType();
-
+
///
/// 是否允许连接
///
-
- public bool IsCanConnected
+ public bool IsCanConnected()
{
- get
+ if (StartJunction is null
+ || CurrentJunction is null )
{
+ return false;
+ }
+ if (StartJunction?.MyNode is null
+ || StartJunction.MyNode.Equals(CurrentJunction.MyNode))
+ return false;
- if (StartJunction is null
- || CurrentJunction is null
- )
- {
- return false;
- }
- if(StartJunction?.MyNode is null)
- {
- return false;
- }
- if (!StartJunction.MyNode.Equals(CurrentJunction.MyNode)
- && StartJunction.JunctionType.IsCanConnection(CurrentJunction.JunctionType))
- {
- return true;
- }
- else
- {
- return false;
- }
+ if (StartJunction.JunctionType.IsCanConnection(CurrentJunction.JunctionType))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
}
}
+
+
///
/// 更新临时的连接线
///
///
public void UpdatePoint(Point point)
{
- if (StartJunction is null
- || CurrentJunction is null
- )
+ if (StartJunction is null || CurrentJunction is null )
+ {
+ return;
+ }
+ if (IsCanConnected())
{
return;
}
if (StartJunction.JunctionType == Library.JunctionType.Execute
|| StartJunction.JunctionType == Library.JunctionType.ArgData)
{
- TempLine?.Line.UpdateStartPoints(point);
+ TempLine?.RefreshLeftPointOfTempLineDsiplay(point);
}
else
{
- TempLine?.Line.UpdateEndPoints(point);
+ TempLine?.RefreshRightPointOfTempLineDsiplay(point);
}
}
@@ -111,9 +110,17 @@ namespace Serein.Workbench.Avalonia.Model
///
public void Reset()
{
+ if(CurrentJunction is not null)
+ {
+ CurrentJunction.IsPreviewing = false;
+ Dispatcher.UIThread.InvokeAsync(CurrentJunction.InvalidateVisual, DispatcherPriority.Background);
+ }
+ if(StartJunction is not null)
+ {
+ StartJunction.IsPreviewing = false;
+ Dispatcher.UIThread.InvokeAsync(StartJunction.InvalidateVisual, DispatcherPriority.Background);
+ }
IsCreateing = false;
- StartJunction = null;
- CurrentJunction = null;
TempLine?.Remove();
ConnectionInvokeType = ConnectionInvokeType.IsSucceed;
ConnectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData;
diff --git a/Serein.Workbench.Avalonia/Model/NodeConnectionLine.cs b/Serein.Workbench.Avalonia/Model/NodeConnectionLine.cs
new file mode 100644
index 0000000..7a0c85a
--- /dev/null
+++ b/Serein.Workbench.Avalonia/Model/NodeConnectionLine.cs
@@ -0,0 +1,41 @@
+using Avalonia.Controls;
+using Serein.Workbench.Avalonia.Custom.Views;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Avalonia.Model
+{
+
+ ///
+ /// 绘制的线
+ ///
+ public class NodeConnectionLine
+ {
+ ///
+ /// 将线条绘制出来(临时线)
+ ///
+ /// 放置画布
+ /// 线的实体
+ public NodeConnectionLine(Canvas canvas, ConnectionLineShape line)
+ {
+ Canvas = canvas;
+ Line = line;
+ canvas?.Children.Add(line);
+ }
+
+
+ public Canvas Canvas { get; }
+ public ConnectionLineShape Line { get; }
+
+ ///
+ /// 移除线
+ ///
+ public void Remove()
+ {
+ Canvas?.Children.Remove(Line);
+ }
+ }
+}
diff --git a/Serein.Workbench.Avalonia/Serein.Workbench.Avalonia.csproj b/Serein.Workbench.Avalonia/Serein.Workbench.Avalonia.csproj
index ef5b5df..23d6ab0 100644
--- a/Serein.Workbench.Avalonia/Serein.Workbench.Avalonia.csproj
+++ b/Serein.Workbench.Avalonia/Serein.Workbench.Avalonia.csproj
@@ -19,6 +19,7 @@
+
diff --git a/Serein.Workbench.Avalonia/Services/NodeOperationService.cs b/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
index 7608f82..e6079af 100644
--- a/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
+++ b/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
@@ -15,6 +15,7 @@ using Serein.Workbench.Avalonia.Extension;
using Serein.Workbench.Avalonia.Model;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
@@ -81,12 +82,12 @@ namespace Serein.Workbench.Avalonia.Api
internal class NodeViewCreateEventArgs : EventArgs
{
- internal NodeViewCreateEventArgs(INodeControl nodeControl, PositionOfUI position)
+ internal NodeViewCreateEventArgs(NodeControlBase nodeControl, PositionOfUI position)
{
this.NodeControl = nodeControl;
this.Position = position;
}
- public INodeControl NodeControl { get; private set; }
+ public NodeControlBase NodeControl { get; private set; }
public PositionOfUI Position { get; private set; }
}
@@ -112,11 +113,9 @@ namespace Serein.Workbench.Avalonia.Services
{
this.flowEnvironment = flowEnvironment;
this.feefService = feefService;
-
- NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeView), typeof(ActionNodeViewModel)); // 注册动作节点
- ConnectingData = new ConnectingData();
feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件
-
+ feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件
+ NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeView), typeof(ActionNodeViewModel)); // 注册动作节点
// 手动加载项目
_ = Task.Run(async delegate
@@ -133,17 +132,24 @@ namespace Serein.Workbench.Avalonia.Services
}
- public ConnectingData ConnectingData { get; private set; }
+
+ #region 接口属性
+ public ConnectingData ConnectingData { get; private set; } = new ConnectingData();
public Canvas MainCanvas { get; set; }
-
+ #endregion
#region 私有变量
///
/// 存储所有与节点有关的控件
///
- private Dictionary NodeControls { get; } = [];
+ private Dictionary NodeControls { get; } = [];
+
+ ///
+ /// 存储所有连接
+ ///
+ private List Connections { get; } = [];
@@ -158,28 +164,16 @@ namespace Serein.Workbench.Avalonia.Services
private readonly IFlowEEForwardingService feefService;
#endregion
+ #region 节点操作事件
+
///
/// 创建了节点控件
///
public event NodeViewCreateHandle OnNodeViewCreate;
- ///
- /// 创建节点控件
- ///
- /// 控件类型
- /// 创建坐标
- /// 节点方法信息(基础节点传null)
- public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position)
- {
- Task.Run(async () =>
- {
- if (EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType))
- {
- await flowEnvironment.CreateNodeAsync(nodeType, position, methodDetailsInfo);
- }
- });
- }
+ #endregion
+ #region 转发事件的处理
///
/// 从工作台事件转发器监听节点创建事件
@@ -225,6 +219,178 @@ namespace Serein.Workbench.Avalonia.Services
}
+
+ ///
+ /// 运行环境连接了节点事件
+ ///
+ ///
+ ///
+ private void FeefService_OnNodeConnectChange(NodeConnectChangeEventArgs eventArgs)
+ {
+#if false
+ string fromNodeGuid = eventArgs.FromNodeGuid;
+ string toNodeGuid = eventArgs.ToNodeGuid;
+ if (!TryGetControl(fromNodeGuid, out var fromNodeControl)
+ || !TryGetControl(toNodeGuid, out var toNodeControl))
+ {
+ return;
+ }
+
+ if (eventArgs.JunctionOfConnectionType == JunctionOfConnectionType.Invoke)
+ {
+ ConnectionInvokeType connectionType = eventArgs.ConnectionInvokeType;
+ #region 创建/删除节点之间的调用关系
+ #region 创建连接
+ if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
+ return;
+ }
+ var startJunction = IFormJunction.NextStepJunction;
+ var endJunction = IToJunction.ExecuteJunction;
+
+ startJunction.TransformToVisual(MainCanvas);
+
+ // 添加连接
+ var shape = new ConnectionLineShape(
+ FlowChartCanvas,
+ connectionType,
+ startJunction,
+ endJunction
+ );
+ NodeConnectionLine nodeConnectionLine = new NodeConnectionLine(MainCanvas, shape);
+
+ //if (toNodeControl is FlipflopNodeControl flipflopControl
+ // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
+ //{
+ // NodeTreeViewer.RemoveGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
+ //}
+
+ Connections.Add(nodeConnectionLine);
+ fromNodeControl.AddCnnection(shape);
+ toNodeControl.AddCnnection(shape);
+ }
+ #endregion
+#if false
+
+ #region 移除连接
+ else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) // 移除连接
+ {
+ // 需要移除连接
+ var removeConnections = Connections.Where(c =>
+ c.Start.MyNode.Guid.Equals(fromNodeGuid)
+ && c.End.MyNode.Guid.Equals(toNodeGuid)
+ && (c.Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke
+ || c.End.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke))
+ .ToList();
+
+
+ foreach (var connection in removeConnections)
+ {
+ Connections.Remove(connection);
+ fromNodeControl.RemoveConnection(connection); // 移除连接
+ toNodeControl.RemoveConnection(connection); // 移除连接
+ if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
+ {
+ JudgmentFlipFlopNode(control); // 连接关系变更时判断
+ }
+ }
+ }
+ #endregion
+
+#endif
+ #endregion
+ }
+ else
+ {
+ #if false
+ ConnectionArgSourceType connectionArgSourceType = eventArgs.ConnectionArgSourceType;
+ #region 创建/删除节点之间的参数传递关系
+ #region 创建连接
+ if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的情况");
+ return;
+ }
+
+ JunctionControlBase startJunction = eventArgs.ConnectionArgSourceType switch
+ {
+ ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点
+ ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
+ ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
+ _ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
+ };
+
+ if (IToJunction.ArgDataJunction.Length <= eventArgs.ArgIndex)
+ {
+ _ = Task.Run(async () =>
+ {
+ await Task.Delay(500);
+ FlowEnvironment_NodeConnectChangeEvemt(eventArgs);
+ });
+ return;
+ }
+ JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
+ LineType lineType = LineType.Bezier;
+ // 添加连接
+ var connection = new ConnectionControl(
+ lineType,
+ FlowChartCanvas,
+ eventArgs.ArgIndex,
+ eventArgs.ConnectionArgSourceType,
+ startJunction,
+ endJunction,
+ IToJunction
+ );
+ Connections.Add(connection);
+ fromNodeControl.AddCnnection(connection);
+ toNodeControl.AddCnnection(connection);
+ EndConnection(); // 环境触发了创建节点连接事件
+
+
+ }
+ #endregion
+ #region 移除连接
+ else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) // 移除连接
+ {
+ // 需要移除连接
+ var removeConnections = Connections.Where(c => c.Start.MyNode.Guid.Equals(fromNodeGuid)
+ && c.End.MyNode.Guid.Equals(toNodeGuid))
+ .ToList(); // 获取这两个节点之间的所有连接关系
+
+
+
+ foreach (var connection in removeConnections)
+ {
+ if (connection.End is ArgJunctionControl junctionControl && junctionControl.ArgIndex == eventArgs.ArgIndex)
+ {
+ // 找到符合删除条件的连接线
+ Connections.Remove(connection); // 从本地记录中移除
+ fromNodeControl.RemoveConnection(connection); // 从节点持有的记录移除
+ toNodeControl.RemoveConnection(connection); // 从节点持有的记录移除
+ }
+
+
+ //if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
+ //{
+ // JudgmentFlipFlopNode(control); // 连接关系变更时判断
+ //}
+ }
+ }
+ #endregion
+ #endregion
+#endif
+ }
+#endif
+ }
+ #endregion
+
+ #region 私有方法
+
///
/// 创建节点控件
///
@@ -234,7 +400,7 @@ namespace Serein.Workbench.Avalonia.Services
/// 返回的节点对象
/// 是否创建成功
/// 无法创建节点控件
- private bool TryCreateNodeView(Type viewType, Type viewModelType, NodeModelBase nodeModel, out INodeControl? nodeView)
+ private bool TryCreateNodeView(Type viewType, Type viewModelType, NodeModelBase nodeModel, out NodeControlBase? nodeView)
{
if (string.IsNullOrEmpty(nodeModel.Guid))
{
@@ -248,16 +414,16 @@ namespace Serein.Workbench.Avalonia.Services
}
viewModelBase.NodeModelBase = nodeModel; // 设置节点对象
var controlObj = Activator.CreateInstance(viewType);
- if (controlObj is not INodeControl nodeControl)
+ if (controlObj is NodeControlBase nodeControl)
{
- nodeView = null;
- return false;
+ nodeControl.DataContext = viewModelBase;
+ nodeView = nodeControl;
+ return true;
}
else
{
- nodeControl.SetNodeModel(nodeModel);
- nodeView = nodeControl;
- return true;
+ nodeView = null;
+ return false;
}
// 在其它地方验证过了,所以注释
@@ -276,6 +442,47 @@ namespace Serein.Workbench.Avalonia.Services
//}
}
+ private bool TryGetControl(string nodeGuid, out NodeControlBase nodeControl)
+ {
+ if (string.IsNullOrEmpty(nodeGuid))
+ {
+ nodeControl = null;
+ return false;
+ }
+ if (!NodeControls.TryGetValue(nodeGuid, out nodeControl))
+ {
+ nodeControl = null;
+ return false;
+ }
+ if (nodeControl is null)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ #endregion
+
+ #region 操作接口对外暴露的接口
+
+ ///
+ /// 创建节点控件
+ ///
+ /// 控件类型
+ /// 创建坐标
+ /// 节点方法信息(基础节点传null)
+ public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position)
+ {
+ Task.Run(async () =>
+ {
+ if (EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType))
+ {
+ await flowEnvironment.CreateNodeAsync(nodeType, position, methodDetailsInfo);
+ }
+ });
+ }
+
+
///
/// 尝试在连接控制点之间创建连接线
///
@@ -283,25 +490,23 @@ namespace Serein.Workbench.Avalonia.Services
{
if (MainCanvas is not null)
{
- var myData = ConnectingData;
- var junctionSize = startJunction.GetTransformedBounds()!.Value.Bounds.Size;
- var junctionPoint = new Point(junctionSize.Width / 2, junctionSize.Height / 2);
- if (startJunction.TranslatePoint(junctionPoint, MainCanvas) is Point point)
+ ConnectingData.Reset();
+ ConnectingData.IsCreateing = true; // 表示开始连接
+ ConnectingData.StartJunction = startJunction;
+ ConnectingData.CurrentJunction = startJunction;
+ if(startJunction.JunctionType == JunctionType.NextStep || startJunction.JunctionType == JunctionType.ReturnData)
{
- myData.StartPoint = point;
+
+ ConnectingData.TempLine = new NodeConnectionLineView(MainCanvas, startJunction, null);
}
else
{
- return;
+ ConnectingData.TempLine = new NodeConnectionLineView(MainCanvas,null ,startJunction);
}
- myData.Reset();
- myData.IsCreateing = true; // 表示开始连接
- myData.StartJunction = startJunction;
- myData.CurrentJunction = startJunction;
- var junctionOfConnectionType = startJunction.JunctionType.ToConnectyionType();
- ConnectionLineShape bezierLine; // 类别
+ /*var junctionOfConnectionType = startJunction.JunctionType.ToConnectyionType();
+ ConnectionLineShape bezierLine;
Brush brushColor; // 临时线的颜色
if (junctionOfConnectionType == JunctionOfConnectionType.Invoke)
{
@@ -319,11 +524,13 @@ namespace Serein.Workbench.Avalonia.Services
myData.StartPoint,
brushColor,
isTop: true); // 绘制临时的线
-
+ */
//Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线
- myData.TempLine = new MyLine(MainCanvas, bezierLine);
+
}
- }
+ }
+
+ #endregion
}
}
diff --git a/Serein.Workbench.Avalonia/Views/MainView.axaml b/Serein.Workbench.Avalonia/Views/MainView.axaml
index a26f1c7..fee1b0a 100644
--- a/Serein.Workbench.Avalonia/Views/MainView.axaml
+++ b/Serein.Workbench.Avalonia/Views/MainView.axaml
@@ -18,7 +18,7 @@
-
+