2021-09-09 20:42:29 -04:00

486 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.Profiling;
using PositionType = UnityEngine.UIElements.Position;
namespace UnityEditor.VFX.UI
{
class VFXNodeUI : Node, IControlledElement, ISettableControlledElement<VFXNodeController>, IVFXMovable
{
VFXNodeController m_Controller;
Controller IControlledElement.controller
{
get { return m_Controller; }
}
public void OnMoved()
{
controller.position = GetPosition().position;
}
public VFXNodeController controller
{
get { return m_Controller; }
set
{
if (m_Controller != null)
{
m_Controller.UnregisterHandler(this);
}
m_Controller = value;
OnNewController();
if (m_Controller != null)
{
m_Controller.RegisterHandler(this);
}
}
}
protected virtual void OnNewController()
{
if (controller != null)
viewDataKey = string.Format("NodeID-{0}", controller.model.GetInstanceID());
}
public void OnSelectionMouseDown(MouseDownEvent e)
{
var gv = GetFirstAncestorOfType<VFXView>();
if (IsSelected(gv))
{
if (e.actionKey)
{
Unselect(gv);
}
}
else
{
Select(gv, e.actionKey);
}
}
public VisualElement settingsContainer {get; private set; }
private List<PropertyRM> m_Settings = new List<PropertyRM>();
static string UXMLResourceToPackage(string resourcePath)
{
return VisualEffectAssetEditorUtility.editorResourcesPath + "/" + resourcePath + ".uxml";
}
public VFXNodeUI(string template) : base(UXMLResourceToPackage(template))
{
Initialize();
}
void OnFocusIn(FocusInEvent e)
{
var gv = GetFirstAncestorOfType<VFXView>();
if (!IsSelected(gv))
Select(gv, false);
e.StopPropagation();
}
VisualElement m_SelectionBorder;
public VFXNodeUI() : base(UXMLResourceToPackage("uxml/VFXNode"))
{
styleSheets.Add(EditorGUIUtility.Load("StyleSheets/GraphView/Node.uss") as StyleSheet);
Initialize();
}
bool m_Hovered;
void OnMouseEnter(MouseEnterEvent e)
{
m_Hovered = true;
UpdateBorder();
e.PreventDefault();
//e.StopPropagation();
}
void OnMouseLeave(MouseLeaveEvent e)
{
m_Hovered = false;
UpdateBorder();
e.PreventDefault();
//e.StopPropagation();
}
bool m_Selected;
public override void OnSelected()
{
base.OnSelected();
m_Selected = true;
UpdateBorder();
}
public override void OnUnselected()
{
m_Selected = false;
UpdateBorder();
base.OnUnselected();
}
void UpdateBorder()
{
m_SelectionBorder.style.borderBottomWidth =
m_SelectionBorder.style.borderTopWidth =
m_SelectionBorder.style.borderLeftWidth =
m_SelectionBorder.style.borderRightWidth = (m_Selected ? 2 : (m_Hovered ? 1 : 0));
m_SelectionBorder.style.borderBottomColor =
m_SelectionBorder.style.borderTopColor =
m_SelectionBorder.style.borderLeftColor =
m_SelectionBorder.style.borderRightColor = m_Selected ? new Color(68.0f / 255.0f, 192.0f / 255.0f, 255.0f / 255.0f, 1.0f) : (m_Hovered ? new Color(68.0f / 255.0f, 192.0f / 255.0f, 255.0f / 255.0f, 0.5f) : Color.clear);
}
void Initialize()
{
this.AddStyleSheetPath("VFXNode");
AddToClassList("VFXNodeUI");
RegisterCallback<MouseEnterEvent>(OnMouseEnter);
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
RegisterCallback<FocusInEvent>(OnFocusIn);
m_SelectionBorder = this.Query("selection-border");
}
public virtual void OnControllerChanged(ref ControllerChangedEvent e)
{
if (e.controller == controller)
{
Profiler.BeginSample(GetType().Name + "::SelfChange()");
SelfChange();
Profiler.EndSample();
}
else if (e.controller is VFXDataAnchorController)
{
RefreshExpandedState();
}
}
protected virtual bool HasPosition()
{
return true;
}
protected VisualElement m_SettingsDivider;
public virtual bool hasSettingDivider
{
get { return true; }
}
protected virtual void SyncSettings()
{
Profiler.BeginSample("VFXNodeUI.SyncSettings");
if (settingsContainer == null && controller.settings != null)
{
object settings = controller.settings;
settingsContainer = this.Q("settings");
m_SettingsDivider = this.Q("settings-divider");
foreach (var setting in controller.settings)
{
AddSetting(setting);
}
}
if (settingsContainer != null)
{
var activeSettings = controller.model.GetSettings(false, VFXSettingAttribute.VisibleFlags.InGraph);
for (int i = 0; i < m_Settings.Count; ++i)
m_Settings[i].RemoveFromHierarchy();
hasSettings = false;
for (int i = 0; i < m_Settings.Count; ++i)
{
PropertyRM prop = m_Settings[i];
if (prop != null && activeSettings.Any(s => s.field.Name == controller.settings[i].name))
{
hasSettings = true;
settingsContainer.Add(prop);
prop.Update();
}
}
if (hasSettings)
{
settingsContainer.RemoveFromClassList("nosettings");
}
else
{
settingsContainer.AddToClassList("nosettings");
}
}
if (m_SettingsDivider != null)
m_SettingsDivider.visible = hasSettingDivider && hasSettings;
Profiler.EndSample();
}
protected bool hasSettings
{
get;
private set;
}
void SyncAnchors()
{
Profiler.BeginSample("VFXNodeUI.SyncAnchors");
SyncAnchors(controller.inputPorts, inputContainer);
SyncAnchors(controller.outputPorts, outputContainer);
Profiler.EndSample();
}
void SyncAnchors(ReadOnlyCollection<VFXDataAnchorController> ports, VisualElement container)
{
var existingAnchors = container.Children().Cast<VFXDataAnchor>().ToDictionary(t => t.controller, t => t);
Profiler.BeginSample("VFXNodeUI.SyncAnchors Delete");
var deletedControllers = existingAnchors.Keys.Except(ports).ToArray();
foreach (var deletedController in deletedControllers)
{
//Explicitely remove edges before removing anchor.
GetFirstAncestorOfType<VFXView>().RemoveAnchorEdges(existingAnchors[deletedController]);
container.Remove(existingAnchors[deletedController]);
existingAnchors.Remove(deletedController);
}
Profiler.EndSample();
Profiler.BeginSample("VFXNodeUI.SyncAnchors New");
var order = ports.Select((t, i) => new KeyValuePair<VFXDataAnchorController, int>(t, i)).ToDictionary(t => t.Key, t => t.Value);
var newAnchors = ports.Except(existingAnchors.Keys).ToArray();
VFXDataAnchor firstAnchor = null;
foreach (var newController in newAnchors)
{
Profiler.BeginSample("VFXNodeUI.InstantiateDataAnchor");
var newElement = InstantiateDataAnchor(newController, this);
Profiler.EndSample();
(newElement as VFXDataAnchor).controller = newController;
container.Add(newElement);
existingAnchors[newController] = newElement;
if (firstAnchor == null)
firstAnchor = newElement as VFXDataAnchor;
}
if (firstAnchor != null)
firstAnchor.AddToClassList("first");
Profiler.EndSample();
Profiler.BeginSample("VFXNodeUI.SyncAnchors Reorder");
//Reorder anchors.
if (ports.Count > 0)
{
var correctOrder = new VFXDataAnchor[ports.Count];
foreach (var kv in existingAnchors)
{
correctOrder[order[kv.Key]] = kv.Value;
}
correctOrder[0].SendToBack();
correctOrder[0].AddToClassList("first");
for (int i = 1; i < correctOrder.Length; ++i)
{
if (container.ElementAt(i) != correctOrder[i])
correctOrder[i].PlaceInFront(correctOrder[i - 1]);
correctOrder[i].RemoveFromClassList("first");
}
}
Profiler.EndSample();
}
public void ForceUpdate()
{
SelfChange();
}
public void UpdateCollapse()
{
if (superCollapsed)
{
AddToClassList("superCollapsed");
}
else
{
RemoveFromClassList("superCollapsed");
}
}
public void AssetMoved()
{
title = controller.title;
foreach (var setting in m_Settings)
{
setting.UpdateGUI(true);
}
foreach (VFXEditableDataAnchor input in GetPorts(true, false).OfType<VFXEditableDataAnchor>())
{
input.AssetMoved();
}
}
protected virtual void SelfChange()
{
Profiler.BeginSample("VFXNodeUI.SelfChange");
if (controller == null)
return;
title = controller.title;
if (HasPosition())
{
style.position = PositionType.Absolute;
style.left = controller.position.x;
style.top = controller.position.y;
}
base.expanded = controller.expanded;
/*
if (m_CollapseButton != null)
{
m_CollapseButton.SetEnabled(false);
m_CollapseButton.SetEnabled(true);
}*/
SyncSettings();
SyncAnchors();
Profiler.BeginSample("VFXNodeUI.SelfChange The Rest");
RefreshExpandedState();
RefreshLayout();
Profiler.EndSample();
Profiler.EndSample();
UpdateCollapse();
}
public override bool expanded
{
get { return base.expanded; }
set
{
if (base.expanded == value)
return;
base.expanded = value;
controller.expanded = value;
}
}
public virtual VFXDataAnchor InstantiateDataAnchor(VFXDataAnchorController controller, VFXNodeUI node)
{
if (controller.direction == Direction.Input)
{
VFXEditableDataAnchor anchor = VFXEditableDataAnchor.Create(controller, node);
return anchor;
}
else
{
return VFXOutputDataAnchor.Create(controller, node);
}
}
public IEnumerable<VFXDataAnchor> GetPorts(bool input, bool output)
{
if (input)
{
foreach (var child in inputContainer.Children())
{
if (child is VFXDataAnchor)
yield return child as VFXDataAnchor;
}
}
if (output)
{
foreach (var child in outputContainer.Children())
{
if (child is VFXDataAnchor)
yield return child as VFXDataAnchor;
}
}
}
public virtual void GetPreferedSettingsWidths(ref float labelWidth, ref float controlWidth)
{
foreach (var setting in m_Settings)
{
if (setting.parent == null)
continue;
float portLabelWidth = setting.GetPreferredLabelWidth() + 5;
float portControlWidth = setting.GetPreferredControlWidth();
if (labelWidth < portLabelWidth)
{
labelWidth = portLabelWidth;
}
if (controlWidth < portControlWidth)
{
controlWidth = portControlWidth;
}
}
}
public virtual void GetPreferedWidths(ref float labelWidth, ref float controlWidth)
{
}
public virtual void ApplyWidths(float labelWidth, float controlWidth)
{
}
public virtual void ApplySettingsWidths(float labelWidth, float controlWidth)
{
foreach (var setting in m_Settings)
{
setting.SetLabelWidth(labelWidth);
}
}
public const int DefaultLabelWidth = 148;
protected void AddSetting(VFXSettingController setting)
{
var rm = PropertyRM.Create(setting, DefaultLabelWidth);
if (rm != null)
{
m_Settings.Add(rm);
}
else
{
Debug.LogErrorFormat("Cannot create controller for {0}", setting.name);
}
}
public virtual void RefreshLayout()
{
}
public virtual bool superCollapsed
{
get { return controller.superCollapsed; }
}
}
}