VisualElement MakeItem()
        {
            var element = new BuilderExplorerItem();

            element.name = "unity-treeview-item-content";
            element.RegisterCallback <MouseEnterEvent>((e) =>
            {
                ClearHighlightOverlay();

                var explorerItem    = e.target as VisualElement;
                var documentElement = explorerItem?.GetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName) as VisualElement;
                HighlightAllRelatedDocumentElements(documentElement);
            });
            element.RegisterCallback <MouseLeaveEvent>((e) =>
            {
                ClearHighlightOverlay();
            });

            element.RegisterCustomBuilderStyleChangeEvent(elementStyle =>
            {
                var documentElement = element.GetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName) as VisualElement;
                var isValidTarget   = documentElement != null;
                if (!isValidTarget)
                {
                    return;
                }

                var icon = element.Q(null, BuilderConstants.ExplorerItemIconClassName);
                if (icon == null)
                {
                    return;
                }

                var path        = documentElement.GetProperty(BuilderConstants.LibraryItemLinkedTemplateContainerPathVEPropertyName) as string;
                var libraryIcon = BuilderLibraryContent.GetTypeLibraryIcon(documentElement.GetType());
                if (documentElement is TemplateContainer && !string.IsNullOrEmpty(path))
                {
                    libraryIcon = BuilderLibraryContent.GetUXMLAssetIcon(path);
                }
                else if (elementStyle == BuilderElementStyle.Highlighted && !EditorGUIUtility.isProSkin)
                {
                    libraryIcon = BuilderLibraryContent.GetTypeDarkSkinLibraryIcon(documentElement.GetType());
                }

                var styleBackgroundImage   = icon.style.backgroundImage;
                styleBackgroundImage.value = new Background {
                    texture = libraryIcon
                };
                icon.style.backgroundImage = styleBackgroundImage;
            });

            return(element);
        }
        static void AddCategoriesToStack(BuilderLibraryItem sourceCategory, List <BuilderLibraryItem> categoryStack, string[] split)
        {
            if (categoryStack.Count > split.Length)
            {
                categoryStack.RemoveRange(split.Length, categoryStack.Count - split.Length);
            }

            string fullName = string.Empty;

            for (int i = 0; i < split.Length; ++i)
            {
                var part = split[i];
                fullName += part;
                if (categoryStack.Count > i)
                {
                    var data = categoryStack[i].data;
                    if (data.name == part)
                    {
                        continue;
                    }
                    else if (data.name != part)
                    {
                        categoryStack.RemoveRange(i, categoryStack.Count - i);
                    }
                }

                if (categoryStack.Count <= i)
                {
                    var newCategory = BuilderLibraryContent.CreateItem(part,
                                                                       null, null, null,
                                                                       null, null,
                                                                       null, fullName.GetHashCode());

                    if (categoryStack.Count == 0)
                    {
                        sourceCategory.AddChild(newCategory);
                    }
                    else
                    {
                        categoryStack[i - 1].AddChild(newCategory);
                    }

                    categoryStack.Add(newCategory);
                }
            }
        }
        private void SetTypeAndIcon()
        {
            string typeName;

            if (currentVisualElement.typeName == nameof(TemplateContainer))
            {
                typeName = BuilderConstants.BuilderInspectorTemplateInstance;
                m_Icon.style.backgroundImage = EditorGUIUtility.Load("VisualTreeAsset Icon") as Texture2D;
            }
            else
            {
                typeName = currentVisualElement.typeName;
                m_Icon.style.backgroundImage =
                    BuilderLibraryContent.GetTypeLibraryLargeIcon(currentVisualElement.GetType());
            }

            (m_TextField.labelElement as INotifyValueChanged <string>).SetValueWithoutNotify(typeName);
        }
