示例#1
0
        /// <summary>
        /// Adds a mask for a view.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="child"></param>
        static void AddMaskToView(GUIViewProxy view, VisualElement child)
        {
            // Since 2019.3(?), we must suppress input to the elements behind masks.
            // TODO Doesn't suppress everything, e.g. tooltips are shown still.
            child.RegisterCallback <MouseDownEvent>((e) => e.StopPropagation());
            child.RegisterCallback <MouseUpEvent>((e) => e.StopPropagation());
            child.RegisterCallback <MouseMoveEvent>((e) => e.StopPropagation());
            child.RegisterCallback <WheelEvent>((e) => e.StopPropagation());
            child.RegisterCallback <PointerDownEvent>((e) => e.StopPropagation());
            child.RegisterCallback <PointerUpEvent>((e) => e.StopPropagation());
            child.RegisterCallback <PointerMoveEvent>((e) => e.StopPropagation());
            child.RegisterCallback <KeyDownEvent>((e) => e.StopPropagation());
            child.RegisterCallback <KeyUpEvent>((e) => e.StopPropagation());

            if (view.IsDockedToEditor())
            {
                UIElementsHelper.GetVisualTree(view).Add(child);
            }
            else
            {
                var viewVisualElement = UIElementsHelper.GetVisualTree(view);

                Debug.Assert(
                    viewVisualElement.Children().Count() == 2 &&
                    viewVisualElement.Children().Count(viewChild => viewChild is IMGUIContainer) == 1,
                    "Could not find the expected VisualElement structure"
                    );

                foreach (var visualElement in viewVisualElement.Children())
                {
                    if (!(visualElement is IMGUIContainer))
                    {
                        visualElement.Add(child);
                        break;
                    }
                }
            }
        }
