public BuilderViewport(BuilderPaneWindow paneWindow, BuilderSelection selection, BuilderElementContextMenu contextMenuManipulator)
        {
            m_PaneWindow             = paneWindow;
            m_Selection              = selection;
            m_ContextMenuManipulator = contextMenuManipulator;

            AddToClassList("unity-builder-viewport");

            var template = AssetDatabase.LoadAssetAtPath <VisualTreeAsset>(BuilderConstants.UIBuilderPackagePath + "/BuilderViewport.uxml");

            template.CloneTree(this);

            m_Toolbar             = this.Q("toolbar");
            m_ViewportWrapper     = this.Q("viewport-wrapper");
            m_Viewport            = this.Q("viewport");
            m_Surface             = this.Q("viewport-surface");
            m_Surface.pickingMode = PickingMode.Ignore;
            m_Canvas          = this.Q <BuilderCanvas>("canvas");
            m_Canvas.document = paneWindow.document;
            m_SharedStylesAndDocumentElement = this.Q("shared-styles-and-document");
            m_StyleSelectorElementContainer  = this.Q(BuilderConstants.StyleSelectorElementContainerName);
            m_DocumentElement        = this.Q("document");
            m_Canvas.documentElement = m_DocumentElement;
            m_PickOverlay            = this.Q("pick-overlay");
            m_HighlightOverlay       = this.Q("highlight-overlay");
            m_BuilderParentTracker   = this.Q <BuilderParentTracker>("parent-tracker");
            m_BuilderResizer         = this.Q <BuilderResizer>("resizer");
            m_BuilderMover           = this.Q <BuilderMover>("mover");
            m_BuilderAnchorer        = this.Q <BuilderAnchorer>("anchorer");
            m_BuilderZoomer          = new BuilderZoomer(this);
            m_BuilderPanner          = new BuilderPanner(this);

            m_BuilderMover.parentTracker = m_BuilderParentTracker;

            m_PickOverlay.RegisterCallback <MouseDownEvent>(OnPick);
            m_PickOverlay.RegisterCallback <MouseMoveEvent>(OnHover);
            m_PickOverlay.RegisterCallback <MouseLeaveEvent>(OnMouseLeave);
            m_Viewport.RegisterCallback <MouseDownEvent>(OnMissPick);
            m_Viewport.RegisterCallback <GeometryChangedEvent>(OnGeometryChanged);

            m_ContextMenuManipulator?.RegisterCallbacksOnTarget(m_Viewport);

            // Make sure this gets focus when the pane gets focused.
            primaryFocusable = this;
            focusable        = true;

            // Restore the zoom scale
            zoomScale     = paneWindow.document.viewportZoomScale;
            contentOffset = paneWindow.document.viewportContentOffset;
        }
        void OnPick(MouseDownEvent evt)
        {
            // Do not prevent zoom and pan
            if (evt.button == 2 || (evt.ctrlKey && evt.altKey || (evt.button == (int)MouseButton.RightMouse && evt.altKey)))
            {
                return;
            }

            var pickedElement = PickElement(evt.mousePosition);

            if (pickedElement != null)
            {
                m_Selection.Select(this, pickedElement);
                SetInnerSelection(pickedElement);

                if (evt.clickCount == 2)
                {
                    var posInViewport = m_PickOverlay.ChangeCoordinatesTo(this, evt.localMousePosition);
                    BuilderInPlaceTextEditingUtilities.OpenEditor(pickedElement, this.ChangeCoordinatesTo(pickedElement, posInViewport));
                }
            }
            else
            {
                ClearInnerSelection();
                m_Selection.ClearSelection(this);
            }

            if (evt.button == (int)MouseButton.RightMouse)
            {
                if (pickedElement != null && m_ContextMenuManipulator != null)
                {
                    pickedElement.SetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName, pickedElement);
                    m_ContextMenuManipulator.RegisterCallbacksOnTarget(pickedElement);
                    m_ContextMenuManipulator.DisplayContextMenu(evt, pickedElement);
                    evt.StopPropagation();
                }
            }
            else
            {
                evt.StopPropagation();
            }
        }
        void OnPick(MouseDownEvent evt)
        {
            // Do not prevent zoom and pan
            if (evt.button == 2 || (evt.ctrlKey && evt.altKey || (evt.button == (int)MouseButton.RightMouse && evt.altKey)))
            {
                return;
            }

            var pickedElement = PickElement(evt.mousePosition);

            if (pickedElement != null)
            {
                SetInnerSelection(pickedElement);
                m_Selection.Select(this, pickedElement);
            }
            else
            {
                ClearInnerSelection();
                m_Selection.ClearSelection(this);
            }

            if (evt.button == (int)MouseButton.RightMouse)
            {
                if (pickedElement != null && m_ContextMenuManipulator != null)
                {
                    pickedElement.SetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName, pickedElement);
                    m_ContextMenuManipulator.RegisterCallbacksOnTarget(pickedElement);
                    m_ContextMenuManipulator.DisplayContextMenu(evt, pickedElement);
                    evt.StopPropagation();
                }
            }
            else
            {
                evt.StopPropagation();
            }
        }
        public BuilderViewport(BuilderPaneWindow paneWindow, BuilderSelection selection, BuilderElementContextMenu contextMenuManipulator)
        {
            m_PaneWindow             = paneWindow;
            m_Selection              = selection;
            m_ContextMenuManipulator = contextMenuManipulator;

            AddToClassList("unity-builder-viewport");

            var template = AssetDatabase.LoadAssetAtPath <VisualTreeAsset>(BuilderConstants.UIBuilderPackagePath + "/BuilderViewport.uxml");

            template.CloneTree(this);

            m_Toolbar             = this.Q("toolbar");
            m_ViewportWrapper     = this.Q("viewport-wrapper");
            m_Viewport            = this.Q("viewport");
            m_Surface             = this.Q("viewport-surface");
            m_Surface.pickingMode = PickingMode.Ignore;
            m_Canvas          = this.Q <BuilderCanvas>("canvas");
            m_Canvas.document = paneWindow.document;
            m_SharedStylesAndDocumentElement = this.Q("shared-styles-and-document");
            m_SharedStylesAndDocumentElement.pseudoStates |= PseudoStates.Root; // To apply variables of the active theme that are defined in the :root selector
            m_StyleSelectorElementContainer = this.Q(BuilderConstants.StyleSelectorElementContainerName);
            m_DocumentElement        = this.Q("document");
            m_Canvas.documentElement = m_DocumentElement;
            m_EditorLayer            = this.Q("__unity-editor-layer");
            m_EditorLayer.AddToClassList(BuilderConstants.HiddenStyleClassName);
            m_TextEditor                = this.Q <TextField>("__unity-text-editor");
            m_Canvas.editorLayer        = m_EditorLayer;
            m_PickOverlay               = this.Q("pick-overlay");
            m_HighlightOverlay          = this.Q("highlight-overlay");
            m_BuilderParentTracker      = this.Q <BuilderParentTracker>("parent-tracker");
            m_BuilderSelectionIndicator = this.Q <BuilderSelectionIndicator>("selection-indicator");
            m_BuilderResizer            = this.Q <BuilderResizer>("resizer");
            m_BuilderMover              = this.Q <BuilderMover>("mover");
            m_BuilderAnchorer           = this.Q <BuilderAnchorer>("anchorer");
            m_BuilderZoomer             = new BuilderZoomer(this);
            m_BuilderPanner             = new BuilderPanner(this);

            m_BuilderMover.parentTracker = m_BuilderParentTracker;

            m_PickOverlay.RegisterCallback <MouseDownEvent>(OnPick);
            m_PickOverlay.RegisterCallback <MouseMoveEvent>(OnHover);
            m_PickOverlay.RegisterCallback <MouseLeaveEvent>(OnMouseLeave);
            m_Viewport.RegisterCallback <MouseDownEvent>(OnMissPick);
            m_Viewport.RegisterCallback <GeometryChangedEvent>(OnGeometryChanged);

            m_Canvas.header.AddManipulator(new Clickable(OnCanvasHeaderClick));
            m_ContextMenuManipulator?.RegisterCallbacksOnTarget(m_Viewport);

            // Make sure this gets focus when the pane gets focused.
            primaryFocusable = this;
            focusable        = true;

            // Restore the zoom scale
            zoomScale     = paneWindow.document.viewportZoomScale;
            contentOffset = paneWindow.document.viewportContentOffset;

            // Repaint bug workaround.
            m_CheckerboardBackground = this.Q <CheckerboardBackground>();
            RegisterCallback <BlurEvent>(e => { m_CheckerboardBackground.MarkDirtyRepaint(); });
            RegisterCallback <FocusEvent>(e => { m_CheckerboardBackground.MarkDirtyRepaint(); });
        }
