using System; using System.Collections.Generic; using System.Linq; using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEngine.VFX; using UnityEditor.VFX; using UnityEngine.UIElements; using UnityEngine.Profiling; using System.Reflection; using PositionType = UnityEngine.UIElements.Position; namespace UnityEditor.VFX.UI { class VFXContextUI : VFXNodeUI { // TODO: Unused except for debugging readonly CustomStyleProperty RectColorProperty = new CustomStyleProperty("--rect-color"); Image m_HeaderIcon; Image m_HeaderSpace; VisualElement m_Footer; Image m_FooterIcon; Label m_FooterTitle; VisualElement m_FlowInputConnectorContainer; VisualElement m_FlowOutputConnectorContainer; VisualElement m_BlockContainer; VisualElement m_NoBlock; VisualElement m_DragDisplay; Label m_Label; TextField m_TextField; public new VFXContextController controller { get { return base.controller as VFXContextController; } } protected override void OnNewController() { var blocks = new List>(VFXLibrary.GetBlocks()); m_CanHaveBlocks = blocks.Any(t => controller.model.AcceptChild(t.model)); } public bool canHaveBlocks { get => m_CanHaveBlocks; } public static string ContextEnumToClassName(string name) { if (name[0] == 'k') { Debug.LogError("Fix this since k should have been removed from enums"); } return name.ToLower(); } public void UpdateLabel() { var graph = controller.model.GetGraph(); if (graph != null && controller.model.contextType == VFXContextType.Spawner) m_Label.text = graph.systemNames.GetUniqueSystemName(controller.model); else m_Label.text = controller.model.label; } protected override void SelfChange() { base.SelfChange(); Profiler.BeginSample("VFXContextUI.CreateBlockProvider"); if (m_BlockProvider == null) { m_BlockProvider = new VFXBlockProvider(controller, (d, mPos) => { if (d is VFXBlockProvider.NewBlockDescriptor) { UpdateSelectionWithNewBlocks(); AddBlock(mPos, (d as VFXBlockProvider.NewBlockDescriptor).newBlock); } else { var subgraphBlock = AssetDatabase.LoadAssetAtPath((d as VFXBlockProvider.SubgraphBlockDescriptor).item.path); int blockIndex = GetDragBlockIndex(mPos); VFXBlock newModel = ScriptableObject.CreateInstance(); newModel.SetSettingValue("m_Subgraph", subgraphBlock); UpdateSelectionWithNewBlocks(); using (var growContext = new GrowContext(this)) { controller.AddBlock(blockIndex, newModel, true); } } }); } Profiler.EndSample(); if (inputContainer.childCount == 0 && !hasSettings) { mainContainer.AddToClassList("empty"); } else { mainContainer.RemoveFromClassList("empty"); } m_Divider.visible = hasSettings; m_HeaderIcon.image = GetIconForVFXType(controller.model.inputType); m_HeaderIcon.visible = m_HeaderIcon.image != null; Profiler.BeginSample("VFXContextUI.SetAllStyleClasses"); VFXContextType contextType = controller.model.contextType; foreach (VFXContextType value in System.Enum.GetValues(typeof(VFXContextType))) { if (value != contextType) RemoveFromClassList(ContextEnumToClassName(value.ToString())); } AddToClassList(ContextEnumToClassName(contextType.ToString())); var inputType = controller.model.inputType; if (inputType == VFXDataType.None) { inputType = controller.model.ownedType; } foreach (VFXDataType value in System.Enum.GetValues(typeof(VFXDataType))) { if (inputType != value) RemoveFromClassList("inputType" + ContextEnumToClassName(value.ToString())); } AddToClassList("inputType" + ContextEnumToClassName(inputType.ToString())); var outputType = controller.model.outputType; foreach (VFXDataType value in System.Enum.GetValues(typeof(VFXDataType))) { if (value != outputType) RemoveFromClassList("outputType" + ContextEnumToClassName(value.ToString())); } AddToClassList("outputType" + ContextEnumToClassName(outputType.ToString())); var type = controller.model.ownedType; foreach (VFXDataType value in System.Enum.GetValues(typeof(VFXDataType))) { if (value != type) RemoveFromClassList("type" + ContextEnumToClassName(value.ToString())); } AddToClassList("type" + ContextEnumToClassName(type.ToString())); var space = controller.model.space; foreach (VFXCoordinateSpace val in System.Enum.GetValues(typeof(VFXCoordinateSpace))) { if (val != space || !controller.model.spaceable) m_HeaderSpace.RemoveFromClassList("space" + val.ToString()); } if (controller.model.spaceable) m_HeaderSpace.AddToClassList("space" + (controller.model.space).ToString()); Profiler.EndSample(); if (controller.model.outputType == VFXDataType.None) { if (m_Footer.parent != null) m_Footer.RemoveFromHierarchy(); } else { if (m_Footer.parent == null) mainContainer.Add(m_Footer); if (controller.model.outputFlowSlot.Any()) { m_FooterTitle.text = controller.model.outputType.ToString(); m_FooterIcon.image = GetIconForVFXType(controller.model.outputType); } else { m_FooterTitle.text = string.Empty; m_FooterIcon.image = null; } m_FooterIcon.visible = m_FooterIcon.image != null; } Profiler.BeginSample("VFXContextUI.CreateInputFlow"); HashSet newInAnchors = new HashSet(); foreach (var inanchorcontroller in controller.flowInputAnchors) { var existing = m_FlowInputConnectorContainer.Children().Select(t => t as VFXFlowAnchor).FirstOrDefault(t => t.controller == inanchorcontroller); if (existing == null) { var anchor = VFXFlowAnchor.Create(inanchorcontroller); m_FlowInputConnectorContainer.Add(anchor); newInAnchors.Add(anchor); } else { newInAnchors.Add(existing); } } foreach (var nonLongerExistingAnchor in m_FlowInputConnectorContainer.Children().Where(t => !newInAnchors.Contains(t)).ToList()) // ToList to make a copy because the enumerable will change when we delete { m_FlowInputConnectorContainer.Remove(nonLongerExistingAnchor); } Profiler.EndSample(); Profiler.BeginSample("VFXContextUI.CreateInputFlow"); HashSet newOutAnchors = new HashSet(); foreach (var outanchorcontroller in controller.flowOutputAnchors) { var existing = m_FlowOutputConnectorContainer.Children().Select(t => t as VFXFlowAnchor).FirstOrDefault(t => t.controller == outanchorcontroller); if (existing == null) { var anchor = VFXFlowAnchor.Create(outanchorcontroller); m_FlowOutputConnectorContainer.Add(anchor); newOutAnchors.Add(anchor); } else { newOutAnchors.Add(existing); } } foreach (var nonLongerExistingAnchor in m_FlowOutputConnectorContainer.Children().Where(t => !newOutAnchors.Contains(t)).ToList()) // ToList to make a copy because the enumerable will change when we delete { m_FlowOutputConnectorContainer.Remove(nonLongerExistingAnchor); } Profiler.EndSample(); UpdateLabel(); if (string.IsNullOrEmpty(m_Label.text)) { m_Label.AddToClassList("empty"); } else { m_Label.RemoveFromClassList("empty"); } foreach (var inEdge in m_FlowInputConnectorContainer.Children().OfType().SelectMany(t => t.connections)) inEdge.UpdateEdgeControl(); foreach (var outEdge in m_FlowOutputConnectorContainer.Children().OfType().SelectMany(t => t.connections)) outEdge.UpdateEdgeControl(); RefreshContext(); } VisualElement m_Divider; public VFXContextUI() : base("uxml/VFXContext") { capabilities |= Capabilities.Selectable | Capabilities.Movable | Capabilities.Deletable | Capabilities.Ascendable; styleSheets.Add(VFXView.LoadStyleSheet("VFXContext")); styleSheets.Add(VFXView.LoadStyleSheet("Selectable")); AddToClassList("VFXContext"); AddToClassList("selectable"); this.mainContainer.style.overflow = Overflow.Visible; m_Divider = this.mainContainer.Q("divider"); m_FlowInputConnectorContainer = this.Q("flow-inputs"); m_FlowOutputConnectorContainer = this.Q("flow-outputs"); m_HeaderIcon = titleContainer.Q("icon"); m_HeaderSpace = titleContainer.Q("header-space"); m_HeaderSpace.AddManipulator(new Clickable(OnSpace)); m_BlockContainer = this.Q("block-container"); m_NoBlock = m_BlockContainer.Q("no-blocks"); m_Footer = this.Q("footer"); m_FooterTitle = m_Footer.Q