您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

440 行
11 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.RMGUI;
namespace RMGUI.GraphView
{
[StyleSheet("Assets/NewUI/Editor/Views/GraphView.uss")]
public abstract class GraphView : DataWatchContainer, ISelection
{
private GraphViewPresenter m_Presenter;
public T GetPresenter<T>() where T : GraphViewPresenter
{
return presenter as T;
}
public GraphViewPresenter presenter
{
get { return m_Presenter; }
set
{
if (m_Presenter == value)
return;
RemoveWatch();
m_Presenter = value;
OnDataChanged();
AddWatch();
}
}
class ContentViewContainer : VisualContainer
{
public override bool Overlaps(Rect r)
{
return true;
}
}
protected GraphViewTypeMapper typeMapper { 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 ContentViewContainer
{
name = "contentViewContainer",
clipChildren = false,
pickingMode = PickingMode.Ignore
};
// make it absolute and 0 sized so it acts as a transform to move children to and fro
AddChild(contentViewContainer);
typeMapper = new GraphViewTypeMapper();
typeMapper[typeof(EdgePresenter)] = typeof(Edge);
}
public override void OnDataChanged()
{
if (m_Presenter == 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_Presenter.elements.Contains(c.presenter))
{
c.parent.RemoveChild(c);
selection.Remove(c);
}
}
// process additions
var elements = contentViewContainer.children.OfType<GraphElement>().ToList();
elements.AddRange(children.OfType<GraphElement>().ToList());
foreach (GraphElementPresenter elementPresenter in m_Presenter.elements)
{
// been added?
var found = false;
foreach (var dc in elements)
{
if (dc != null && dc.presenter == elementPresenter)
{
found = true;
break;
}
}
if (!found)
InstantiateElement(elementPresenter);
}
}
protected override object toWatch
{
get { return presenter; }
}
// ISelection implementation
public List<ISelectable> selection { get; protected set; }
// functions to ISelection extensions
public virtual void AddToSelection(ISelectable selectable)
{
var graphElement = selectable as GraphElement;
if (graphElement != null && graphElement.presenter != null)
graphElement.presenter.selected = true;
selection.Add(selectable);
contentViewContainer.Touch(ChangeType.Repaint);
}
public virtual void RemoveFromSelection(ISelectable selectable)
{
var graphElement = selectable as GraphElement;
if (graphElement != null && graphElement.presenter != null)
graphElement.presenter.selected = false;
selection.Remove(selectable);
contentViewContainer.Touch(ChangeType.Repaint);
}
public virtual void ClearSelection()
{
foreach (var graphElement in selection.OfType<GraphElement>())
{
if (graphElement.presenter != null)
graphElement.presenter.selected = false;
}
selection.Clear();
contentViewContainer.Touch(ChangeType.Repaint);
}
private void InstantiateElement(GraphElementPresenter elementPresenter)
{
// call factory
GraphElement newElem = typeMapper.Create(elementPresenter);
if (newElem == null)
{
return;
}
newElem.SetPosition(elementPresenter.position);
newElem.presenter = elementPresenter;
if ((elementPresenter.capabilities & Capabilities.Resizable) != 0)
{
newElem.AddChild(new Resizer());
newElem.borderBottom = 6;
}
bool attachToContainer = (elementPresenter.capabilities & Capabilities.Floating) == 0;
if (attachToContainer)
contentViewContainer.AddChild(newElem);
else
AddChild(newElem);
}
protected EventPropagation DeleteSelection()
{
// and DeleteSelection would call that method.
if (presenter == null)
return EventPropagation.Stop;
var elementsToRemove = new HashSet<GraphElementPresenter>();
foreach (var selectedElement in selection.Cast<GraphElement>()
.Where(e => e != null && e.presenter != null))
{
if ((selectedElement.presenter.capabilities & Capabilities.Deletable) == 0)
continue;
elementsToRemove.Add(selectedElement.presenter);
var connectorColl = selectedElement.GetPresenter<NodePresenter>();
if (connectorColl == null)
continue;
elementsToRemove.UnionWith(connectorColl.inputAnchors.SelectMany(c => c.connections)
.Where(d => (d.capabilities & Capabilities.Deletable) != 0)
.Cast<GraphElementPresenter>());
elementsToRemove.UnionWith(connectorColl.outputAnchors.SelectMany(c => c.connections)
.Where(d => (d.capabilities & Capabilities.Deletable) != 0)
.Cast<GraphElementPresenter>());
}
// Notify the ends of connections that the connection is going way.
foreach (var connection in elementsToRemove.OfType<EdgePresenter>())
{
if (connection.output != null)
{
connection.output.Disconnect(connection);
}
if (connection.input != null)
{
connection.input.Disconnect(connection);
}
}
foreach (var b in elementsToRemove)
presenter.RemoveElement(b);
return EventPropagation.Stop;
}
public EventPropagation FrameAll()
{
return Frame(FrameType.All);
}
public EventPropagation FrameSelection()
{
return Frame(FrameType.Selection);
}
public EventPropagation FrameOrigin()
{
return Frame(FrameType.Origin);
}
protected EventPropagation FramePrev()
{
if (contentViewContainer.childrenCount == 0)
return EventPropagation.Continue;
var childrenList = contentViewContainer.children.ToList();
childrenList.Reverse();
return FramePrevNext(childrenList.GetEnumerator());
}
protected EventPropagation FrameNext()
{
if (contentViewContainer.childrenCount == 0)
return EventPropagation.Continue;
return FramePrevNext(contentViewContainer.GetChildren());
}
// TODO: Do we limit to GraphElements or can we tab through ISelectable's?
EventPropagation FramePrevNext(List<VisualElement>.Enumerator childrenEnum)
{
GraphElement graphElement = null;
// Start from current selection, if any
if (selection.Count != 0)
graphElement = selection[0] as GraphElement;
var it = childrenEnum;
while (it.MoveNext())
{
if (graphElement == null)
{
// Select first item we encounter
graphElement = it.Current as GraphElement;
break;
}
if (graphElement == it.Current as GraphElement)
graphElement = null; // I.e. select next item in line
}
if (graphElement == null)
{
// It is possible we exhausted the list, so go back to the start
it = childrenEnum;
it.MoveNext();
graphElement = it.Current as GraphElement;
}
if (graphElement == null)
return EventPropagation.Continue;
// New selection...
ClearSelection();
AddToSelection(graphElement);
// ...and frame this new selection
return Frame(FrameType.Selection);
}
EventPropagation Frame(FrameType frameType)
{
// Reset container translation, scale and position
contentViewContainer.transform = Matrix4x4.identity;
// TODO remove once we clarify Touch()
contentViewContainer.Touch(ChangeType.Repaint);
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)*/
{
rectToFit = CalculateRectToFitAll(contentViewContainer);
}
Vector3 frameTranslation;
Vector3 frameScaling;
int frameBorder = 30;
CalculateFrameTransform(rectToFit, position, frameBorder, 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;
}
public static Rect CalculateRectToFitAll(VisualContainer contentViewContainer)
{
Rect rectToFit = contentViewContainer.position;
bool reachedFirstChild = false;
var child = contentViewContainer.GetChildren();
while (child.MoveNext())
{
var graphElement = child.Current as GraphElement;
var elementPresenter = (graphElement != null) ? graphElement.GetPresenter<GraphElementPresenter>() : null;
if (elementPresenter == null ||
(elementPresenter.capabilities & Capabilities.Floating) != 0 ||
(elementPresenter is EdgePresenter))
{
continue;
}
if (!reachedFirstChild)
{
rectToFit = graphElement.localBound;
reachedFirstChild = true;
}
else
{
rectToFit = RectUtils.Encompass(rectToFit, graphElement.localBound);
}
}
return rectToFit;
}
public static void CalculateFrameTransform(Rect rectToFit, Rect clientRect, int border, out Vector3 frameTranslation, out Vector3 frameScaling)
{
// bring slightly smaller screen rect into GUI space
var screenRect = new Rect
{
xMin = border,
xMax = clientRect.width - border,
yMin = border,
yMax = clientRect.height - border
};
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, ContentZoomer.DefaultMinScale.y, 1.0f);
var 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;
GUI.matrix = m;
}
}
}