using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.VisualTree;
using Newtonsoft.Json.Linq;
using Serein.Library;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.Views;
using Serein.Workbench.Avalonia.Custom.ViewModels;
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 Point = Avalonia.Point;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class NodeContainerView : UserControl
{
private readonly NodeContainerViewModel _vm;
private readonly INodeOperationService nodeOperationService;
private readonly IKeyEventService keyEventService;
#region 与画布相关的字段
///
/// 是否正在预览节点控件
///
private bool IsPreviewNodeControl;
///
/// 标记是否正在尝试选取控件
///
private bool IsSelectControl;
///
/// 标记是否正在拖动控件
///
private bool IsControlDragging;
///
/// 标记是否正在拖动画布
///
private bool IsCanvasDragging;
///
/// 标记是否正在选取节点
///
private bool IsSelectDragging;
///
/// 当前选取的控件
///
private readonly List selectNodeControls = [];
///
/// 记录开始拖动节点控件时的鼠标位置
///
private Point startControlDragPoint;
///
/// 记录移动画布开始时的鼠标位置
///
private Point startCanvasDragPoint;
///
/// 记录开始选取节点控件时的鼠标位置
///
private Point startSelectControolPoint;
///
/// 组合变换容器
///
private readonly TransformGroup canvasTransformGroup;
///
/// 缩放画布
///
private readonly ScaleTransform scaleTransform;
///
/// 平移画布
///
private readonly TranslateTransform translateTransform;
#endregion
public NodeContainerView()
{
InitializeComponent();
_vm= App.GetService();
DataContext = _vm;
#region 获取UI相关的服务
keyEventService = App.GetService();
nodeOperationService = App.GetService();
nodeOperationService.MainCanvas = PART_NodeContainer;
nodeOperationService.OnNodeViewCreate += NodeOperationService_OnNodeViewCreate; // 处理事件
keyEventService.KeyUp += (k) =>
{
if (k == Key.Escape)
{
IsCanvasDragging = false;
IsControlDragging = false;
nodeOperationService.ConnectingManage.Reset();
}
};
#endregion
#region 设置UI事件
AddHandler(DragDrop.DropEvent, Drop); // 创建节点相关
PointerPressed += NodeContainerView_PointerPressed;
PointerReleased += NodeContainerView_PointerReleased;
PointerMoved += NodeContainerView_PointerMoved;
PointerWheelChanged += NodeContainerView_PointerWheelChanged;
#endregion
#region 初始化画布动画容器
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
PART_NodeContainer.RenderTransform = canvasTransformGroup;
#endregion
}
#region 工具方法
public Point GetPositionOfCanvas(PointerEventArgs e)
{
return e.GetPosition(PART_NodeContainer);
}
public Point GetPositionOfCanvas(DragEventArgs e)
{
return e.GetPosition(PART_NodeContainer);
}
#endregion
#region 画布的移动、缩放、框选,以及拖拽事件
#region 响应拖拽事件
private void Drop(object? sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
{
var json = e.Data.GetText();
if (string.IsNullOrEmpty(json))
{
return;
}
var mdInfo = json.ToJsonObject();
if (mdInfo is not null)
{
var canvasDropPosition = GetPositionOfCanvas(e); // 更新画布落点
PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
nodeOperationService.CreateNodeView(mdInfo, position); // 提交创建节点的请求
}
}
else // if (e.Data.Contains(DataFormats.FileNames))
{
var files = e.Data.GetFiles();
var str = files?.Select(f => f.Path);
if (str is not null)
{
}
}
}
#endregion
#region 拖动画布
private void NodeContainerView_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (IsPreviewNodeControl)
{
IsCanvasDragging = false;
e.Handled = true;
return;
}
if (!IsCanvasDragging)
{
IsCanvasDragging = true;
startCanvasDragPoint = e.GetPosition(this);
e.Handled = true;
}
}
private void NodeContainerView_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
IsCanvasDragging = false; // 不再拖动
}
private void NodeContainerView_PointerMoved(object? sender, PointerEventArgs e)
{
// 是否正在连接
var myData = nodeOperationService.ConnectingManage;
if (myData.IsCreateing)
{
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsLeftButtonPressed);
if (isPass)
{
if (myData.Type == JunctionOfConnectionType.Invoke)
{
_vm.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
}
else
{
_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;
}
}
if (IsCanvasDragging)
{
// 拖动画布
Point currentMousePosition = e.GetPosition(this);
double deltaX = currentMousePosition.X - startCanvasDragPoint.X;
double deltaY = currentMousePosition.Y - startCanvasDragPoint.Y;
translateTransform.X += deltaX;
translateTransform.Y += deltaY;
startCanvasDragPoint = currentMousePosition;
}
}
// 缩放
private void NodeContainerView_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
var delta = e.Delta.Y;
if (delta < 0 && scaleTransform.ScaleX < 0.02) return;
if (delta > 0 && scaleTransform.ScaleY > 4.0) return;
// 缩放因子,根据滚轮方向调整
double zoomFactor = delta > 0 ? 1.23 : 0.78;
// 当前缩放比例
double oldScale = scaleTransform.ScaleX;
double newScale = oldScale * zoomFactor;
// 记录缩放前的鼠标位置
var mousePosition = GetPositionOfCanvas(e);
// 更新缩放比例
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
// 记录缩放后的鼠标位置
var newMousePosition = GetPositionOfCanvas(e);
// 更新 TranslateTransform,确保以鼠标位置为中心进行缩放
var s_position = newMousePosition - mousePosition; // 计算偏移量
translateTransform.X += s_position.X * newScale; // 根据缩放比例进行偏移
translateTransform.Y += s_position.Y * newScale; // 根据缩放比例进行偏移
}
#endregion
#endregion
#region 节点事件处理相关方法
///
/// 拖拽创建控件
///
///
///
private bool NodeOperationService_OnNodeViewCreate(NodeViewCreateEventArgs eventArgs)
{
if (eventArgs.NodeControl is not Control control)
{
return false;
}
var position = eventArgs.Position;// 坐标
SetNodeEvent(control); // 设置该控件与画布交互的相关事件
DragControl(control, position.X, position.Y);
PART_NodeContainer.Children.Add(control);
return true;
}
///
/// 设置节点与画布容器相关的操作事件
///
///
private void SetNodeEvent(Control nodeControl)
{
nodeControl.PointerMoved += NodeControl_PointerMoved; ;
nodeControl.PointerExited += NodeControl_PointerExited;
nodeControl.PointerPressed += Block_MouseLeftButtonDown;
nodeControl.PointerMoved += Block_MouseMove;
nodeControl.PointerReleased += (s, e) => IsControlDragging = false;
}
#endregion
#region 控件交互的相关方法
///
/// 移动控件
///
///
///
///
private void DragControl(Control nodeControl, double x, double y)
{
Canvas.SetLeft(nodeControl, x);
Canvas.SetTop(nodeControl, y);
}
///
/// 控件的鼠标右键按下事件,启动拖动操作。
///
private void Block_MouseLeftButtonDown(object? sender, PointerPressedEventArgs e)
{
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsRightButtonPressed);
if (!isPass)
{
return;
}
if (sender is NodeControlBase nodeControl)
{
IsControlDragging = true;
startControlDragPoint = GetPositionOfCanvas(e); // 记录鼠标按下时的位置
e.Handled = true; // 防止事件传播影响其他控件
}
}
///
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
///
private void Block_MouseMove(object? sender, PointerEventArgs e)
{
if (sender is not NodeControlBase nodeControl)
{
return;
}
if (IsCanvasDragging)
return;
if (IsSelectControl)
return;
if (IsControlDragging) // 如果正在拖动控件
{
Point currentPosition = GetPositionOfCanvas(e); // 获取当前鼠标位置
// 单个移动
if (selectNodeControls.Count == 0 || !selectNodeControls.Contains(nodeControl))
{
double deltaX = currentPosition.X - startControlDragPoint.X; // 计算X轴方向的偏移量
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
DragControl(nodeControl, newLeft, newTop);
nodeControl.UpdateLocationConnections();
}
// 批量移动
else
{
// 进行批量移动
// 获取旧位置
var oldLeft = Canvas.GetLeft(nodeControl);
var oldTop = Canvas.GetTop(nodeControl);
// 计算被选择控件的偏移量
var deltaX = /*(int)*/(currentPosition.X - startControlDragPoint.X);
var deltaY = /*(int)*/(currentPosition.Y - startControlDragPoint.Y);
// 移动被选择的控件
var newLeft = oldLeft + deltaX;
var newTop = oldTop + deltaY;
//this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
DragControl(nodeControl, newLeft, newTop);
// 计算控件实际移动的距离
var actualDeltaX = newLeft - oldLeft;
var actualDeltaY = newTop - oldTop;
// 移动其它选中的控件
foreach (var selectItemNode in selectNodeControls)
{
if (selectItemNode != nodeControl) // 跳过已经移动的控件
{
var otherNewLeft = Canvas.GetLeft(selectItemNode) + actualDeltaX;
var otherNewTop = Canvas.GetTop(selectItemNode) + actualDeltaY;
DragControl(selectItemNode, otherNewLeft, otherNewTop);
//this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点
}
}
// 更新节点之间线的连接位置
foreach (var item in selectNodeControls)
{
item.UpdateLocationConnections();
}
}
startControlDragPoint = currentPosition; // 更新起始点位置
}
}
private void NodeControl_PointerExited(object? sender, PointerEventArgs e)
{
IsPreviewNodeControl = false;
}
private void NodeControl_PointerMoved(object? sender, PointerEventArgs e)
{
IsPreviewNodeControl = true;
}
#endregion
}