Пример #1
0
        /// <summary>
        ///     Converts a rectangle from an Avalon Rect to a Win32 RECT
        /// </summary>
        /// <remarks>
        ///     Rounds "double" values to the nearest "int"
        /// </remarks>
        /// <param name="rect">
        ///     The rectangle as an Avalon Rect
        /// </param>
        /// <returns>
        ///     The rectangle as a Win32 RECT
        /// </returns>
        internal static NativeMethods.RECT FromRect(Rect rect)
        {
            NativeMethods.RECT rc = new NativeMethods.RECT();

            rc.top    = DoubleUtil.DoubleToInt(rect.Y);
            rc.left   = DoubleUtil.DoubleToInt(rect.X);
            rc.bottom = DoubleUtil.DoubleToInt(rect.Bottom);
            rc.right  = DoubleUtil.DoubleToInt(rect.Right);

            return(rc);
        }
Пример #2
0
        // To position the popup, we find the InterestPoints of the placement rectangle/point
        // in the screen coordinate space.  We also find the InterestPoints of the child in
        // the popup's space.  Then we attempt all valid combinations of matching InterestPoints
        // (based on PlacementMode) to find the position that best fits on the screen.
        // NOTE: any reference to the screen implies the monitor for full trust and
        //       the browser area for partial trust
        private void UpdatePosition()
        {
            if (_popupRoot == null)
            {
                return;
            }

            PlacementMode placement = PlacementInternal;

            // Get a list of the corners of the target/child in screen space
            Point[] placementTargetInterestPoints = GetPlacementTargetInterestPoints(placement);
            Point[] childInterestPoints           = GetChildInterestPoints(placement);

            // Find bounds of screen and child in screen space
            Rect targetBounds = GetBounds(placementTargetInterestPoints);
            Rect screenBounds;
            Rect childBounds = GetBounds(childInterestPoints);

            double childArea = childBounds.Width * childBounds.Height;

            Rect windowRect = _secHelper.GetWindowRect();

            _positionInfo ??= new PositionInfo();
            _positionInfo.X         = (int)windowRect.X;
            _positionInfo.Y         = (int)windowRect.Y;
            _positionInfo.ChildSize = windowRect.Size;

            // Rank possible positions
            int              bestIndex       = -1;
            Vector           bestTranslation = new Vector(_positionInfo.X, _positionInfo.Y);
            double           bestScore       = -1;
            PopupPrimaryAxis bestAxis        = PopupPrimaryAxis.None;

            int positions;

            CustomPopupPlacement[] customPlacements = null;

            // Find the number of possible positions
            if (placement == PlacementMode.Custom)
            {
                CustomPopupPlacementCallback customCallback = CustomPopupPlacementCallback;
                if (customCallback != null)
                {
                    customPlacements = customCallback(childBounds.Size, targetBounds.Size, new Point(HorizontalOffset, VerticalOffset));
                }
                positions = customPlacements == null ? 0 : customPlacements.Length;

                // Return if callback closed the popup
                if (!IsOpen)
                {
                    return;
                }
            }
            else
            {
                positions = GetNumberOfCombinations(placement);
            }

            // Try each position until the best one is found
            for (int i = 0; i < positions; i++)
            {
                Vector popupTranslation;

                PopupPrimaryAxis axis;

                // Get the ith Position to rank
                if (placement == PlacementMode.Custom)
                {
                    // The custom callback only calculates relative to 0,0
                    // so the placementTarget's top/left need to be re-applied.
                    popupTranslation = (Vector)placementTargetInterestPoints[(int)InterestPoint.TopLeft]
                                       + (Vector)customPlacements[i].Point; // vector from origin

                    axis = customPlacements[i].PrimaryAxis;
                }
                else
                {
                    PointCombination pointCombination = GetPointCombination(placement, i, out axis);

                    InterestPoint targetInterestPoint = pointCombination.TargetInterestPoint;
                    InterestPoint childInterestPoint  = pointCombination.ChildInterestPoint;

                    // Compute the vector from the screen origin to the top left corner of the popup
                    // that will cause the the two interest points to overlap
                    popupTranslation = placementTargetInterestPoints[(int)targetInterestPoint]
                                       - childInterestPoints[(int)childInterestPoint];
                }

                // Find percent of popup on screen by translating the popup bounds
                // and calculating the percent of the bounds that is on screen
                // Note: this score is based on the percent of the popup that is on screen
                //       not the percent of the child that is on screen.  For certain
                //       scenarios, this may produce in counter-intuitive results.
                //       If this is a problem, more complex scoring is needed
                Rect tranlsatedChildBounds = Rect.Offset(childBounds, popupTranslation);
                screenBounds = GetScreenBounds(targetBounds, placementTargetInterestPoints[(int)InterestPoint.TopLeft]);
                Rect currentIntersection = Rect.Intersect(screenBounds, tranlsatedChildBounds);

                // Calculate area of intersection
                double score = currentIntersection != Rect.Empty ? currentIntersection.Width * currentIntersection.Height : 0;

                // If current score is better than the best score so far, save the position info
                if (score - bestScore > Tolerance)
                {
                    bestIndex       = i;
                    bestTranslation = popupTranslation;
                    bestScore       = score;
                    bestAxis        = axis;

                    // Stop when we find a popup that is completely on screen
                    if (Math.Abs(score - childArea) < Tolerance)
                    {
                        break;
                    }
                }
            }

            // Check to see if the pop needs to be nudged onto the screen.
            // Popups are not nudged if their axes do not align with the screen axes

            // Use the size of the popupRoot in case it is clipping the popup content
            Matrix transformToDevice = _secHelper.GetTransformToDevice();

            childBounds = new Rect((Size)transformToDevice.Transform((Point)GetChildSize()));

            childBounds.Offset(bestTranslation);

            Vector childTranslation = (Vector)transformToDevice.Transform(GetChildTranslation());

            childBounds.Offset(childTranslation);

            screenBounds = GetScreenBounds(targetBounds, placementTargetInterestPoints[(int)InterestPoint.TopLeft]);
            Rect intersection = Rect.Intersect(screenBounds, childBounds);

            // See if width/height of intersection are less than child's
            if (Math.Abs(intersection.Width - childBounds.Width) > Tolerance ||
                Math.Abs(intersection.Height - childBounds.Height) > Tolerance)
            {
                // Nudge Horizontally
                Point topLeft  = placementTargetInterestPoints[(int)InterestPoint.TopLeft];
                Point topRight = placementTargetInterestPoints[(int)InterestPoint.TopRight];

                // Create a vector pointing from the top of the placement target to the bottom
                // to determine which direction the popup should be nudged in.
                // If the vector is zero (NaN's after normalization), nudge horizontally
                Vector horizontalAxis = topRight - topLeft;
                horizontalAxis.Normalize();

                // See if target's horizontal axis is aligned with screen
                // (For opaque windows always translate horizontally)
                if (!IsTransparent || double.IsNaN(horizontalAxis.Y) || Math.Abs(horizontalAxis.Y) < Tolerance)
                {
                    // Nudge horizontally
                    if (childBounds.Right > screenBounds.Right)
                    {
                        bestTranslation.X  = screenBounds.Right - childBounds.Width;
                        bestTranslation.X -= childTranslation.X;
                    }
                    else if (childBounds.Left < screenBounds.Left)
                    {
                        bestTranslation.X  = screenBounds.Left;
                        bestTranslation.X -= childTranslation.X;
                    }
                }
                else if (IsTransparent && Math.Abs(horizontalAxis.X) < Tolerance)
                {
                    // Nudge vertically, limit horizontally
                    if (childBounds.Bottom > screenBounds.Bottom)
                    {
                        bestTranslation.Y  = screenBounds.Bottom - childBounds.Height;
                        bestTranslation.Y -= childTranslation.Y;
                    }
                    else if (childBounds.Top < screenBounds.Top)
                    {
                        bestTranslation.Y  = screenBounds.Top;
                        bestTranslation.Y -= childTranslation.Y;
                    }
                }

                // Nudge Vertically
                Point bottomLeft = placementTargetInterestPoints[(int)InterestPoint.BottomLeft];

                // Create a vector pointing from the top of the placement target to the bottom
                // to determine which direction the popup should be nudged in
                // If the vector is zero (NaN's after normalization), nudge vertically
                Vector verticalAxis = topLeft - bottomLeft;
                verticalAxis.Normalize();

                // Axis is aligned with screen, nudge
                if (!IsTransparent || double.IsNaN(verticalAxis.X) || Math.Abs(verticalAxis.X) < Tolerance)
                {
                    if (childBounds.Bottom > screenBounds.Bottom)
                    {
                        bestTranslation.Y  = screenBounds.Bottom - childBounds.Height;
                        bestTranslation.Y -= childTranslation.Y;
                    }
                    else if (childBounds.Top < screenBounds.Top)
                    {
                        bestTranslation.Y  = screenBounds.Top;
                        bestTranslation.Y -= childTranslation.Y;
                    }
                }
                else if (IsTransparent && Math.Abs(verticalAxis.Y) < Tolerance)
                {
                    if (childBounds.Right > screenBounds.Right)
                    {
                        bestTranslation.X  = screenBounds.Right - childBounds.Width;
                        bestTranslation.X -= childTranslation.X;
                    }
                    else if (childBounds.Left < screenBounds.Left)
                    {
                        bestTranslation.X  = screenBounds.Left;
                        bestTranslation.X -= childTranslation.X;
                    }
                }
            }

            // Finally, take the best position and apply it to the popup
            int bestX = DoubleUtil.DoubleToInt(bestTranslation.X);
            int bestY = DoubleUtil.DoubleToInt(bestTranslation.Y);

            if (bestX != _positionInfo.X || bestY != _positionInfo.Y)
            {
                _positionInfo.X = bestX;
                _positionInfo.Y = bestY;
                _secHelper.SetPopupPos(true, bestX, bestY, false, 0, 0);
            }

            Size GetChildSize()
            {
                if (_popup.Child is { } child)
                {
                    return(child.RenderSize);
                }
                return(_popupRoot.RenderSize);
            }

            Point GetChildTranslation()
            {
                if (_popup.Child is { } child)
                {
                    return(child.TranslatePoint(new Point(), _popupRoot));
                }
                return(new Point());
            }
        }
