using System; using System.Linq; using System.Collections.Generic; using UnityEditor.Experimental; using UnityEditor.Experimental.GraphView; using UnityEditor.VFX.Block; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.VFX; using UnityEngine.UIElements; using PositionType = UnityEngine.UIElements.Position; namespace UnityEditor.VFX.UI { interface IParameterItem { VFXGraph graph { get; set; } string title { get; set; } // This property is used to add an extra margin below the last item (done in css) bool isLast { get; set; } bool isExpanded { get; set; } int index { get; set; } int id { get; set; } bool canRename { get; } ISelectable selectable { get; set; } bool Accept(IParameterItem item); } interface IParameterCategory { bool isRoot { get; } } class ParameterItem : IParameterItem { protected ParameterItem(string title, int id, bool isExpanded) { this.title = title; this.id = id; this.isExpanded = isExpanded; } public VFXGraph graph { get; set; } public virtual string title { get; set; } public bool isLast { get; set; } public bool isExpanded { get; set; } public int index { get; set; } public int id { get; set; } public virtual bool canRename => true; public ISelectable selectable { get; set; } public virtual bool Accept(IParameterItem item) => false; } class PropertyCategory : ParameterItem, IParameterCategory { public PropertyCategory(string title, int id, bool isRoot, bool isExpanded) : base(title, id, isExpanded) { this.isRoot = isRoot; } public bool isRoot { get; } public override bool canRename => !isRoot; public override bool Accept(IParameterItem item) { return item is PropertyItem; } } class PropertyItem : ParameterItem { public PropertyItem(VFXParameterController controller, int id) : base(null, id, false) { this.controller = controller; } public VFXParameterController controller { get; } public override string title => controller.exposedName; } class OutputCategory : PropertyCategory { public const string Label = "Output"; public OutputCategory(bool isExpanded, int id) : base(Label, id, false, isExpanded) { } } class AttributeItem : ParameterItem { public AttributeItem(string name, CustomAttributeUtility.Signature type, int id, string description, bool isExpanded, bool isEditable, IEnumerable subgraphUse) : base(name, id, false) { this.title = name; this.type = type; this.description = description; this.isEditable = isEditable; this.subgraphUse = subgraphUse?.ToArray(); this.isBuiltIn = VFXAttributesManager.ExistsBuiltInOnly(name); this.isReadOnly = Array.FindIndex(VFXAttributesManager.GetBuiltInNamesAndCombination(false, false, true, false).ToArray(), x => x == name) != -1; this.isExpanded = isExpanded; } public CustomAttributeUtility.Signature type { get; set; } public bool isEditable { get; } public string[] subgraphUse { get; } public bool isBuiltIn { get; } public bool isReadOnly { get; } public string description { get; set; } public override bool canRename => !isBuiltIn && isEditable; } class AttributeCategory : ParameterItem, IParameterCategory { public AttributeCategory(string title, int id, bool isRoot, bool isExpanded) : base(title, id, isExpanded) { this.isRoot = isRoot; } public bool isRoot { get; } public override bool canRename => false; } class CustomAttributeCategory : AttributeCategory { public const string Title = "Custom Attributes"; public CustomAttributeCategory(int id, bool isExpanded) : base("Custom Attributes", id, false, isExpanded) { } } class VFXBlackboard : Blackboard, IVFXMovable, IControlledElement { [Flags] enum ViewMode { Properties = 0x1, Attributes = 0x2, All = Properties | Attributes, } const string PropertiesCategoryTitle = "Properties"; const string BuiltInAttributesCategoryTitle = "Built-in Attributes"; const string AttributesCategoryTitle = "Attributes"; static readonly Rect defaultRect = new Rect(100, 100, 300, 500); static System.Reflection.PropertyInfo s_LayoutManual = typeof(VisualElement).GetProperty("isLayoutManual", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); readonly VFXView m_View; readonly Button m_AddButton; readonly List> m_ParametersController = new (); VFXViewController m_Controller; Image m_VCSStatusImage; TreeView m_Treeview; Label m_PathLabel; TextField m_PathTextField; bool m_CanEdit; bool m_IsChangingSelection; ViewMode m_ViewMode; List m_pendingSelectionItems = new (); Controller IControlledElement.controller => m_Controller; public VFXViewController controller { get => m_Controller; set { if (m_Controller != value) { m_pendingSelectionItems.Clear(); if (m_Controller != null) { m_Controller.UnregisterHandler(this); } Clear(); m_Controller = value; if (m_Controller != null) { m_Controller.RegisterHandler(this); Update(true); } m_AddButton.SetEnabled(m_Controller != null); } } } private bool isPropertiesCategoryExpanded { get => EditorPrefs.GetBool("VFXBlackboard.isPropertiesCategoryExpanded"); set => EditorPrefs.SetBool("VFXBlackboard.isPropertiesCategoryExpanded", value); } private bool isOutputCategoryExpanded { get => EditorPrefs.GetBool("VFXBlackboard.isOutputCategoryExpanded"); set => EditorPrefs.SetBool("VFXBlackboard.isOutputCategoryExpanded", value); } private bool isAttributesCategoryExpanded { get => EditorPrefs.GetBool("VFXBlackboard.isAttributesCategoryExpanded"); set => EditorPrefs.SetBool("VFXBlackboard.isAttributesCategoryExpanded", value); } private bool isBuiltInAttributesCategoryExpanded { get => EditorPrefs.GetBool("VFXBlackboard.isBuiltInAttributesCategoryExpanded"); set => EditorPrefs.SetBool("VFXBlackboard.isBuiltInAttributesCategoryExpanded", value); } private bool isCustomAttributesCategoryExpanded { get => EditorPrefs.GetBool("VFXBlackboard.isCustomAttributesCategoryExpanded"); set => EditorPrefs.SetBool("VFXBlackboard.isCustomAttributesCategoryExpanded", value); } new void Clear() { m_ParametersController.Clear(); m_Treeview.SetRootItems(m_ParametersController); m_Treeview.Rebuild(); } public VFXBlackboard(VFXView view) { m_View = view; m_ViewMode = ViewMode.All; addItemRequested = OnAddItemButton; this.scrollable = false; SetPosition(BoardPreferenceHelper.LoadPosition(BoardPreferenceHelper.Board.blackboard, defaultRect)); m_Treeview = new TreeView { reorderable = true, selectionType = SelectionType.Multiple, virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight }; m_Treeview.canStartDrag += OnCanDragStart; m_Treeview.dragAndDropUpdate += OnDragAndDropUpdate; m_Treeview.handleDrop += OnHandleDrop; m_Treeview.setupDragAndDrop += OnSetupDragAndDrop; m_Treeview.makeItem += MakeItem; m_Treeview.bindItem += BindItem; m_Treeview.unbindItem += UnbindItem; m_Treeview.selectionChanged += OnSelectionChanged; // Trickle down because on macOS the context menu is opened on PointerDown event which stop the event propagation m_Treeview.RegisterCallback(OnTreeViewPointerDown, TrickleDown.TrickleDown); Add(m_Treeview); var tabsContainer = new VisualElement { name = "tabsContainer" }; var allTab = new Toggle { text = "All", value = true }; var propertiesTab = new Toggle { text = "Properties" }; var attributesTab = new Toggle { text = "Attributes" }; tabsContainer.Add(new VisualElement { name = "bottomBorder" }); tabsContainer.Add(allTab); tabsContainer.Add(propertiesTab); tabsContainer.Add(attributesTab); tabsContainer.Add(new VisualElement { name = "spacer" }); allTab.RegisterCallback, ViewMode>(OnTabChanged, ViewMode.All); propertiesTab.RegisterCallback, ViewMode>(OnTabChanged, ViewMode.Properties); attributesTab.RegisterCallback, ViewMode>(OnTabChanged, ViewMode.Attributes); Insert(0, tabsContainer); styleSheets.Add(VFXView.LoadStyleSheet("VFXBlackboard")); RegisterCallback(OnGetFocus); RegisterCallback(OnKeyDown); focusable = true; m_AddButton = this.Q