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, 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(); if (IsSelected(gv)) { if (e.actionKey) { Unselect(gv); } } else { Select(gv, e.actionKey); } } public VisualElement settingsContainer {get; private set; } private List m_Settings = new List(); static string UXMLResourceToPackage(string resourcePath) { return VisualEffectGraphPackageInfo.assetPackagePath + "/Editor/Resources/" + resourcePath + ".uxml"; } public VFXNodeUI(string template) : base(UXMLResourceToPackage(template)) { Initialize(); } void OnFocusIn(FocusInEvent e) { var gv = GetFirstAncestorOfType(); 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.borderBottom = m_SelectionBorder.style.borderTop = m_SelectionBorder.style.borderLeft = m_SelectionBorder.style.borderRight = (m_Selected ? 1 : (m_Hovered ? 1 : 0));*/ m_SelectionBorder.style.borderColor = 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"); cacheAsBitmap = true; RegisterCallback(OnMouseEnter); RegisterCallback(OnMouseLeave); RegisterCallback(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 ports, VisualElement container) { var existingAnchors = container.Children().Cast().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().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(t, i)).ToDictionary(t => t.Key, t => t.Value); var newAnchors = ports.Except(existingAnchors.Keys).ToArray(); 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; } 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()) { 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 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); } } protected void AddSetting(VFXSettingController setting) { var rm = PropertyRM.Create(setting, 100); 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; } } } }