您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
379 行
9.8 KiB
379 行
9.8 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.RMGUI;
|
|
using UnityEngine.RMGUI.StyleSheets;
|
|
|
|
namespace RMGUI.GraphView
|
|
{
|
|
[StyleSheet("Assets/NewUI/Editor/Views/GraphView.uss")]
|
|
public abstract class GraphView : DataWatchContainer, ISelection
|
|
{
|
|
private IGraphElementDataSource m_DataSource;
|
|
|
|
public IGraphElementDataSource dataSource
|
|
{
|
|
get { return m_DataSource; }
|
|
set
|
|
{
|
|
if (m_DataSource == value)
|
|
return;
|
|
|
|
RemoveWatch();
|
|
m_DataSource = value;
|
|
OnDataChanged();
|
|
AddWatch();
|
|
}
|
|
}
|
|
|
|
class ContentiewContainer : VisualContainer
|
|
{
|
|
public override bool Overlaps(Rect r)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
protected GraphViewDataMapper dataMapper { get; set; }
|
|
|
|
public VisualContainer contentViewContainer{ get; private set; }
|
|
|
|
public VisualContainer viewport
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
bool m_FrameAnimate = false;
|
|
|
|
public enum FrameType
|
|
{
|
|
All = 0,
|
|
Selection = 1,
|
|
Origin = 2
|
|
}
|
|
|
|
protected GraphView()
|
|
{
|
|
selection = new List<ISelectable>();
|
|
clipChildren = true;
|
|
contentViewContainer = new ContentiewContainer
|
|
{
|
|
name = "contentViewContainer",
|
|
clipChildren = false,
|
|
position = new Rect(0, 0, 0, 0)
|
|
};
|
|
// make it absolute and 0 sized so it acts as a transform to move children to and fro
|
|
AddChild(contentViewContainer);
|
|
|
|
dataMapper = new GraphViewDataMapper();
|
|
dataMapper[typeof(EdgeData)] = typeof(Edge);
|
|
}
|
|
|
|
public override void OnDataChanged()
|
|
{
|
|
if (m_DataSource == null)
|
|
return;
|
|
|
|
// process removals
|
|
var current = contentViewContainer.children.OfType<GraphElement>().ToList();
|
|
current.AddRange(children.OfType<GraphElement>());
|
|
foreach (var c in current)
|
|
{
|
|
// been removed?
|
|
if (!m_DataSource.elements.Contains(c.dataProvider))
|
|
{
|
|
c.parent.RemoveChild(c);
|
|
}
|
|
}
|
|
|
|
// process additions
|
|
var elements = contentViewContainer.children.OfType<GraphElement>().ToList();
|
|
elements.AddRange(children.OfType<GraphElement>().ToList());
|
|
foreach (var elementData in m_DataSource.elements)
|
|
{
|
|
// been added?
|
|
var found = false;
|
|
|
|
// TODO what the heck is a "dc" anyway?
|
|
foreach (var dc in elements)
|
|
{
|
|
if (dc != null && dc.dataProvider == elementData)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
InstanciateElement(elementData);
|
|
}
|
|
}
|
|
|
|
protected override object toWatch
|
|
{
|
|
get { return dataSource; }
|
|
}
|
|
|
|
// ISelection implementation
|
|
public List<ISelectable> selection { get; protected set; }
|
|
|
|
// functions to ISelection extensions
|
|
public void AddToSelection(ISelectable selectable)
|
|
{
|
|
var graphElement = selectable as GraphElement;
|
|
if (graphElement != null && graphElement.dataProvider != null)
|
|
graphElement.dataProvider.selected = true;
|
|
selection.Add(selectable);
|
|
contentViewContainer.Touch(ChangeType.Repaint);
|
|
}
|
|
|
|
public void RemoveFromSelection(ISelectable selectable)
|
|
{
|
|
var graphElement = selectable as GraphElement;
|
|
if (graphElement != null && graphElement.dataProvider != null)
|
|
graphElement.dataProvider.selected = false;
|
|
selection.Remove(selectable);
|
|
contentViewContainer.Touch(ChangeType.Repaint);
|
|
}
|
|
|
|
public void ClearSelection()
|
|
{
|
|
foreach (var graphElement in selection.OfType<GraphElement>())
|
|
{
|
|
if (graphElement.dataProvider != null)
|
|
graphElement.dataProvider.selected = false;
|
|
}
|
|
|
|
selection.Clear();
|
|
contentViewContainer.Touch(ChangeType.Repaint);
|
|
}
|
|
|
|
private void InstanciateElement(GraphElementData elementData)
|
|
{
|
|
// call factory
|
|
GraphElement newElem = dataMapper.Create(elementData);
|
|
|
|
if (newElem == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
newElem.SetPosition(elementData.position);
|
|
newElem.dataProvider = elementData;
|
|
|
|
if ((elementData.capabilities & Capabilities.Resizable) != 0)
|
|
{
|
|
var resizable = new Resizer();
|
|
newElem.AddManipulator(resizable);
|
|
newElem.AddDecorator(resizable);
|
|
newElem.borderBottom = 6;
|
|
}
|
|
|
|
bool attachToContainer = (elementData.capabilities & Capabilities.Floating) == 0;
|
|
if (attachToContainer)
|
|
contentViewContainer.AddChild(newElem);
|
|
else
|
|
AddChild(newElem);
|
|
}
|
|
|
|
protected EventPropagation DeleteSelection()
|
|
{
|
|
// and DeleteSelection would call that method.
|
|
var nodesContentViewData = dataSource as GraphViewDataSource;
|
|
if (nodesContentViewData == null)
|
|
return EventPropagation.Stop;
|
|
|
|
var elementsToRemove = new HashSet<GraphElementData>();
|
|
foreach (var selectedElement in selection.Cast<GraphElement>()
|
|
.Where(e => e != null && e.dataProvider != null))
|
|
{
|
|
if ((selectedElement.dataProvider.capabilities & Capabilities.Deletable) == 0)
|
|
continue;
|
|
|
|
elementsToRemove.Add(selectedElement.dataProvider);
|
|
|
|
var connectorColl = selectedElement.dataProvider as IConnectorCollection;
|
|
if (connectorColl == null)
|
|
continue;
|
|
|
|
elementsToRemove.UnionWith(connectorColl.inputConnectors.SelectMany(c => c.connections)
|
|
.Cast<GraphElementData>()
|
|
.Where(d => (d.capabilities & Capabilities.Deletable) != 0));
|
|
elementsToRemove.UnionWith(connectorColl.outputConnectors.SelectMany(c => c.connections)
|
|
.Cast<GraphElementData>()
|
|
.Where(d => (d.capabilities & Capabilities.Deletable) != 0));
|
|
}
|
|
|
|
// Notify the ends of connections that the connection is going way.
|
|
foreach (var connection in elementsToRemove.OfType<IConnection>())
|
|
{
|
|
if (connection.output != null)
|
|
{
|
|
connection.output.Disconnect(connection);
|
|
}
|
|
|
|
if (connection.input != null)
|
|
{
|
|
connection.input.Disconnect(connection);
|
|
}
|
|
}
|
|
|
|
foreach (var b in elementsToRemove)
|
|
nodesContentViewData.RemoveElement(b);
|
|
|
|
return EventPropagation.Stop;
|
|
}
|
|
|
|
protected EventPropagation FrameAll()
|
|
{
|
|
return Frame(FrameType.All);
|
|
}
|
|
|
|
protected EventPropagation FrameSelection()
|
|
{
|
|
return Frame(FrameType.Selection);
|
|
}
|
|
|
|
protected EventPropagation FrameOrigin()
|
|
{
|
|
return Frame(FrameType.Origin);
|
|
}
|
|
|
|
EventPropagation Frame(FrameType frameType)
|
|
{
|
|
// Reset container translation, scale and position
|
|
contentViewContainer.transform *= contentViewContainer.transform.inverse;
|
|
Rect p = contentViewContainer.position;
|
|
p.x = 0;
|
|
p.y = 0;
|
|
contentViewContainer.position = p;
|
|
|
|
if (frameType == FrameType.Origin)
|
|
{
|
|
return EventPropagation.Stop;
|
|
}
|
|
|
|
Rect rectToFit = contentViewContainer.position;
|
|
if (frameType == FrameType.Selection)
|
|
{
|
|
// Now calculate rectangle to fit all selected elements
|
|
if (selection.Count == 0)
|
|
{
|
|
return EventPropagation.Continue;
|
|
}
|
|
|
|
var graphElement = selection[0] as GraphElement;
|
|
if (graphElement != null)
|
|
{
|
|
rectToFit = graphElement.localBound;
|
|
}
|
|
|
|
rectToFit = selection.OfType<GraphElement>()
|
|
.Aggregate(rectToFit, (current, e) => RectUtils.Encompass(current, e.localBound));
|
|
}
|
|
else /*if (frameType == FrameType.All)*/
|
|
{
|
|
bool reachedFirstChild = false;
|
|
foreach (VisualElement child in contentViewContainer.children)
|
|
{
|
|
var graphElement = child as GraphElement;
|
|
if (graphElement == null ||
|
|
(graphElement.dataProvider.capabilities & Capabilities.Floating) != 0 ||
|
|
(graphElement.dataProvider is EdgeData))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!reachedFirstChild)
|
|
{
|
|
rectToFit = graphElement.localBound;
|
|
reachedFirstChild = true;
|
|
}
|
|
else
|
|
{
|
|
rectToFit = RectUtils.Encompass(rectToFit, graphElement.localBound);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 frameTranslation;
|
|
Vector3 frameScaling;
|
|
|
|
CalculateFrameTransform(rectToFit, out frameTranslation, out frameScaling);
|
|
|
|
if (m_FrameAnimate)
|
|
{
|
|
// TODO Animate framing
|
|
// RMAnimation animation = new RMAnimation();
|
|
// parent.Animate(parent)
|
|
// .Lerp(new string[] {"m_Scale", "m_Translation"},
|
|
// new object[] {parent.scale, parent.translation},
|
|
// new object[] {frameScaling, frameTranslation}, 0.08f);
|
|
}
|
|
else
|
|
{
|
|
Matrix4x4 t = Matrix4x4.identity;
|
|
t *= Matrix4x4.TRS(frameTranslation, Quaternion.identity, frameScaling);
|
|
contentViewContainer.transform = t;
|
|
}
|
|
|
|
contentViewContainer.Touch(ChangeType.Repaint);
|
|
|
|
return EventPropagation.Stop;
|
|
}
|
|
|
|
void CalculateFrameTransform(Rect rectToFit, out Vector3 frameTranslation, out Vector3 frameScaling)
|
|
{
|
|
// Give it full width/height
|
|
Rect clientRect = position;
|
|
|
|
// bring slightly smaller screen rect into GUI space
|
|
var screenRect = new Rect
|
|
{
|
|
xMin = 30,
|
|
xMax = clientRect.width - 30,
|
|
yMin = 30,
|
|
yMax = clientRect.height - 30
|
|
};
|
|
|
|
Matrix4x4 m = GUI.matrix;
|
|
GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one);
|
|
Rect identity = GUIUtility.ScreenToGUIRect(screenRect);
|
|
|
|
// measure zoom level necessary to fit the canvas rect into the screen rect
|
|
float zoomLevel = Math.Min(identity.width / rectToFit.width, identity.height / rectToFit.height);
|
|
|
|
// clamp
|
|
zoomLevel = Mathf.Clamp(zoomLevel, 0.08f, 1.0f);
|
|
|
|
var cachedScale = new Vector3(transform.GetColumn(0).magnitude,
|
|
transform.GetColumn(1).magnitude,
|
|
transform.GetColumn(2).magnitude);
|
|
Vector4 cachedTranslation = transform.GetColumn(3);
|
|
|
|
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(zoomLevel, zoomLevel, 1.0f));
|
|
|
|
var edge = new Vector2(clientRect.width, clientRect.height);
|
|
var origin = new Vector2(0, 0);
|
|
|
|
var r = new Rect
|
|
{
|
|
min = origin,
|
|
max = edge
|
|
};
|
|
|
|
var parentScale = new Vector3(transform.GetColumn(0).magnitude,
|
|
transform.GetColumn(1).magnitude,
|
|
transform.GetColumn(2).magnitude);
|
|
Vector2 offset = r.center - (rectToFit.center * parentScale.x);
|
|
|
|
// Update output values before leaving
|
|
frameTranslation = new Vector3(offset.x, offset.y, 0.0f);
|
|
frameScaling = parentScale;
|
|
|
|
transform = Matrix4x4.TRS(cachedTranslation, Quaternion.identity, cachedScale);
|
|
|
|
GUI.matrix = m;
|
|
}
|
|
}
|
|
}
|