Пример #4
0
        public BuilderLibrary(
            BuilderPaneWindow paneWindow, BuilderViewport viewport,
            BuilderSelection selection, BuilderLibraryDragger dragger,
            BuilderTooltipPreview tooltipPreview)
        {
            m_PaneWindow      = paneWindow;
            m_DocumentElement = viewport.documentRootElement;
            m_Selection       = selection;
            m_Dragger         = dragger;
            m_TooltipPreview  = tooltipPreview;

            viewDataKey = "unity-ui-builder-library";

            // Load styles.
            AddToClassList(k_UssClassName);
            styleSheets.Add(BuilderPackageUtilities.LoadAssetAtPath <StyleSheet>(BuilderConstants.LibraryUssPathNoExt + ".uss"));
            styleSheets.Add(EditorGUIUtility.isProSkin
                ? BuilderPackageUtilities.LoadAssetAtPath <StyleSheet>(BuilderConstants.LibraryUssPathNoExt + "Dark.uss")
                : BuilderPackageUtilities.LoadAssetAtPath <StyleSheet>(BuilderConstants.LibraryUssPathNoExt + "Light.uss"));

            var template = BuilderPackageUtilities.LoadAssetAtPath <VisualTreeAsset>(BuilderConstants.LibraryUssPathNoExt + ".uxml");

            template.CloneTree(this);

            m_EditorExtensionMode     = paneWindow.document.fileSettings.editorExtensionMode;
            m_LibraryContentContainer = this.Q <VisualElement>(k_ContentContainerName);

            m_HeaderButtonStrip         = this.Q <ToggleButtonStrip>();
            m_HeaderButtonStrip.choices = Enum.GetNames(typeof(BuilderLibraryTab));
            m_HeaderButtonStrip.labels  = new List <string> {
                BuilderConstants.LibraryStandardControlsTabName, BuilderConstants.LibraryProjectTabName
            };
            m_HeaderButtonStrip.RegisterValueChangedCallback(e =>
            {
                SwitchLibraryTab((BuilderLibraryTab)Enum.Parse(typeof(BuilderLibraryTab), e.newValue));
            });

            AddFocusable(m_HeaderButtonStrip);
            BuilderLibraryContent.RegenerateLibraryContent();
            BuilderLibraryContent.OnLibraryContentUpdated += RebuildView;
        }
        void FillItem(VisualElement element, ITreeViewItem item)
        {
            var explorerItem = element as BuilderExplorerItem;

            explorerItem.Clear();

            // Pre-emptive cleanup.
            var row = explorerItem.parent.parent;

            row.RemoveFromClassList(BuilderConstants.ExplorerHeaderRowClassName);
            row.RemoveFromClassList(BuilderConstants.ExplorerItemHiddenClassName);
            row.RemoveFromClassList(BuilderConstants.ExplorerActiveStyleSheetClassName);

            // Get target element (in the document).
            var documentElement = (item as TreeViewItem <VisualElement>).data;

            documentElement.SetProperty(BuilderConstants.ElementLinkedExplorerItemVEPropertyName, explorerItem);
            explorerItem.SetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName, documentElement);
            row.userData = documentElement;

            // If we have a FillItem callback (override), we call it and stop creating the rest of the item.
            var fillItemCallback =
                documentElement.GetProperty(BuilderConstants.ExplorerItemFillItemCallbackVEPropertyName) as Action <VisualElement, ITreeViewItem, BuilderSelection>;

            if (fillItemCallback != null)
            {
                fillItemCallback(explorerItem, item, m_Selection);
                return;
            }

            // Create main label container.
            var labelCont = new VisualElement();

            labelCont.AddToClassList(BuilderConstants.ExplorerItemLabelContClassName);
            explorerItem.Add(labelCont);

            if (BuilderSharedStyles.IsStyleSheetElement(documentElement))
            {
                var styleSheetAsset     = documentElement.GetStyleSheet();
                var styleSheetFileName  = AssetDatabase.GetAssetPath(styleSheetAsset);
                var styleSheetAssetName = BuilderAssetUtilities.GetStyleSheetAssetName(styleSheetAsset, hasUnsavedChanges);
                var ssLabel             = new Label(styleSheetAssetName);
                ssLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                ssLabel.AddToClassList("unity-debugger-tree-item-type");
                row.AddToClassList(BuilderConstants.ExplorerHeaderRowClassName);
                labelCont.Add(ssLabel);

                // Register right-click events for context menu actions.
                m_ContextMenuManipulator.RegisterCallbacksOnTarget(explorerItem);

                // Register drag-and-drop events for reparenting.
                m_ExplorerDragger.RegisterCallbacksOnTarget(explorerItem);

                // Allow reparenting.
                explorerItem.SetProperty(BuilderConstants.ExplorerItemElementLinkVEPropertyName, documentElement);

                if (styleSheetAsset == m_PaneWindow.document.activeStyleSheet)
                {
                    row.AddToClassList(BuilderConstants.ExplorerActiveStyleSheetClassName);
                }

                return;
            }
            else if (BuilderSharedStyles.IsSelectorElement(documentElement))
            {
                var selectorParts = BuilderSharedStyles.GetSelectorParts(documentElement);

                // Register right-click events for context menu actions.
                m_ContextMenuManipulator.RegisterCallbacksOnTarget(explorerItem);

                // Register drag-and-drop events for reparenting.
                m_ExplorerDragger.RegisterCallbacksOnTarget(explorerItem);

                foreach (var partStr in selectorParts)
                {
                    if (partStr.StartsWith(BuilderConstants.UssSelectorClassNameSymbol))
                    {
                        m_ClassPillTemplate.CloneTree(labelCont);
                        var pill      = labelCont.contentContainer.ElementAt(labelCont.childCount - 1);
                        var pillLabel = pill.Q <Label>("class-name-label");
                        pill.name = "unity-builder-tree-class-pill";
                        pill.AddToClassList("unity-debugger-tree-item-pill");
                        pill.SetProperty(BuilderConstants.ExplorerStyleClassPillClassNameVEPropertyName, partStr);
                        pill.userData = documentElement;

                        // Add ellipsis if the class name is too long.
                        var partStrShortened = BuilderNameUtilities.CapStringLengthAndAddEllipsis(partStr, BuilderConstants.ClassNameInPillMaxLength);
                        pillLabel.text = partStrShortened;

                        m_ClassDragger.RegisterCallbacksOnTarget(pill);
                    }
                    else if (partStr.StartsWith(BuilderConstants.UssSelectorNameSymbol))
                    {
                        var selectorPartLabel = new Label(partStr);
                        selectorPartLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        selectorPartLabel.AddToClassList(BuilderConstants.ElementNameClassName);
                        labelCont.Add(selectorPartLabel);
                    }
                    else if (partStr.StartsWith(BuilderConstants.UssSelectorPseudoStateSymbol))
                    {
                        var selectorPartLabel = new Label(partStr);
                        selectorPartLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        selectorPartLabel.AddToClassList(BuilderConstants.ElementPseudoStateClassName);
                        labelCont.Add(selectorPartLabel);
                    }
                    else if (partStr == BuilderConstants.SingleSpace)
                    {
                        var selectorPartLabel = new Label(BuilderConstants.TripleSpace);
                        selectorPartLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        selectorPartLabel.AddToClassList(BuilderConstants.ElementTypeClassName);
                        labelCont.Add(selectorPartLabel);
                    }
                    else
                    {
                        var selectorPartLabel = new Label(partStr);
                        selectorPartLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        selectorPartLabel.AddToClassList(BuilderConstants.ElementTypeClassName);
                        labelCont.Add(selectorPartLabel);
                    }
                }

                // Allow reparenting.
                explorerItem.SetProperty(BuilderConstants.ExplorerItemElementLinkVEPropertyName, documentElement);

                return;
            }

            if (BuilderSharedStyles.IsDocumentElement(documentElement))
            {
                var uxmlAsset = documentElement.GetVisualTreeAsset();
                var ssLabel   = new Label(BuilderAssetUtilities.GetVisualTreeAssetAssetName(uxmlAsset, hasUnsavedChanges));
                ssLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                ssLabel.AddToClassList("unity-debugger-tree-item-type");
                row.AddToClassList(BuilderConstants.ExplorerHeaderRowClassName);
                labelCont.Add(ssLabel);
                return;
            }

            // Check if element is inside current document.
            if (!documentElement.IsPartOfCurrentDocument())
            {
                row.AddToClassList(BuilderConstants.ExplorerItemHiddenClassName);
            }

            // Register drag-and-drop events for reparenting.
            m_ExplorerDragger.RegisterCallbacksOnTarget(explorerItem);

            // Allow reparenting.
            explorerItem.SetProperty(BuilderConstants.ExplorerItemElementLinkVEPropertyName, documentElement);

            // Element type label.
            if (string.IsNullOrEmpty(documentElement.name) ||
                elementInfoVisibilityState.HasFlag(BuilderExplorer.BuilderElementInfoVisibilityState.TypeName))
            {
                var typeLabel = new Label(documentElement.typeName);
                typeLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                typeLabel.AddToClassList(BuilderConstants.ElementTypeClassName);
                labelCont.Add(typeLabel);
            }

            // Element name label.
            var nameLabel = new Label();

            nameLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
            nameLabel.AddToClassList("unity-debugger-tree-item-name-label");
            nameLabel.AddToClassList(BuilderConstants.ExplorerItemNameLabelClassName);
            nameLabel.AddToClassList(BuilderConstants.ElementNameClassName);
            if (!string.IsNullOrEmpty(documentElement.name))
            {
                nameLabel.text = BuilderConstants.UssSelectorNameSymbol + documentElement.name;
            }
            labelCont.Add(nameLabel);

            // Textfield to rename element in hierarchy.
            var renameTextfield = explorerItem.CreateRenamingTextField(documentElement, nameLabel, m_Selection);

            labelCont.Add(renameTextfield);

            // Add class list.
            if (documentElement.classList.Count > 0 && elementInfoVisibilityState.HasFlag(BuilderExplorer.BuilderElementInfoVisibilityState.ClassList))
            {
                foreach (var ussClass in documentElement.GetClasses())
                {
                    var classLabelCont = new VisualElement();
                    classLabelCont.AddToClassList(BuilderConstants.ExplorerItemLabelContClassName);
                    explorerItem.Add(classLabelCont);

                    var classLabel = new Label(BuilderConstants.UssSelectorClassNameSymbol + ussClass);
                    classLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                    classLabel.AddToClassList(BuilderConstants.ElementClassNameClassName);
                    classLabel.AddToClassList("unity-debugger-tree-item-classlist-label");
                    classLabelCont.Add(classLabel);
                }
            }

            // Show name of uxml file if this element is a TemplateContainer.
            var       path = documentElement.GetProperty(BuilderConstants.LibraryItemLinkedTemplateContainerPathVEPropertyName) as string;
            Texture2D itemIcon;

            if (documentElement is TemplateContainer && !string.IsNullOrEmpty(path))
            {
                var pathStr = Path.GetFileName(path);
                var label   = new Label(pathStr);
                label.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                label.AddToClassList(BuilderConstants.ElementTypeClassName);
                label.AddToClassList("unity-builder-explorer-tree-item-template-path"); // Just make it look a bit shaded.
                labelCont.Add(label);
                itemIcon = BuilderLibraryContent.GetUXMLAssetIcon(path);
            }
            else
            {
                itemIcon = BuilderLibraryContent.GetTypeLibraryIcon(documentElement.GetType());
            }

            // Element icon.
            var icon = new VisualElement();

            icon.AddToClassList(BuilderConstants.ExplorerItemIconClassName);
            var styleBackgroundImage = icon.style.backgroundImage;

            styleBackgroundImage.value = new Background {
                texture = itemIcon
            };
            icon.style.backgroundImage = styleBackgroundImage;
            labelCont.Insert(0, icon);

            // Register right-click events for context menu actions.
            m_ContextMenuManipulator.RegisterCallbacksOnTarget(explorerItem);
        }
        public void ImportFactoriesFromSource(BuilderLibraryItem sourceCategory)
        {
            var deferredFactories      = new List <IUxmlFactory>();
            var processingData         = new FactoryProcessingHelper();
            var emptyNamespaceControls = new List <TreeViewItem>();

            foreach (var factories in VisualElementFactoryRegistry.factories)
            {
                if (factories.Value.Count == 0)
                {
                    continue;
                }

                var factory = factories.Value[0];
                if (!ProcessFactory(factory, processingData))
                {
                    // Could not process the factory now, because it depends on a yet unprocessed factory.
                    // Defer its processing.
                    deferredFactories.Add(factory);
                }
            }

            List <IUxmlFactory> deferredFactoriesCopy;

            do
            {
                deferredFactoriesCopy = new List <IUxmlFactory>(deferredFactories);
                foreach (var factory in deferredFactoriesCopy)
                {
                    deferredFactories.Remove(factory);
                    if (!ProcessFactory(factory, processingData))
                    {
                        // Could not process the factory now, because it depends on a yet unprocessed factory.
                        // Defer its processing again.
                        deferredFactories.Add(factory);
                    }
                }
            }while (deferredFactoriesCopy.Count > deferredFactories.Count);

            if (deferredFactories.Count > 0)
            {
                Debug.Log("Some factories could not be processed because their base type is missing.");
            }

            var categoryStack = new List <BuilderLibraryItem>();

            foreach (var known in processingData.knownTypes.Values)
            {
                var split = known.uxmlNamespace.Split('.');
                if (split.Length == 0)
                {
                    continue;
                }

                // Avoid adding our own internal factories (like Package Manager templates).
                if (!Unsupported.IsDeveloperMode() && split.Length > 0 && s_NameSpacesToAvoid.Contains(split[0]))
                {
                    continue;
                }

                // Avoid adding UI Builder's own types, even in internal mode.
                if (split.Length >= 3 && split[0] == "Unity" && split[1] == "UI" && split[2] == "Builder")
                {
                    continue;
                }

                var asset     = new VisualElementAsset(known.uxmlQualifiedName);
                var slots     = new Dictionary <string, VisualElement>();
                var overrides = new List <TemplateAsset.AttributeOverride>();
                var vta       = ScriptableObject.CreateInstance <VisualTreeAsset>();
                var context   = new CreationContext(slots, overrides, vta, null);

                Type elementType = null;
                var  factoryType = known.GetType();
                while (factoryType != null && elementType == null)
                {
                    if (factoryType.IsGenericType && factoryType.GetGenericTypeDefinition() == typeof(UxmlFactory <,>))
                    {
                        elementType = factoryType.GetGenericArguments()[0];
                    }
                    else
                    {
                        factoryType = factoryType.BaseType;
                    }
                }

                var newItem = BuilderLibraryContent.CreateItem(
                    known.uxmlName, elementType == typeof(TemplateContainer) ? nameof(TemplateContainer) : "CustomCSharpElement", elementType, () => known.Create(asset, context));
                newItem.data.hasPreview = true;

                if (string.IsNullOrEmpty(split[0]))
                {
                    emptyNamespaceControls.Add(newItem);
                }
                else
                {
                    AddCategoriesToStack(sourceCategory, categoryStack, split);
                    if (categoryStack.Count == 0)
                    {
                        sourceCategory.AddChild(newItem);
                    }
                    else
                    {
                        categoryStack.Last().AddChild(newItem);
                    }
                }
            }

            sourceCategory.AddChildren(emptyNamespaceControls);
        }
        public void ImportUxmlFromProject(BuilderLibraryItem projectCategory, bool includePackages)
        {
            var categoryStack = new List <BuilderLibraryItem>();
            var assets        = AssetDatabase.FindAllAssets(m_SearchFilter);

            foreach (var asset in assets)
            {
                var assetPath  = AssetDatabase.GetAssetPath(asset.instanceID);
                var prettyPath = assetPath;
                prettyPath = Path.GetDirectoryName(prettyPath);
                prettyPath = prettyPath.ConvertSeparatorsToUnity();
                if (prettyPath.StartsWith("Packages/") && !includePackages)
                {
                    continue;
                }

                if (prettyPath.StartsWith(BuilderConstants.UIBuilderPackageRootPath))
                {
                    continue;
                }

                // Check to make sure the asset is actually writable.
                var packageInfo = PackageInfo.FindForAssetPath(assetPath);
                if (packageInfo != null && packageInfo.source != PackageSource.Embedded && packageInfo.source != PackageSource.Local)
                {
                    continue;
                }

                // Anoter way to check the above. Leaving it here for references in case the above stops working.
                //AssetDatabase.GetAssetFolderInfo(assetPath, out bool isRoot, out bool isImmutable);
                //if (isImmutable)
                //continue;

                var split = prettyPath.Split('/');
                AddCategoriesToStack(projectCategory, categoryStack, split);

                var vta     = asset.pptrValue as VisualTreeAsset;
                var newItem = BuilderLibraryContent.CreateItem(asset.name + ".uxml", nameof(TemplateContainer), typeof(TemplateContainer),
                                                               () =>
                {
                    if (vta == null)
                    {
                        return(null);
                    }

                    var tree = vta.CloneTree();
                    tree.SetProperty(BuilderConstants.LibraryItemLinkedTemplateContainerPathVEPropertyName, assetPath);
                    tree.name = vta.name;
                    return(tree);
                },
                                                               (inVta, inParent, ve) =>
                {
                    var vea = inVta.AddTemplateInstance(inParent, assetPath) as VisualElementAsset;
                    vea.AddProperty("name", vta.name);
                    ve.SetProperty(BuilderConstants.ElementLinkedInstancedVisualTreeAssetVEPropertyName, vta);
                    return(vea);
                },
                                                               null, vta);

                var data = newItem.data;
                if (data.icon == null)
                {
                    data.SetIcon((Texture2D)EditorGUIUtility.IconContent("VisualTreeAsset Icon").image);
                }
                data.hasPreview = true;

                if (categoryStack.Count == 0)
                {
                    projectCategory.AddChild(newItem);
                }
                else
                {
                    categoryStack.Last().AddChild(newItem);
                }
            }
        }