示例#2
0
        public static MaskData GetViewsAndRects(IEnumerable <UnmaskedView> unmaskedViews, out bool foundAncestorProperty)
        {
            foundAncestorProperty = false;

            var allViews = new List <GUIViewProxy>();

            GUIViewDebuggerHelperProxy.GetViews(allViews);

            // initialize result
            var result           = new Dictionary <GUIViewProxy, MaskViewData>();
            var unmaskedControls = new Dictionary <GUIViewProxy, List <GuiControlSelector> >();
            var viewsWithWindows = new Dictionary <GUIViewProxy, HashSet <EditorWindow> >();

            foreach (var unmaskedView in unmaskedViews)
            {
                foreach (var view in GetMatchingViews(unmaskedView, allViews, viewsWithWindows))
                {
                    MaskViewData maskViewData;
                    if (!result.TryGetValue(view, out maskViewData))
                    {
                        result[view] = new MaskViewData
                        {
                            rects            = new List <Rect>(8),
                            maskType         = unmaskedView.m_MaskType,
                            maskSizeModifier = unmaskedView.m_MaskSizeModifier,
                            EditorWindowType = unmaskedView.ResolvedEditorWindowType
                        };
                    }

                    List <GuiControlSelector> controls;
                    if (!unmaskedControls.TryGetValue(view, out controls))
                    {
                        unmaskedControls[view] = controls = new List <GuiControlSelector>();
                    }

                    controls.AddRange(unmaskedView.m_UnmaskedControls);
                }
            }

            // validate input
            foreach (var viewWithWindow in viewsWithWindows)
            {
                if (viewWithWindow.Value.Count > 1)
                {
                    throw new ArgumentException(
                              string.Format(
                                  "Tried to get controls from multiple EditorWindows docked in the same location: {0}",
                                  string.Join(", ", viewWithWindow.Value.Select(w => w.GetType().Name).ToArray())
                                  ),
                              "unmaskedViews"
                              );
                }
            }

            // populate result
            var drawInstructions         = new List <IMGUIDrawInstructionProxy>(32);
            var namedControlInstructions = new List <IMGUINamedControlInstructionProxy>(32);
            var propertyInstructions     = new List <IMGUIPropertyInstructionProxy>(32);

            foreach (var viewRects in result)
            {
                // prevents null exception when repainting in case e.g., user has accidentally maximized view
                if (!viewRects.Key.IsWindowAndRootViewValid)
                {
                    continue;
                }

                var unmaskedControlSelectors = unmaskedControls[viewRects.Key];
                if (unmaskedControlSelectors.Count == 0)
                {
                    continue;
                }

                // if the view refers to an InspectorWindow, flush the optimized GUI blocks so that Editor control rects will be updated
                HashSet <EditorWindow> windows;
                if (viewsWithWindows.TryGetValue(viewRects.Key, out windows) && windows.Count > 0)
                {
                    InspectorWindowProxy.DirtyAllEditors(windows.First());
                }

                // TODO: use actual selectors when API is in place
                GUIViewDebuggerHelperProxy.DebugWindow(viewRects.Key);

                viewRects.Key.RepaintImmediately();

                GUIViewDebuggerHelperProxy.GetDrawInstructions(drawInstructions);
                GUIViewDebuggerHelperProxy.GetNamedControlInstructions(namedControlInstructions);
                GUIViewDebuggerHelperProxy.GetPropertyInstructions(propertyInstructions);

                foreach (var controlSelector in unmaskedControls[viewRects.Key])
                {
                    Rect regionRect  = Rect.zero;
                    bool regionFound = false;
                    switch (controlSelector.SelectorMode)
                    {
                    case GuiControlSelector.Mode.GuiContent:
                        var selectorContent = controlSelector.GuiContent;
                        foreach (var instruction in drawInstructions)
                        {
                            if (AreEquivalent(instruction.usedGUIContent, selectorContent))
                            {
                                regionFound = true;
                                regionRect  = instruction.rect;
                            }
                        }
                        break;

                    case GuiControlSelector.Mode.GuiStyleName:
                        foreach (var instruction in drawInstructions)
                        {
                            if (instruction.usedGUIStyleName == controlSelector.GuiStyleName)
                            {
                                regionFound = true;
                                regionRect  = instruction.rect;
                            }
                        }
                        break;

                    case GuiControlSelector.Mode.NamedControl:
                        foreach (var instruction in namedControlInstructions)
                        {
                            if (instruction.name == controlSelector.ControlName)
                            {
                                regionFound = true;
                                regionRect  = instruction.rect;
                            }
                        }
                        break;

                    case GuiControlSelector.Mode.Property:
                        if (controlSelector.TargetType == null)
                        {
                            continue;
                        }
                        var targetTypeName = controlSelector.TargetType.AssemblyQualifiedName;
                        foreach (var instruction in propertyInstructions)
                        {
                            if (
                                instruction.targetTypeName == targetTypeName &&
                                instruction.path == controlSelector.PropertyPath
                                )
                            {
                                regionFound = true;
                                regionRect  = instruction.rect;
                            }
                        }

                        if (!regionFound)
                        {
                            // Property instruction not found
                            // Let's see if we can find any of the ancestor instructions to allow the user to unfold
                            regionFound = FindAncestorPropertyRegion(controlSelector.PropertyPath, targetTypeName,
                                                                     drawInstructions, propertyInstructions,
                                                                     ref regionRect);
                            foundAncestorProperty = regionFound;
                        }
                        break;

                    case GuiControlSelector.Mode.ObjectReference:
                        if (controlSelector.ObjectReference == null)
                        {
                            continue;
                        }

                        var referencedObject = controlSelector.ObjectReference.SceneObjectReference.ReferencedObject;
                        if (referencedObject == null)
                        {
                            continue;
                        }

                        foreach (var instruction in drawInstructions)
                        {
                            if (instruction.usedGUIContent.text != referencedObject.name)
                            {
                                continue;
                            }

                            regionFound = true;
                            regionRect  = instruction.rect;
                            break;
                        }
                        break;

                    case GuiControlSelector.Mode.VisualElement:
                        // At least one of the three properties must be specified in order to make a sensible query.
                        if (controlSelector.VisualElementTypeName.IsNotNullOrWhiteSpace() ||
                            controlSelector.VisualElementClassName.IsNotNullOrWhiteSpace() ||
                            controlSelector.VisualElementName.IsNotNullOrWhiteSpace())
                        {
                            var visualTree = UIElementsHelper.GetVisualTree(viewRects.Key);
                            // Passing null as name or class will make the query to consider it as an optional argument.
                            var queryBuilder = visualTree.Query(
                                controlSelector.VisualElementName.AsNullIfWhiteSpace(),
                                controlSelector.VisualElementClassName.AsNullIfWhiteSpace()
                                );
                            // Apply type, if valid type specified.
                            if (controlSelector.VisualElementTypeName.IsNotNullOrWhiteSpace())
                            {
                                queryBuilder = queryBuilder.Where(elem => elem.GetType().ToString() == controlSelector.VisualElementTypeName);
                            }
                            // Keep behavior consistent with the IMGUI selectors: use the last available element if we have multiple hits.
                            var element = queryBuilder.Build().Last();
                            if (element != null)
                            {
                                regionFound = true;
                                regionRect  = element.worldBound;
                            }
                        }
                        break;

                    default:
                        Debug.LogErrorFormat(
                            "No method currently implemented for selecting using specified mode: {0}",
                            controlSelector.SelectorMode
                            );
                        break;
                    }

                    if (regionFound)
                    {
                        if (viewRects.Value.maskSizeModifier == MaskSizeModifier.ExpandWidthToWholeWindow)
                        {
                            const int padding = 5;
                            regionRect.x     = padding;
                            regionRect.width = viewRects.Key.Position.width - padding * 2;
                        }
                        viewRects.Value.rects.Add(regionRect);
                    }
                }

                GUIViewDebuggerHelperProxy.StopDebugging();
            }

            return(new MaskData(result));
        }
