445 lines
14 KiB
C#
445 lines
14 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using UnityEditor.Experimental.GraphView;
|
|
using System;
|
|
using System.Linq;
|
|
|
|
using UnityObject = UnityEngine.Object;
|
|
|
|
namespace UnityEditor.VFX.UI
|
|
{
|
|
class VFXSystemBorderFactory : UxmlFactory<VFXSystemBorder>
|
|
{}
|
|
|
|
|
|
class VFXSystemBorder : GraphElement, IControlledElement<VFXSystemController>, IDisposable
|
|
{
|
|
class Content : ImmediateModeElement
|
|
{
|
|
VFXSystemBorder m_Border;
|
|
|
|
public Content(VFXSystemBorder border)
|
|
{
|
|
m_Border = border;
|
|
}
|
|
|
|
protected override void ImmediateRepaint()
|
|
{
|
|
m_Border.RecreateResources();
|
|
VFXView view = GetFirstAncestorOfType<VFXView>();
|
|
if (view != null && m_Border.m_Mat != null)
|
|
{
|
|
float radius = m_Border.resolvedStyle.borderTopLeftRadius;
|
|
|
|
float realBorder = m_Border.resolvedStyle.borderLeftWidth * view.scale;
|
|
|
|
Vector4 size = new Vector4(layout.width * .5f, layout.height * 0.5f, 0, 0);
|
|
m_Border.m_Mat.SetVector("_Size", size);
|
|
m_Border.m_Mat.SetFloat("_Border", realBorder < 1.75f ? 1.75f / view.scale : m_Border.resolvedStyle.borderLeftWidth);
|
|
m_Border.m_Mat.SetFloat("_Radius", radius);
|
|
|
|
float opacity = m_Border.resolvedStyle.opacity;
|
|
|
|
|
|
Color start = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.startColor.gamma : m_Border.startColor;
|
|
start.a *= opacity;
|
|
m_Border.m_Mat.SetColor("_ColorStart", start);
|
|
Color end = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.endColor.gamma : m_Border.endColor;
|
|
end.a *= opacity;
|
|
m_Border.m_Mat.SetColor("_ColorEnd", end);
|
|
|
|
Color middle = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.middleColor.gamma : m_Border.middleColor;
|
|
middle.a *= opacity;
|
|
m_Border.m_Mat.SetColor("_ColorMiddle", middle);
|
|
|
|
m_Border.m_Mat.SetPass(0);
|
|
|
|
Graphics.DrawMeshNow(s_Mesh, Matrix4x4.Translate(new Vector3(size.x, size.y, 0)));
|
|
}
|
|
}
|
|
}
|
|
|
|
Material m_Mat;
|
|
|
|
static Mesh s_Mesh;
|
|
|
|
public VFXSystemBorder()
|
|
{
|
|
RecreateResources();
|
|
|
|
var tpl = VFXView.LoadUXML("VFXSystemBorder");
|
|
tpl.CloneTree(this);
|
|
|
|
this.AddStyleSheetPath("VFXSystemBorder");
|
|
|
|
this.style.overflow = Overflow.Visible;
|
|
|
|
m_Title = this.Query<Label>("title");
|
|
m_TitleField = this.Query<TextField>("title-field");
|
|
|
|
m_TitleField.style.display = DisplayStyle.None;
|
|
|
|
m_Title.RegisterCallback<MouseDownEvent>(OnTitleMouseDown);
|
|
|
|
m_TitleField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(OnTitleBlur);
|
|
m_TitleField.RegisterCallback<ChangeEvent<string>>(OnTitleChange);
|
|
m_Title.RegisterCallback<GeometryChangedEvent>(OnTitleRelayout);
|
|
|
|
Content content = new Content(this);
|
|
content.style.position = UnityEngine.UIElements.Position.Absolute;
|
|
content.style.top = content.style.left = content.style.right = content.style.bottom = 0f;
|
|
content.pickingMode = PickingMode.Ignore;
|
|
|
|
pickingMode = PickingMode.Ignore;
|
|
Add(content);
|
|
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
|
this.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu));
|
|
visible = false;
|
|
}
|
|
|
|
public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
|
{
|
|
}
|
|
|
|
public void OnRename()
|
|
{
|
|
m_TitleField.RemoveFromClassList("empty");
|
|
m_TitleField.value = m_Title.text;
|
|
|
|
m_TitleField.style.display = DisplayStyle.Flex;
|
|
UpdateTitleFieldRect();
|
|
m_TitleField.Q(TextField.textInputUssName).Focus();
|
|
m_TitleField.SelectAll();
|
|
}
|
|
|
|
Label m_Title;
|
|
TextField m_TitleField;
|
|
|
|
|
|
void OnTitleMouseDown(MouseDownEvent e)
|
|
{
|
|
if (e.clickCount == 2)
|
|
{
|
|
OnRename();
|
|
e.StopPropagation();
|
|
e.PreventDefault();
|
|
}
|
|
}
|
|
|
|
void OnTitleRelayout(GeometryChangedEvent e)
|
|
{
|
|
UpdateTitleFieldRect();
|
|
RecomputeBounds();
|
|
}
|
|
|
|
void UpdateTitleFieldRect()
|
|
{
|
|
Rect rect = m_Title.layout;
|
|
|
|
m_Title.parent.ChangeCoordinatesTo(m_TitleField.parent, rect);
|
|
|
|
|
|
m_TitleField.style.top = rect.yMin - 6;
|
|
m_TitleField.style.left = rect.xMin - 5;
|
|
m_TitleField.style.right = m_Title.resolvedStyle.marginRight + m_Title.resolvedStyle.borderRightWidth + 2;
|
|
m_TitleField.style.height = rect.height - m_Title.resolvedStyle.marginTop - m_Title.resolvedStyle.marginBottom;
|
|
}
|
|
|
|
void OnTitleBlur(FocusOutEvent e)
|
|
{
|
|
title = m_TitleField.value;
|
|
m_TitleField.style.display = DisplayStyle.None;
|
|
|
|
controller.title = title;
|
|
}
|
|
|
|
void OnContextChanged(GeometryChangedEvent e)
|
|
{
|
|
RecomputeBounds();
|
|
}
|
|
|
|
void OnTitleChange(ChangeEvent<string> e)
|
|
{
|
|
title = m_TitleField.value;
|
|
if (string.IsNullOrEmpty(e.previousValue) != string.IsNullOrEmpty(e.newValue))
|
|
{
|
|
RecomputeBounds();
|
|
}
|
|
}
|
|
|
|
public override string title
|
|
{
|
|
get
|
|
{
|
|
return m_Title.text;
|
|
}
|
|
set
|
|
{
|
|
if (m_Title.text != value)
|
|
{
|
|
m_Title.text = value;
|
|
RecomputeBounds();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool m_WaitingRecompute;
|
|
|
|
public void RecomputeBounds()
|
|
{
|
|
if (m_WaitingRecompute)
|
|
return;
|
|
visible = true;
|
|
//title width should be at least as wide as a context to be valid.
|
|
float titleWidth = m_Title.layout.width;
|
|
bool invalidTitleWidth = float.IsNaN(titleWidth) || titleWidth < 50;
|
|
bool titleEmpty = string.IsNullOrEmpty(m_Title.text) || invalidTitleWidth;
|
|
if (titleEmpty)
|
|
{
|
|
m_Title.AddToClassList("empty");
|
|
}
|
|
else
|
|
{
|
|
m_Title.RemoveFromClassList("empty");
|
|
}
|
|
|
|
Rect rect = Rect.zero;
|
|
|
|
if (m_Contexts != null)
|
|
{
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
if (context != null)
|
|
{
|
|
if (rect == Rect.zero)
|
|
{
|
|
rect = context.localBound;
|
|
}
|
|
else
|
|
{
|
|
rect = RectUtils.Encompass(rect, context.GetPosition());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (float.IsNaN(rect.xMin) || float.IsNaN(rect.yMin) || float.IsNaN(rect.width) || float.IsNaN(rect.height))
|
|
rect = Rect.zero;
|
|
|
|
rect = RectUtils.Inflate(rect, 20, titleEmpty ? 20 : m_Title.layout.height, 20, 20);
|
|
|
|
if (invalidTitleWidth)
|
|
{
|
|
SetPosition(rect);
|
|
if (!m_WaitingRecompute)
|
|
{
|
|
m_WaitingRecompute = true;
|
|
schedule.Execute(() => { m_WaitingRecompute = false; RecomputeBounds(); }).ExecuteLater(0); // title height might have changed if width have changed
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetPosition(rect);
|
|
}
|
|
}
|
|
|
|
VFXContextUI[] m_Contexts;
|
|
private VFXContextUI[] contexts
|
|
{
|
|
get
|
|
{
|
|
return m_Contexts;
|
|
}
|
|
set
|
|
{
|
|
if (m_Contexts != null)
|
|
{
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
context?.UnregisterCallback<GeometryChangedEvent>(OnContextChanged);
|
|
}
|
|
}
|
|
m_Contexts = value;
|
|
if (m_Contexts != null)
|
|
{
|
|
foreach (var context in m_Contexts)
|
|
{
|
|
context?.RegisterCallback<GeometryChangedEvent>(OnContextChanged);
|
|
}
|
|
}
|
|
RecomputeBounds();
|
|
}
|
|
}
|
|
|
|
void RecreateResources()
|
|
{
|
|
if (s_Mesh == null)
|
|
{
|
|
s_Mesh = new Mesh();
|
|
int verticeCount = 20;
|
|
|
|
var vertices = new Vector3[verticeCount];
|
|
var uvsBorder = new Vector2[verticeCount];
|
|
var uvsDistance = new Vector2[verticeCount];
|
|
|
|
for (int ix = 0; ix < 4; ++ix)
|
|
{
|
|
for (int iy = 0; iy < 4; ++iy)
|
|
{
|
|
vertices[ix + iy * 4] = new Vector3(ix < 2 ? -1 : 1, iy < 2 ? -1 : 1, 0);
|
|
uvsBorder[ix + iy * 4] = new Vector2(ix == 0 || ix == 3 ? 1 : 0, iy == 0 || iy == 3 ? 1 : 0);
|
|
uvsDistance[ix + iy * 4] = new Vector2(iy < 2 ? ix / 2 : 2 - ix / 2, iy < 2 ? 0 : 1);
|
|
}
|
|
}
|
|
|
|
for (int i = 16; i < 20; ++i)
|
|
{
|
|
vertices[i] = vertices[i - 16];
|
|
uvsBorder[i] = uvsBorder[i - 16];
|
|
uvsDistance[i] = new Vector2(2, 2);
|
|
}
|
|
|
|
vertices[16] = vertices[0];
|
|
vertices[17] = vertices[1];
|
|
vertices[18] = vertices[4];
|
|
vertices[19] = vertices[5];
|
|
|
|
uvsBorder[16] = uvsBorder[0];
|
|
uvsBorder[17] = uvsBorder[1];
|
|
uvsBorder[18] = uvsBorder[4];
|
|
uvsBorder[19] = uvsBorder[5];
|
|
|
|
uvsDistance[16] = new Vector2(2, 2);
|
|
uvsDistance[17] = new Vector2(2, 2);
|
|
uvsDistance[18] = new Vector2(2, 2);
|
|
uvsDistance[19] = new Vector2(2, 2);
|
|
|
|
var indices = new int[4 * 8];
|
|
|
|
for (int ix = 0; ix < 3; ++ix)
|
|
{
|
|
for (int iy = 0; iy < 3; ++iy)
|
|
{
|
|
int quadIndex = (ix + iy * 3);
|
|
if (quadIndex == 4)
|
|
continue;
|
|
else if (quadIndex > 4)
|
|
--quadIndex;
|
|
int vertIndex = quadIndex * 4;
|
|
|
|
|
|
indices[vertIndex] = ix + iy * 4;
|
|
indices[vertIndex + 1] = ix + (iy + 1) * 4;
|
|
indices[vertIndex + 2] = ix + 1 + (iy + 1) * 4;
|
|
indices[vertIndex + 3] = ix + 1 + iy * 4;
|
|
if (quadIndex == 3)
|
|
{
|
|
indices[vertIndex] = 18;
|
|
indices[vertIndex + 3] = 19;
|
|
}
|
|
}
|
|
}
|
|
|
|
s_Mesh.vertices = vertices;
|
|
s_Mesh.uv = uvsBorder;
|
|
s_Mesh.uv2 = uvsDistance;
|
|
s_Mesh.SetIndices(indices, MeshTopology.Quads, 0);
|
|
}
|
|
if (m_Mat == null)
|
|
m_Mat = new Material(Shader.Find("Hidden/VFX/GradientDashedBorder"));
|
|
}
|
|
|
|
void IDisposable.Dispose()
|
|
{
|
|
UnityObject.DestroyImmediate(m_Mat);
|
|
}
|
|
|
|
Color m_StartColor;
|
|
public Color startColor
|
|
{
|
|
get
|
|
{
|
|
return m_StartColor;
|
|
}
|
|
set
|
|
{
|
|
m_StartColor = value;
|
|
}
|
|
}
|
|
Color m_EndColor;
|
|
public Color endColor
|
|
{
|
|
get
|
|
{
|
|
return m_EndColor;
|
|
}
|
|
set
|
|
{
|
|
m_EndColor = value;
|
|
}
|
|
}
|
|
Color m_MiddleColor;
|
|
public Color middleColor
|
|
{
|
|
get
|
|
{
|
|
return m_MiddleColor;
|
|
}
|
|
set
|
|
{
|
|
m_MiddleColor = value;
|
|
}
|
|
}
|
|
|
|
static readonly CustomStyleProperty<Color> s_StartColorProperty = new CustomStyleProperty<Color>("--start-color");
|
|
static readonly CustomStyleProperty<Color> s_EndColorProperty = new CustomStyleProperty<Color>("--end-color");
|
|
static readonly CustomStyleProperty<Color> s_MiddleColorProperty = new CustomStyleProperty<Color>("--middle-color");
|
|
private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
|
|
{
|
|
var customStyle = e.customStyle;
|
|
customStyle.TryGetValue(s_StartColorProperty, out m_StartColor);
|
|
customStyle.TryGetValue(s_EndColorProperty, out m_EndColor);
|
|
customStyle.TryGetValue(s_MiddleColorProperty, out m_MiddleColor);
|
|
}
|
|
|
|
VFXSystemController m_Controller;
|
|
Controller IControlledElement.controller
|
|
{
|
|
get { return m_Controller; }
|
|
}
|
|
public VFXSystemController controller
|
|
{
|
|
get { return m_Controller; }
|
|
set
|
|
{
|
|
if (m_Controller != null)
|
|
{
|
|
m_Controller.UnregisterHandler(this);
|
|
}
|
|
m_Controller = value;
|
|
if (m_Controller != null)
|
|
{
|
|
m_Controller.RegisterHandler(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
VFXView view = GetFirstAncestorOfType<VFXView>();
|
|
if (view == null || m_Controller == null)
|
|
return;
|
|
contexts = controller.contexts.Select(t => view.GetGroupNodeElement(t) as VFXContextUI).ToArray();
|
|
|
|
title = controller.contexts[0].model.GetGraph().systemNames.GetUniqueSystemName(controller.contexts[0].model.GetData());
|
|
}
|
|
|
|
public void OnControllerChanged(ref ControllerChangedEvent e)
|
|
{
|
|
Update();
|
|
}
|
|
}
|
|
}
|