| 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;        }    }}
 |