示例#3
0
        /// <summary>
        /// Applies masking.
        /// </summary>
        /// <param name="unmaskedViewsAndRegionsMaskData"></param>
        /// <param name="maskColor"></param>
        /// <param name="highlightedRegionsMaskData"></param>
        /// <param name="highlightColor"></param>
        /// <param name="blockedInteractionsColor"></param>
        /// <param name="highlightThickness"></param>
        public static void Mask(
            UnmaskedView.MaskData unmaskedViewsAndRegionsMaskData, Color maskColor,
            UnmaskedView.MaskData highlightedRegionsMaskData, Color highlightColor, Color blockedInteractionsColor, float highlightThickness
            )
        {
            Unmask();

            CopyMaskData(unmaskedViewsAndRegionsMaskData, s_UnmaskedViews);
            CopyMaskData(highlightedRegionsMaskData, s_HighlightedViews);

            List <GUIViewProxy> views = new List <GUIViewProxy>();

            GUIViewDebuggerHelperProxy.GetViews(views);

            foreach (var view in views)
            {
                if (!view.IsValid)
                {
                    continue;
                }

                MaskViewData maskViewData;

                var viewRect = new Rect(0, 0, view.Position.width, view.Position.height);

                // mask everything except the unmasked view rects
                if (s_UnmaskedViews.TryGetValue(view, out maskViewData))
                {
                    // Beginning from 2021.2 the layout of floating/undocked EditorWindows has changed a bit and now contains
                    // an offset caused by the tab area which we need to take into account.
                    EditorWindow parentWindow = null;
                    if (maskViewData.EditorWindowType != null)
                    {
                        parentWindow = FindOpenEditorWindowInstance(maskViewData.EditorWindowType);
                    }

                    List <Rect> rects       = maskViewData.rects;
                    var         maskedRects = GetNegativeSpaceRects(viewRect, rects);
                    for (var i = 0; i < maskedRects.Count; ++i)
                    {
                        var rect = maskedRects[i];
                        if (parentWindow != null && !parentWindow.IsDocked())
                        {
                            // In theory we could have an X offset also but it seems highgly unlikely.
                            rect.y -= parentWindow.rootVisualElement.layout.y;
                        }
                        var mask = new VisualElement();
                        mask.style.backgroundColor = maskColor;
                        mask.SetLayout(rect);
                        AddMaskToView(view, mask);
                        s_Masks.Add(mask);
                    }

                    if (maskViewData.maskType == MaskType.BlockInteractions)
                    {
                        foreach (var rect in rects)
                        {
                            var mask = new VisualElement();
                            mask.style.backgroundColor = blockedInteractionsColor;
                            mask.SetLayout(rect);
                            AddMaskToView(view, mask);
                            s_Masks.Add(mask);
                        }
                    }
                }
                else // mask the whole view
                {
                    var mask = new VisualElement();
                    mask.style.backgroundColor = maskColor;
                    mask.SetLayout(viewRect);
                    AddMaskToView(view, mask);
                    s_Masks.Add(mask);
                }

                if (s_HighlightedViews.TryGetValue(view, out maskViewData))
                {
                    var rects = maskViewData.rects;
                    // unclip highlight to apply as "outer stroke" if it is being applied to some control(s) in the view
                    var unclip       = rects.Count > 1 || rects[0] != viewRect;
                    var borderRadius = 5.0f;
                    foreach (var rect in rects)
                    {
                        var highlighter = new VisualElement();
#if UNITY_2019_3_OR_NEWER
                        highlighter.style.borderLeftColor   = highlightColor;
                        highlighter.style.borderRightColor  = highlightColor;
                        highlighter.style.borderTopColor    = highlightColor;
                        highlighter.style.borderBottomColor = highlightColor;
#else
                        highlighter.style.borderColor = highlightColor;
#endif
                        highlighter.style.borderLeftWidth   = highlightThickness;
                        highlighter.style.borderRightWidth  = highlightThickness;
                        highlighter.style.borderTopWidth    = highlightThickness;
                        highlighter.style.borderBottomWidth = highlightThickness;

                        highlighter.style.borderBottomLeftRadius  = borderRadius;
                        highlighter.style.borderTopLeftRadius     = borderRadius;
                        highlighter.style.borderBottomRightRadius = borderRadius;
                        highlighter.style.borderTopRightRadius    = borderRadius;

                        highlighter.pickingMode = PickingMode.Ignore;
                        var layout = rect;
                        if (unclip)
                        {
                            layout.xMin -= highlightThickness;
                            layout.xMax += highlightThickness;
                            layout.yMin -= highlightThickness;
                            layout.yMax += highlightThickness;
                        }
                        highlighter.SetLayout(layout);
                        UIElementsHelper.GetVisualTree(view).Add(highlighter);
                        s_Highlighters.Add(highlighter);
                    }
                }
            }

            s_LastHighlightTime = EditorApplication.timeSinceStartup;
        }
        /// <summary>
        /// Applies masking.
        /// </summary>
        /// <param name="unmaskedViewsAndRegionsMaskData"></param>
        /// <param name="maskColor"></param>
        /// <param name="highlightedRegionsMaskData"></param>
        /// <param name="highlightColor"></param>
        /// <param name="blockedInteractionsColor"></param>
        /// <param name="highlightThickness"></param>
        public static void Mask(
            UnmaskedView.MaskData unmaskedViewsAndRegionsMaskData, Color maskColor,
            UnmaskedView.MaskData highlightedRegionsMaskData, Color highlightColor, Color blockedInteractionsColor, float highlightThickness
            )
        {
            Unmask();

            CopyMaskData(unmaskedViewsAndRegionsMaskData, s_UnmaskedViews);
            CopyMaskData(highlightedRegionsMaskData, s_HighlightedViews);

            List <GUIViewProxy> views = new List <GUIViewProxy>();

            GUIViewDebuggerHelperProxy.GetViews(views);

            foreach (var view in views)
            {
                if (!view.IsValid)
                {
                    continue;
                }

                MaskViewData maskViewData;

                var viewRect = new Rect(0, 0, view.Position.width, view.Position.height);

                // mask everything except the unmasked view rects
                if (s_UnmaskedViews.TryGetValue(view, out maskViewData))
                {
                    List <Rect> rects       = maskViewData.rects;
                    var         maskedRects = GetNegativeSpaceRects(viewRect, rects);
                    foreach (var rect in maskedRects)
                    {
                        var mask = new VisualElement();
                        mask.style.backgroundColor = maskColor;
                        mask.SetLayout(rect);
                        AddMaskToView(view, mask);
                        s_Masks.Add(mask);
                    }

                    if (maskViewData.maskType == MaskType.BlockInteractions)
                    {
                        foreach (var rect in rects)
                        {
                            var mask = new VisualElement();
                            mask.style.backgroundColor = blockedInteractionsColor;
                            mask.SetLayout(rect);
                            AddMaskToView(view, mask);
                            s_Masks.Add(mask);
                        }
                    }
                }
                // mask the whole view
                else
                {
                    var mask = new VisualElement();
                    mask.style.backgroundColor = maskColor;
                    mask.SetLayout(viewRect);
                    AddMaskToView(view, mask);
                    s_Masks.Add(mask);
                }

                if (s_HighlightedViews.TryGetValue(view, out maskViewData))
                {
                    var rects = maskViewData.rects;
                    // unclip highlight to apply as "outer stroke" if it is being applied to some control(s) in the view
                    var unclip       = rects.Count > 1 || rects[0] != viewRect;
                    var borderRadius = 5.0f;
                    foreach (var rect in rects)
                    {
                        var highlighter = new VisualElement();
#if UNITY_2019_3_OR_NEWER
                        highlighter.style.borderLeftColor   = highlightColor;
                        highlighter.style.borderRightColor  = highlightColor;
                        highlighter.style.borderTopColor    = highlightColor;
                        highlighter.style.borderBottomColor = highlightColor;
#else
                        highlighter.style.borderColor = highlightColor;
#endif
                        highlighter.style.borderLeftWidth   = highlightThickness;
                        highlighter.style.borderRightWidth  = highlightThickness;
                        highlighter.style.borderTopWidth    = highlightThickness;
                        highlighter.style.borderBottomWidth = highlightThickness;

                        highlighter.style.borderBottomLeftRadius  = borderRadius;
                        highlighter.style.borderTopLeftRadius     = borderRadius;
                        highlighter.style.borderBottomRightRadius = borderRadius;
                        highlighter.style.borderTopRightRadius    = borderRadius;

                        highlighter.pickingMode = PickingMode.Ignore;
                        var layout = rect;
                        if (unclip)
                        {
                            layout.xMin -= highlightThickness;
                            layout.xMax += highlightThickness;
                            layout.yMin -= highlightThickness;
                            layout.yMax += highlightThickness;
                        }
                        highlighter.SetLayout(layout);
                        UIElementsHelper.GetVisualTree(view).Add(highlighter);
                        s_Highlighters.Add(highlighter);
                    }
                }
            }

            s_LastHighlightTime = EditorApplication.timeSinceStartup;
        }