Пример #3
0
        private void RaiseInfoCardOpeningEvent()
        {
            ResetInfoCardTimer();

            var lastMouseOver = LastMouseOverWithInfoCard;

            if (lastMouseOver != null)
            {
                var showInfoCard = true;
                var inputElement = lastMouseOver as IInputElement;
                if (inputElement != null)
                {
                    // Raise the screen tip opening event
                    var e = new RoutedEventArgs(InfoCardOpeningEvent, this);
                    inputElement.RaiseEvent(e);
                    showInfoCard = !e.Handled;
                }
                if (showInfoCard)
                {
                    if ((_currentInfoCard != null) && !_currentInfoCard.IsOpen)
                    {
                        RetireInfoCard(_currentInfoCard);
                    }

                    _currentInfoCard = CreateInfoCard(lastMouseOver);

                    if (_currentInfoCard != null)
                    {
                        var targetElement = lastMouseOver as UIElement;

                        _currentInfoCard.TargetElement = targetElement;

                        var infoCardPosition = Mouse.GetPosition(inputElement);
                        var infoCardSite     = _currentInfoCard.RegisteredInfoCardSite ??
                                               lastMouseOver.FindVisualAncestorByType <InfoCardSite>();

                        if (infoCardSite == null)
                        {
                            var window = Window.GetWindow(lastMouseOver);
                            if (window != null)
                            {
                                if (!_generatedSites.TryGetValue(window, out infoCardSite))
                                {
                                    infoCardSite            = new InfoCardSite();
                                    _generatedSites[window] = infoCardSite;
                                }
                            }
                            else
                            {
                                RetireInfoCard(_currentInfoCard);
                                _currentInfoCard = null;
                                return;
                            }
                        }

                        if (!_currentInfoCard.IsOpen)
                        {
                            if (!infoCardSite.InfoCards.Contains(_currentInfoCard))
                            {
                                SetUnregisterInfoCardOnClose(_currentInfoCard, true);
                                infoCardSite.InfoCards.Add(_currentInfoCard);
                            }
                        }

                        if (infoCardSite.IsLoaded)
                        {
                            var targetVisual = targetElement;
                            if (targetVisual != null)
                            {
                                var transformToVisual = targetVisual.TransformToVisual(infoCardSite);
                                if (transformToVisual != null)
                                {
                                    infoCardPosition = transformToVisual.Transform(infoCardPosition);
                                }
                            }
                        }

                        if (targetElement != null)
                        {
                            var customPlacementCallback = _currentInfoCard.CustomPlacementCallback ??
                                                          GetCustomInfoCardPlacementCallback(targetElement);
                            if (customPlacementCallback != null)
                            {
                                _currentInfoCard.UpdateLayout();
                                infoCardPosition = customPlacementCallback(
                                    _currentInfoCard.RenderSize,
                                    targetElement,
                                    infoCardPosition);
                            }
                        }

                        _currentInfoCard.Location = new Point(
                            DoubleUtil.DoubleToInt(infoCardPosition.X),
                            DoubleUtil.DoubleToInt(infoCardPosition.Y));

                        if (_currentInfoCard.IsOpen)
                        {
                            var infoCardWindow = InfoCardHost.GetInfoCardWindow(_currentInfoCard);
                            if (infoCardWindow != null)
                            {
                                infoCardWindow.Setup(_currentInfoCard.Location);
                                infoCardWindow.Activate();
                            }
                            return;
                        }

                        _currentInfoCard.Open();
                    }
                }
            }
        }