I got a bit stuck trying to use GraphView for stuff that it’s probably not designed for, and while I was able to find workarounds for everything so far, I finally got stuck on something: is it possible to get a callback on Edge drag & drop?
My use case is the following: I got multiple Nodes in the GraphView, each of them manually fed with 1 input Port and 1+ ouput Ports. With that setup, if you start dragging the mouse from a port, an Edge is created and should be validated when connected to the Port of another Node (note: in my case, the Edge actually doesn’t stay when I release the mouse, not sure exactly why).
I derived custom Edges & Nodes from the original ones, while Ports are standard. I tried overriding Edge.OnPortChanged, but when this is called both input and output ports seem to be null (wondering if this has anything to do with the Ports looking disabled when dragging the Edge).
Did I miss an implementation somewhere? Or is there a totally different approach to that?
You can add a Manipulator to your ports that does that by implementing the
IEdgeConnectorListener interface. It has an OnDrop and
OnDropOutsidePort method. myPort.AddManipulator(new EdgeConnector<Edge>(new MyIEdgeConnectorListener()));.
There is also public virtual void Connect(Edge edge); on the Port class that you can override.
However, I still can’t get edges to connect altogether therefore I’m leaning toward these port not accepting edges to connect being the issue. Every time I drag an edge from a port, all other ports in the graph get disabled, not allowing to drop anywhere (the manipulator sees the edge has an input OR an output -the first port i connected to- but the other end is always null).
Is there a specific setup required to allow edges to connect to any input from any output (and the other way round)?
Edit: I found a workaround looking at some samples. Looks like the graph’s GetCompatiblePorts would always return an empty list, even when using the same type for every port. I therefore implemented an override of that method, a bit less restrictive than the original one. I now can drop edges on ports properly, which in turn unleashed the manipulator suggested by @Wobbers Cheers!
Sorry to necro but this solution with IEdgeConnectorListener only works to know if you dragged an edge from that specific port outward to somewhere else. Is it also possible to just get a callback whenever the port connection changed, so also when you drag into that port?
It seems that a Port has OnConnect and OnDisconnect actions but these are internal…
Hya, I encontered the same issue and as @alexanderameye said the devs (in all their eternal wisdom) decided to hide the OnConnect and OnDisconnect actions, not only that they made it hard to inherit and create your own Port by setting the DefaultEdgeConnectorListener as private (for some unknown reason).
If anyone needs this here is an my Port implementation that adds two actions OnConnect and OnDisconnect you can use
using System;
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace Editor.Agriculture.PredicateNodes
{
public class MyPort : Port
{
private class DefaultEdgeConnectorListener : IEdgeConnectorListener
{
private GraphViewChange m_GraphViewChange;
private List<Edge> m_EdgesToCreate;
private List<GraphElement> m_EdgesToDelete;
public DefaultEdgeConnectorListener()
{
this.m_EdgesToCreate = new List<Edge>();
this.m_EdgesToDelete = new List<GraphElement>();
this.m_GraphViewChange.edgesToCreate = this.m_EdgesToCreate;
}
public void OnDropOutsidePort(Edge edge, Vector2 position)
{
}
public void OnDrop(UnityEditor.Experimental.GraphView.GraphView graphView, Edge edge)
{
this.m_EdgesToCreate.Clear();
this.m_EdgesToCreate.Add(edge);
this.m_EdgesToDelete.Clear();
if (edge.input.capacity == Port.Capacity.Single)
{
foreach (Edge connection in edge.input.connections)
{
if (connection != edge)
this.m_EdgesToDelete.Add((GraphElement) connection);
}
}
if (edge.output.capacity == Port.Capacity.Single)
{
foreach (Edge connection in edge.output.connections)
{
if (connection != edge)
this.m_EdgesToDelete.Add((GraphElement) connection);
}
}
if (this.m_EdgesToDelete.Count > 0)
graphView.DeleteElements((IEnumerable<GraphElement>) this.m_EdgesToDelete);
List<Edge> edgesToCreate = this.m_EdgesToCreate;
if (graphView.graphViewChanged != null)
edgesToCreate = graphView.graphViewChanged(this.m_GraphViewChange).edgesToCreate;
foreach (Edge edge1 in edgesToCreate)
{
graphView.AddElement((GraphElement) edge1);
edge.input.Connect(edge1);
edge.output.Connect(edge1);
}
}
}
public Action<Port> OnConnect;
public Action<Port> OnDisconnect;
protected MyPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) : base(portOrientation, portDirection, portCapacity, type)
{
}
public override void Connect(Edge edge)
{
base.Connect(edge);
OnConnect?.Invoke(this);
}
public override void Disconnect(Edge edge)
{
base.Disconnect(edge);
OnDisconnect?.Invoke(this);
}
public override void DisconnectAll()
{
base.DisconnectAll();
OnDisconnect?.Invoke(this);
}
public new static MyPort Create<TEdge>(
Orientation orientation,
Direction direction,
Port.Capacity capacity,
System.Type type)
where TEdge : Edge, new()
{
var listener = new MyPort.DefaultEdgeConnectorListener();
var ele = new MyPort(orientation, direction, capacity, type)
{
m_EdgeConnector = (EdgeConnector) new EdgeConnector<TEdge>((IEdgeConnectorListener) listener)
};
ele.AddManipulator((IManipulator) ele.m_EdgeConnector);
return ele;
}
}
}