| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- using System;
- using System.Collections.Generic;
- using System.Windows;
- using System.Windows.Controls.Primitives;
- using System.Windows.Input;
- using System.Windows.Media;
- using TeamAAS.FlowEditor.Models;
- namespace TeamAAS.FlowEditor.Controls
- {
- /// <summary>
- /// 节点拖拽手柄 - 处理节点选择和拖动
- /// </summary>
- public class MoveThumb : Thumb
- {
- private FlowCanvas _canvas;
- // 拖拽锚点:起点时鼠标在画布坐标系的位置 + 各节点的起始坐标。
- // 用 Mouse.GetPosition(画布) 实时计算位移,而不是累加 DragDelta——
- // 后者在 PerMonitorV2 + 高 DPI 下是物理像素,会导致节点移动速度 ≠ 鼠标(4K 200% 缩放下节点跑飞)。
- private Point _dragStartMouseOnCanvas;
- private readonly List<FlowNode> _dragNodes = new List<FlowNode>();
- private readonly List<double> _startXs = new List<double>();
- private readonly List<double> _startYs = new List<double>();
- private int _diagCounter; // TODO 拖拽诊断,问题定位后删除
- public MoveThumb()
- {
- DragStarted += OnDragStartedHandler;
- DragDelta += OnDragDeltaHandler;
- DragCompleted += OnDragCompletedHandler;
- }
- private FlowCanvas FindCanvas()
- {
- if (_canvas != null) return _canvas;
- DependencyObject parent = VisualTreeHelper.GetParent(this);
- while (parent != null && !(parent is FlowCanvas))
- parent = VisualTreeHelper.GetParent(parent);
- _canvas = parent as FlowCanvas;
- return _canvas;
- }
- protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
- {
- // 选择由 FlowCanvas.OnMouseLeftButtonDown 统一处理(含 Ctrl 多选)
- base.OnMouseLeftButtonDown(e);
- }
- private void OnDragStartedHandler(object sender, DragStartedEventArgs e)
- {
- var canvas = FindCanvas();
- var node = DataContext as FlowNode;
- if (canvas == null || node == null) return;
- _dragStartMouseOnCanvas = Mouse.GetPosition(canvas);
- _dragNodes.Clear();
- _startXs.Clear();
- _startYs.Clear();
- var selectedNodes = canvas.GetSelectedNodes();
- if (selectedNodes != null && selectedNodes.Count > 1 && selectedNodes.Contains(node))
- {
- foreach (var selNode in selectedNodes)
- {
- _dragNodes.Add(selNode);
- _startXs.Add(selNode.X);
- _startYs.Add(selNode.Y);
- }
- }
- else
- {
- _dragNodes.Add(node);
- _startXs.Add(node.X);
- _startYs.Add(node.Y);
- }
- // TODO 拖拽诊断,问题定位后删除
- AppLogger.Info($"[DragDiag] started node={node.NodeName} mouseOnCanvas=({_dragStartMouseOnCanvas.X:F1},{_dragStartMouseOnCanvas.Y:F1}) nodePos=({node.X:F1},{node.Y:F1})", "DragDiag");
- }
- private void OnDragDeltaHandler(object sender, DragDeltaEventArgs e)
- {
- var node = DataContext as FlowNode;
- if (node == null) return;
- var canvas = FindCanvas();
- // 流程运行中 / 只读监控模式禁止移动节点
- if (canvas != null && (canvas.IsRunning || canvas.IsReadOnly)) return;
- if (_dragNodes.Count == 0) return;
- // 鼠标相对拖拽起点的位移(画布本地坐标,DPI / 缩放换算均正确)
- var mouse = Mouse.GetPosition(canvas);
- double dx = mouse.X - _dragStartMouseOnCanvas.X;
- double dy = mouse.Y - _dragStartMouseOnCanvas.Y;
- // TODO 拖拽诊断,问题定位后删除
- if ((++_diagCounter % 10) == 0)
- AppLogger.Info($"[DragDiag] delta mouse=({mouse.X:F1},{mouse.Y:F1}) d=({dx:F1},{dy:F1}) node0=({_dragNodes[0].X:F1},{_dragNodes[0].Y:F1}) startPos=({_startXs[0]:F1},{_startYs[0]:F1})", "DragDiag");
- if (_dragNodes.Count > 1)
- {
- // 整组硬墙语义:先预判每个成员的目标位置,
- // 只要有一个成员越出画布边界、或与非同组成员的节点碰撞,
- // 整组本次一动不动(碰壁即停),之后把鼠标拖回自由方向才能继续移动。
- // 同组成员以相同位移整体平移、相对位置不变,彼此不可能相撞,故不算障碍。
- for (int i = 0; i < _dragNodes.Count; i++)
- {
- double sx = _startXs[i] + dx;
- double sy = _startYs[i] + dy;
- if (sx < 0 || sy < 0 || canvas.CheckNodeCollision(_dragNodes[i], Math.Max(0, sx), Math.Max(0, sy), _dragNodes))
- return;
- }
- for (int i = 0; i < _dragNodes.Count; i++)
- {
- _dragNodes[i].X = Math.Max(0, _startXs[i] + dx);
- _dragNodes[i].Y = Math.Max(0, _startYs[i] + dy);
- }
- canvas.UpdateCanvasSize();
- return;
- }
- // 单选拖拽
- double newX = Math.Max(0, _startXs[0] + dx);
- double newY = Math.Max(0, _startYs[0] + dy);
- // 节点碰撞检测
- if (canvas != null)
- {
- if (canvas.CheckNodeCollision(node, newX, newY))
- {
- // 尝试只移动X
- if (!canvas.CheckNodeCollision(node, newX, node.Y))
- node.X = newX;
- // 尝试只移动Y
- else if (!canvas.CheckNodeCollision(node, node.X, newY))
- node.Y = newY;
- return;
- }
- // 对齐引导线检测
- canvas.CheckAlignment(node, ref newX, ref newY);
- }
- node.X = newX;
- node.Y = newY;
- // 自动拉线预览
- canvas?.UpdateAutoConnectPreview(node);
- // 通知画布更新大小
- canvas?.UpdateCanvasSize();
- }
- private void OnDragCompletedHandler(object sender, DragCompletedEventArgs e)
- {
- var canvas = FindCanvas();
- if (canvas == null) return;
- canvas.ClearAlignLines();
- // 自动拉线:仅单节点拖拽时生效(多节点移动不触发)
- var node = DataContext as FlowNode;
- var selectedNodes = canvas.GetSelectedNodes();
- if (node != null && (selectedNodes == null || selectedNodes.Count <= 1))
- canvas.TryAutoConnect(node);
- }
- }
- }
|