Exemple #5
0
        public ElementHierarchyView(
            VisualElement documentRootElement,
            BuilderSelection selection,
            BuilderClassDragger classDragger,
            BuilderHierarchyDragger hierarchyDragger,
            BuilderElementContextMenu contextMenuManipulator,
            Action <VisualElement> selectElementCallback,
            HighlightOverlayPainter highlightOverlayPainter)
        {
            m_DocumentRootElement    = documentRootElement;
            m_Selection              = selection;
            m_ClassDragger           = classDragger;
            m_HierarchyDragger       = hierarchyDragger;
            m_ContextMenuManipulator = contextMenuManipulator;

            this.focusable = true;

            m_SelectElementCallback = selectElementCallback;
            hierarchyHasChanged     = true;

            m_SearchResultsHightlights = new List <VisualElement>();

            this.RegisterCallback <FocusEvent>(e => m_TreeView?.Focus());

            // HACK: ListView/TreeView need to clear their selections when clicking on nothing.
            this.RegisterCallback <MouseDownEvent>(e =>
            {
                var leafTarget = e.leafTarget as VisualElement;
                if (leafTarget.parent is ScrollView)
                {
                    ClearSelection();
                }
            });

            m_TreeViewHoverOverlay = highlightOverlayPainter;

            m_Container                             = new VisualElement();
            m_Container.name                        = "explorer-container";
            m_Container.style.flexGrow              = 1;
            m_ClassDragger.builderHierarchyRoot     = m_Container;
            m_HierarchyDragger.builderHierarchyRoot = m_Container;
            Add(m_Container);

            m_SearchBar = new ElementHierarchySearchBar(this);
            Add(m_SearchBar);

            // TODO: Hiding for now since search does not work, especially with style class pills.
            m_SearchBar.style.display = DisplayStyle.None;

            m_ClassPillTemplate = AssetDatabase.LoadAssetAtPath <VisualTreeAsset>(
                BuilderConstants.UIBuilderPackagePath + "/BuilderClassPill.uxml");

            // Create TreeView.
            m_TreeRootItems           = new List <ITreeViewItem>();
            m_TreeView                = new TreeView(m_TreeRootItems, 20, MakeItem, FillItem);
            m_TreeView.viewDataKey    = "unity-builder-explorer-tree";
            m_TreeView.style.flexGrow = 1;
#if UNITY_2020_1_OR_NEWER
            m_TreeView.onSelectionChange += OnSelectionChange;
#else
            m_TreeView.onSelectionChanged += OnSelectionChange;
#endif

#if UNITY_2019_3_OR_NEWER
            m_TreeView.RegisterCallback <MouseDownEvent>(OnLeakedMouseClick);
#endif

            m_Container.Add(m_TreeView);

            m_ContextMenuManipulator.RegisterCallbacksOnTarget(m_Container);
        }
