123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- using UnityEditor.Experimental.GraphView;
- using UnityEngine.UIElements;
- using UnityEngine;
- using System.Collections.Generic;
- using System;
- using System.Linq;
- namespace GraphProcessor
- {
- public class BaseEdgeDragHelper : EdgeDragHelper
- {
- // https://github.com/Unity-Technologies/UnityCsReference/blob/master/Modules/GraphViewEditor/Manipulators/EdgeDragHelper.cs#L21
- internal const int k_PanAreaWidth = 30;
- internal const int k_PanSpeed = 4;
- internal const int k_PanInterval = 10;
- internal const float k_MinSpeedFactor = 0.5f;
- internal const float k_MaxSpeedFactor = 7f;
- internal const float k_MaxPanSpeed = k_MaxSpeedFactor * k_PanSpeed;
- internal const float kPortDetectionWidth = 30;
- protected Dictionary<BaseNodeView, List<PortView>> compatiblePorts = new Dictionary<BaseNodeView, List<PortView>>();
- private Edge ghostEdge;
- protected GraphView graphView;
- protected static NodeAdapter nodeAdapter = new NodeAdapter();
- protected readonly IEdgeConnectorListener listener;
- private IVisualElementScheduledItem panSchedule;
- private Vector3 panDiff = Vector3.zero;
- private bool wasPanned;
- public bool resetPositionOnPan { get; set; }
- public BaseEdgeDragHelper(IEdgeConnectorListener listener)
- {
- this.listener = listener;
- resetPositionOnPan = true;
- Reset();
- }
- public override Edge edgeCandidate { get; set; }
- public override Port draggedPort { get; set; }
- public override void Reset(bool didConnect = false)
- {
- if (compatiblePorts != null && graphView != null)
- {
- // Reset the highlights.
- graphView.ports.ForEach((p) => {
- p.OnStopEdgeDragging();
- });
- compatiblePorts.Clear();
- }
- // Clean up ghost edge.
- if ((ghostEdge != null) && (graphView != null))
- {
- var pv = ghostEdge.input as PortView;
- graphView.schedule.Execute(() => {
- pv.portCapLit = false;
- // pv.UpdatePortView(pv.portData);
- }).ExecuteLater(10);
- graphView.RemoveElement(ghostEdge);
- }
- if (wasPanned)
- {
- if (!resetPositionOnPan || didConnect)
- {
- Vector3 p = graphView.contentViewContainer.transform.position;
- Vector3 s = graphView.contentViewContainer.transform.scale;
- graphView.UpdateViewTransform(p, s);
- }
- }
- if (panSchedule != null)
- panSchedule.Pause();
- if (ghostEdge != null)
- {
- ghostEdge.input = null;
- ghostEdge.output = null;
- }
- if (draggedPort != null && !didConnect)
- {
- draggedPort.portCapLit = false;
- draggedPort = null;
- }
- if (edgeCandidate != null)
- {
- edgeCandidate.SetEnabled(true);
- }
- ghostEdge = null;
- edgeCandidate = null;
- graphView = null;
- }
- public override bool HandleMouseDown(MouseDownEvent evt)
- {
- Vector2 mousePosition = evt.mousePosition;
- if ((draggedPort == null) || (edgeCandidate == null))
- {
- return false;
- }
- graphView = draggedPort.GetFirstAncestorOfType<GraphView>();
- if (graphView == null)
- {
- return false;
- }
- if (edgeCandidate.parent == null)
- {
- graphView.AddElement(edgeCandidate);
- }
- bool startFromOutput = (draggedPort.direction == Direction.Output);
- edgeCandidate.candidatePosition = mousePosition;
- edgeCandidate.SetEnabled(false);
- if (startFromOutput)
- {
- edgeCandidate.output = draggedPort;
- edgeCandidate.input = null;
- }
- else
- {
- edgeCandidate.output = null;
- edgeCandidate.input = draggedPort;
- }
- draggedPort.portCapLit = true;
- compatiblePorts.Clear();
- foreach (PortView port in graphView.GetCompatiblePorts(draggedPort, nodeAdapter))
- {
- compatiblePorts.TryGetValue(port.owner, out var portList);
- if (portList == null)
- portList = compatiblePorts[port.owner] = new List<PortView>();
- portList.Add(port);
- }
- // Sort ports by position in the node
- foreach (var kp in compatiblePorts)
- kp.Value.Sort((e1, e2) => e1.worldBound.y.CompareTo(e2.worldBound.y));
- // Only light compatible anchors when dragging an edge.
- graphView.ports.ForEach((p) => {
- p.OnStartEdgeDragging();
- });
- foreach (var kp in compatiblePorts)
- foreach (var port in kp.Value)
- port.highlight = true;
- edgeCandidate.UpdateEdgeControl();
- if (panSchedule == null)
- {
- panSchedule = graphView.schedule.Execute(Pan).Every(k_PanInterval).StartingIn(k_PanInterval);
- panSchedule.Pause();
- }
- wasPanned = false;
- edgeCandidate.layer = Int32.MaxValue;
- return true;
- }
- internal Vector2 GetEffectivePanSpeed(Vector2 mousePos)
- {
- Vector2 effectiveSpeed = Vector2.zero;
- if (mousePos.x <= k_PanAreaWidth)
- effectiveSpeed.x = -(((k_PanAreaWidth - mousePos.x) / k_PanAreaWidth) + 0.5f) * k_PanSpeed;
- else if (mousePos.x >= graphView.contentContainer.layout.width - k_PanAreaWidth)
- effectiveSpeed.x = (((mousePos.x - (graphView.contentContainer.layout.width - k_PanAreaWidth)) / k_PanAreaWidth) + 0.5f) * k_PanSpeed;
- if (mousePos.y <= k_PanAreaWidth)
- effectiveSpeed.y = -(((k_PanAreaWidth - mousePos.y) / k_PanAreaWidth) + 0.5f) * k_PanSpeed;
- else if (mousePos.y >= graphView.contentContainer.layout.height - k_PanAreaWidth)
- effectiveSpeed.y = (((mousePos.y - (graphView.contentContainer.layout.height - k_PanAreaWidth)) / k_PanAreaWidth) + 0.5f) * k_PanSpeed;
- effectiveSpeed = Vector2.ClampMagnitude(effectiveSpeed, k_MaxPanSpeed);
- return effectiveSpeed;
- }
- Vector2 lastMousePos;
- public override void HandleMouseMove(MouseMoveEvent evt)
- {
- var ve = (VisualElement)evt.target;
- Vector2 gvMousePos = ve.ChangeCoordinatesTo(graphView.contentContainer, evt.localMousePosition);
- panDiff = GetEffectivePanSpeed(gvMousePos);
- if (panDiff != Vector3.zero)
- panSchedule.Resume();
- else
- panSchedule.Pause();
- Vector2 mousePosition = evt.mousePosition;
- lastMousePos = evt.mousePosition;
- edgeCandidate.candidatePosition = mousePosition;
- // Draw ghost edge if possible port exists.
- Port endPort = GetEndPort(mousePosition);
- if (endPort != null)
- {
- if (ghostEdge == null)
- {
- ghostEdge = CreateEdgeView();
- ghostEdge.isGhostEdge = true;
- ghostEdge.pickingMode = PickingMode.Ignore;
- graphView.AddElement(ghostEdge);
- }
- if (edgeCandidate.output == null)
- {
- ghostEdge.input = edgeCandidate.input;
- if (ghostEdge.output != null)
- ghostEdge.output.portCapLit = false;
- ghostEdge.output = endPort;
- ghostEdge.output.portCapLit = true;
- }
- else
- {
- if (ghostEdge.input != null)
- ghostEdge.input.portCapLit = false;
- ghostEdge.input = endPort;
- ghostEdge.input.portCapLit = true;
- ghostEdge.output = edgeCandidate.output;
- }
- }
- else if (ghostEdge != null)
- {
- if (edgeCandidate.input == null)
- {
- if (ghostEdge.input != null)
- ghostEdge.input.portCapLit = false;
- }
- else
- {
- if (ghostEdge.output != null)
- ghostEdge.output.portCapLit = false;
- }
- graphView.RemoveElement(ghostEdge);
- ghostEdge.input = null;
- ghostEdge.output = null;
- ghostEdge = null;
- }
- }
- protected virtual EdgeView CreateEdgeView()
- {
- return new EdgeView();
- }
- private void Pan(TimerState ts)
- {
- graphView.viewTransform.position -= panDiff;
- // Workaround to force edge to update when we pan the graph
- edgeCandidate.output = edgeCandidate.output;
- edgeCandidate.input = edgeCandidate.input;
- edgeCandidate.UpdateEdgeControl();
- wasPanned = true;
- }
- public override void HandleMouseUp(MouseUpEvent evt)
- {
- bool didConnect = false;
- Vector2 mousePosition = evt.mousePosition;
- // Reset the highlights.
- graphView.ports.ForEach((p) => {
- p.OnStopEdgeDragging();
- });
- // Clean up ghost edges.
- if (ghostEdge != null)
- {
- if (ghostEdge.input != null)
- ghostEdge.input.portCapLit = false;
- if (ghostEdge.output != null)
- ghostEdge.output.portCapLit = false;
- graphView.RemoveElement(ghostEdge);
- ghostEdge.input = null;
- ghostEdge.output = null;
- ghostEdge = null;
- }
- Port endPort = GetEndPort(mousePosition);
- if (endPort == null && listener != null)
- {
- listener.OnDropOutsidePort(edgeCandidate, mousePosition);
- }
- edgeCandidate.SetEnabled(true);
- if (edgeCandidate.input != null)
- edgeCandidate.input.portCapLit = false;
- if (edgeCandidate.output != null)
- edgeCandidate.output.portCapLit = false;
- // If it is an existing valid edge then delete and notify the model (using DeleteElements()).
- if (edgeCandidate.input != null && edgeCandidate.output != null)
- {
- // Save the current input and output before deleting the edge as they will be reset
- Port oldInput = edgeCandidate.input;
- Port oldOutput = edgeCandidate.output;
- graphView.DeleteElements(new[] { edgeCandidate });
- // Restore the previous input and output
- edgeCandidate.input = oldInput;
- edgeCandidate.output = oldOutput;
- }
- // otherwise, if it is an temporary edge then just remove it as it is not already known my the model
- else
- {
- graphView.RemoveElement(edgeCandidate);
- }
- if (endPort != null)
- {
- if (endPort.direction == Direction.Output)
- edgeCandidate.output = endPort;
- else
- edgeCandidate.input = endPort;
- listener.OnDrop(graphView, edgeCandidate);
- didConnect = true;
- }
- else
- {
- edgeCandidate.output = null;
- edgeCandidate.input = null;
- }
- edgeCandidate.ResetLayer();
- edgeCandidate = null;
- compatiblePorts.Clear();
- Reset(didConnect);
- }
- Rect GetPortBounds(BaseNodeView nodeView, int index, List<PortView> portList)
- {
- var port = portList[index];
- var bounds = port.worldBound;
- if (port.orientation == Orientation.Horizontal)
- {
- // Increase horizontal port bounds
- bounds.xMin = nodeView.worldBound.xMin;
- bounds.xMax = nodeView.worldBound.xMax;
- if (index == 0)
- bounds.yMin = nodeView.worldBound.yMin;
- if (index == portList.Count - 1)
- bounds.yMax = nodeView.worldBound.yMax;
-
- if (index > 0)
- {
- Rect above = portList[index - 1].worldBound;
- bounds.yMin = (above.yMax + bounds.yMin) / 2.0f;
- }
- if (index < portList.Count - 1)
- {
- Rect below = portList[index + 1].worldBound;
- bounds.yMax = (below.yMin + bounds.yMax) / 2.0f;
- }
- if (port.direction == Direction.Input)
- bounds.xMin -= kPortDetectionWidth;
- else
- bounds.xMax += kPortDetectionWidth;
- }
- else
- {
- // Increase vertical port bounds
- if (port.direction == Direction.Input)
- bounds.yMin -= kPortDetectionWidth;
- else
- bounds.yMax += kPortDetectionWidth;
- }
- return bounds;
- }
- private Port GetEndPort(Vector2 mousePosition)
- {
- if (graphView == null)
- return null;
- Port bestPort = null;
- float bestDistance = 1e20f;
- foreach (var kp in compatiblePorts)
- {
- var nodeView = kp.Key;
- var portList = kp.Value;
- // We know that the port in the list is top to bottom in term of layout
- for (int i = 0; i < portList.Count; i++)
- {
- var port = portList[i];
- Rect bounds = GetPortBounds(nodeView, i, portList);
- float distance = Vector2.Distance(port.worldBound.position, mousePosition);
- // Check if mouse is over port.
- if (bounds.Contains(mousePosition) && distance < bestDistance)
- {
- bestPort = port;
- bestDistance = distance;
- }
- }
- }
- return bestPort;
- }
- }
- }
|