Exemple #6
0
        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);

            // 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.IsSelectorsContainerElement(documentElement))
            {
                var styleSheetAsset     = documentElement.GetStyleSheet();
                var styleSheetFileName  = AssetDatabase.GetAssetPath(styleSheetAsset);
                var styleSheetAssetName = BuilderAssetUtilities.GetStyleSheetAssetName(styleSheetAsset);
                var ssLabel             = new Label(styleSheetAssetName);
                ssLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                ssLabel.AddToClassList("unity-debugger-tree-item-type");
                row.AddToClassList(BuilderConstants.ExplorerHeaderRowClassName);
                labelCont.Add(ssLabel);
                return;
            }
            else if (BuilderSharedStyles.IsSelectorElement(documentElement))
            {
                var selectorParts = BuilderSharedStyles.GetSelectorParts(documentElement);

                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.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);
                    }
                }

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

                return;
            }

            if (BuilderSharedStyles.IsDocumentElement(documentElement))
            {
                var uxmlAsset = documentElement.GetVisualTreeAsset();
                var ssLabel   = new Label(BuilderAssetUtilities.GetVisualTreeAssetAssetName(uxmlAsset));
                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_HierarchyDragger.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 = "#" + 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("." + 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;

            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);
            }

            // Register right-click events for context menu actions.
            m_ContextMenuManipulator.RegisterCallbacksOnTarget(explorerItem);
        }
        void OnPick(MouseDownEvent evt)
        {
            // Do not prevent zoom and pan
            if (evt.button == 2 || (evt.ctrlKey && evt.altKey || (evt.button == (int)MouseButton.RightMouse && evt.altKey)))
            {
                return;
            }

            m_PickedElements.Clear();
            var pickedElement = PickElement(evt.mousePosition, m_PickedElements);

            if (pickedElement != null)
            {
                var timeSinceStartup  = EditorApplication.timeSinceStartup;
                var previousMouseRect = new Rect(
                    m_PreviousPickMousePosition.x - BuilderConstants.PickSelectionRepeatRectHalfSize,
                    m_PreviousPickMousePosition.y - BuilderConstants.PickSelectionRepeatRectHalfSize,
                    BuilderConstants.PickSelectionRepeatRectSize,
                    BuilderConstants.PickSelectionRepeatRectSize);
                if (timeSinceStartup - m_PreviousPickMouseTime > BuilderConstants.PickSelectionRepeatMinTimeDelay && previousMouseRect.Contains(evt.mousePosition))
                {
                    m_SameLocationPickCount++;
                    var offset = 0;

                    var index = m_PickedElements.IndexOf(pickedElement);
                    // For compound controls, we don't seem to have the actual field root element
                    // in the pickedElements list. So we get index == -1 here. We need to do
                    // some magic to select the proper parent element from the list then.
                    if (index < 0)
                    {
                        index  = m_PickedElements.IndexOf(pickedElement.parent);
                        offset = 1;
                    }

                    var maxIndex = m_PickedElements.Count - 1;
                    var newIndex = index + m_SameLocationPickCount - offset;
                    if (newIndex > maxIndex)
                    {
                        m_SameLocationPickCount = 0;
                    }
                    else
                    {
                        pickedElement = m_PickedElements[newIndex];
                    }
                }
                else
                {
                    m_SameLocationPickCount = 0;
                }
                m_PreviousPickMousePosition = evt.mousePosition;
                m_PreviousPickMouseTime     = EditorApplication.timeSinceStartup;

                m_Selection.Select(this, pickedElement);
                SetInnerSelection(pickedElement);

                if (evt.clickCount == 2)
                {
                    var posInViewport = m_PickOverlay.ChangeCoordinatesTo(this, evt.localMousePosition);
                    BuilderInPlaceTextEditingUtilities.OpenEditor(pickedElement, this.ChangeCoordinatesTo(pickedElement, posInViewport));
                }
            }
            else
            {
                ClearInnerSelection();
                m_Selection.ClearSelection(this);
            }

            if (evt.button == (int)MouseButton.RightMouse)
            {
                if (pickedElement != null && m_ContextMenuManipulator != null)
                {
                    pickedElement.SetProperty(BuilderConstants.ElementLinkedDocumentVisualElementVEPropertyName, pickedElement);
                    m_ContextMenuManipulator.RegisterCallbacksOnTarget(pickedElement);
                    m_ContextMenuManipulator.DisplayContextMenu(evt, pickedElement);
                    evt.StopPropagation();
                }
            }
            else
            {
                evt.StopPropagation();
            }
        }
        public ElementHierarchyView(
            BuilderPaneWindow paneWindow,
            VisualElement documentRootElement,
            BuilderSelection selection,
            BuilderClassDragger classDragger,
            BuilderExplorerDragger explorerDragger,
            BuilderElementContextMenu contextMenuManipulator,
            Action <List <VisualElement> > selectElementCallback,
            HighlightOverlayPainter highlightOverlayPainter)
        {
            m_PaneWindow             = paneWindow;
            m_DocumentRootElement    = documentRootElement;
            m_Selection              = selection;
            m_ClassDragger           = classDragger;
            m_ExplorerDragger        = explorerDragger;
            m_ContextMenuManipulator = contextMenuManipulator;

            this.focusable = true;

            m_SelectElementCallback = selectElementCallback;
            hierarchyHasChanged     = true;
            hasUnsavedChanges       = false;

            m_SearchResultsHightlights = new List <VisualElement>();

            this.RegisterCallback <FocusEvent>(e => m_TreeView?.Focus());

            // HACK: ListView/TreeView need to clear their selections when clicking on nothing.
            this.RegisterCallback <MouseDownEvent>(e =>
            {
                var leafTarget = e.leafTarget as VisualElement;
                if (leafTarget.parent is ScrollView)
                {
                    ClearSelection();
                }
            });

            m_TreeViewHoverOverlay = highlightOverlayPainter;

            m_Container                            = new VisualElement();
            m_Container.name                       = "explorer-container";
            m_Container.style.flexGrow             = 1;
            m_ClassDragger.builderHierarchyRoot    = m_Container;
            m_ExplorerDragger.builderHierarchyRoot = m_Container;
            Add(m_Container);

            m_ClassPillTemplate = AssetDatabase.LoadAssetAtPath <VisualTreeAsset>(
                BuilderConstants.UIBuilderPackagePath + "/BuilderClassPill.uxml");

            // Create TreeView.
            m_TreeRootItems = new List <ITreeViewItem>();
            m_TreeView      = new TreeView(m_TreeRootItems, 20, MakeItem, FillItem);
#if UNITY_2020_1_OR_NEWER
            m_TreeView.selectionType = SelectionType.Multiple;
#else
            m_TreeView.selectionType = SelectionType.Single; // ListView/TreeView do not support selecting mutliple items via code.
#endif
            m_TreeView.viewDataKey    = "unity-builder-explorer-tree";
            m_TreeView.style.flexGrow = 1;
#if UNITY_2020_1_OR_NEWER
            m_TreeView.onSelectionChange += OnSelectionChange;
#else
            m_TreeView.onSelectionChanged += OnSelectionChange;
#endif

#if UNITY_2019_3_OR_NEWER
            m_TreeView.RegisterCallback <MouseDownEvent>(OnLeakedMouseClick);
#endif

            m_Container.Add(m_TreeView);

            m_ContextMenuManipulator.RegisterCallbacksOnTarget(m_Container);
        }
        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 owningUxmlPath         = documentElement.GetProperty(BuilderConstants.ExplorerItemLinkedUXMLFileName) as string;
                var isPartOfParentDocument = !string.IsNullOrEmpty(owningUxmlPath);

                var styleSheetAsset     = documentElement.GetStyleSheet();
                var styleSheetFileName  = AssetDatabase.GetAssetPath(styleSheetAsset);
                var styleSheetAssetName = BuilderAssetUtilities.GetStyleSheetAssetName(styleSheetAsset, hasUnsavedChanges && !isPartOfParentDocument);
                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);

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

                if (isPartOfParentDocument)
                {
                    row.AddToClassList(BuilderConstants.ExplorerItemHiddenClassName);
                }

                // Show name of UXML file that USS file 'belongs' to.
                if (!string.IsNullOrEmpty(owningUxmlPath))
                {
                    var pathStr = Path.GetFileName(owningUxmlPath);
                    var label   = new Label(BuilderConstants.TripleSpace + 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);
                }

                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);

                // Check if selector element is inside current open StyleSheets
                if (documentElement.IsParentSelector())
                {
                    row.AddToClassList(BuilderConstants.ExplorerItemHiddenClassName);
                }

                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);

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

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

                return;
            }

            // Check if element is inside current document.
            if (!documentElement.IsPartOfActiveVisualTreeAsset(m_PaneWindow.document))
            {
                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);
                }
            }

            // Add stylesheets.
            if (elementInfoVisibilityState.HasFlag(BuilderExplorer.BuilderElementInfoVisibilityState.StyleSheets))
            {
                var vea = documentElement.GetVisualElementAsset();
                if (vea != null)
                {
                    foreach (var ussPath in vea.GetStyleSheetPaths())
                    {
                        if (string.IsNullOrEmpty(ussPath))
                        {
                            continue;
                        }

                        var classLabelCont = new VisualElement();
                        classLabelCont.AddToClassList(BuilderConstants.ExplorerItemLabelContClassName);
                        explorerItem.Add(classLabelCont);

                        var classLabel = new Label(Path.GetFileName(ussPath));
                        classLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        classLabel.AddToClassList(BuilderConstants.ElementAttachedStyleSheetClassName);
                        classLabel.AddToClassList("unity-debugger-tree-item-classlist-label");
                        classLabelCont.Add(classLabel);
                    }
                }
                else
                {
                    for (int i = 0; i < documentElement.styleSheets.count; ++i)
                    {
                        var attachedStyleSheet = documentElement.styleSheets[i];
                        if (attachedStyleSheet == null)
                        {
                            continue;
                        }

                        var classLabelCont = new VisualElement();
                        classLabelCont.AddToClassList(BuilderConstants.ExplorerItemLabelContClassName);
                        explorerItem.Add(classLabelCont);

                        var classLabel = new Label(attachedStyleSheet.name + BuilderConstants.UssExtension);
                        classLabel.AddToClassList(BuilderConstants.ExplorerItemLabelClassName);
                        classLabel.AddToClassList(BuilderConstants.ElementAttachedStyleSheetClassName);
                        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 ElementHierarchyView(
            BuilderPaneWindow paneWindow,
            VisualElement documentRootElement,
            BuilderSelection selection,
            BuilderClassDragger classDragger,
            BuilderExplorerDragger explorerDragger,
            BuilderElementContextMenu contextMenuManipulator,
            Action <List <VisualElement> > selectElementCallback,
            HighlightOverlayPainter highlightOverlayPainter)
        {
            m_PaneWindow             = paneWindow;
            m_DocumentRootElement    = documentRootElement;
            m_Selection              = selection;
            m_ClassDragger           = classDragger;
            m_ExplorerDragger        = explorerDragger;
            m_ContextMenuManipulator = contextMenuManipulator;

            this.focusable = true;

            m_SelectElementCallback = selectElementCallback;
            hierarchyHasChanged     = true;
            hasUnsavedChanges       = false;

            m_SearchResultsHightlights = new List <VisualElement>();

            this.RegisterCallback <FocusEvent>(e => m_TreeView?.Focus());

            // HACK: ListView/TreeView need to clear their selections when clicking on nothing.
            this.RegisterCallback <MouseDownEvent>(e =>
            {
                var leafTarget = e.leafTarget as VisualElement;
                if (leafTarget.parent is ScrollView)
                {
                    m_PaneWindow.primarySelection.ClearSelection(null);
                }
            });

            m_TreeViewHoverOverlay = highlightOverlayPainter;

            m_Container                            = new VisualElement();
            m_Container.name                       = "explorer-container";
            m_Container.style.flexGrow             = 1;
            m_ClassDragger.builderHierarchyRoot    = m_Container;
            m_ExplorerDragger.builderHierarchyRoot = m_Container;
            Add(m_Container);

            m_ClassPillTemplate = BuilderPackageUtilities.LoadAssetAtPath <VisualTreeAsset>(
                BuilderConstants.UIBuilderPackagePath + "/BuilderClassPill.uxml");

            // Create TreeView.
            m_TreeRootItems = new List <TreeViewItem>();
            m_TreeView      = new TreeView(20, MakeItem, FillItem);
            m_TreeView.SetRootItems(m_TreeRootItems);
            m_TreeViewController = m_TreeView.viewController as DefaultTreeViewController <VisualElement>;

            m_TreeView.selectionType            = SelectionType.Multiple;
            m_TreeView.viewDataKey              = "unity-builder-explorer-tree";
            m_TreeView.style.flexGrow           = 1;
            m_TreeView.onSelectedIndicesChange += OnSelectionChange;

            m_TreeView.RegisterCallback <MouseDownEvent>(OnLeakedMouseClick);
            m_Container.Add(m_TreeView);

            m_ContextMenuManipulator.RegisterCallbacksOnTarget(m_Container);
        }