private CustomPopupPlacement[] PlacementCallback(Size popup, Size target, Point offset) { double x = 0, y = 0; double gap = Configurations.ToolTipTargetGapInPixels; PopupPrimaryAxis primaryAxis = PopupPrimaryAxis.None; switch (this.AttachmentSide) { case Side.Left: x = -(popup.Width + gap); y = (target.Height - popup.Height) * 0.5; primaryAxis = PopupPrimaryAxis.Horizontal; break; case Side.Right: x = target.Width + gap; y = (target.Height - popup.Height) * 0.5; primaryAxis = PopupPrimaryAxis.Horizontal; break; case Side.Top: x = (target.Width - popup.Width) * 0.5; y = -(popup.Height + gap); primaryAxis = PopupPrimaryAxis.Vertical; break; case Side.Bottom: x = (target.Width - popup.Width) * 0.5; y = target.Height + gap; primaryAxis = PopupPrimaryAxis.Vertical; break; } return(new CustomPopupPlacement[] { new CustomPopupPlacement() { Point = new Point(x, y), PrimaryAxis = primaryAxis } }); }
/// <summary> /// Constructor /// </summary> /// <param name="point">Assigns to Point</param> /// <param name="primaryAxis">Assigns to PrimaryAxis</param> public CustomPopupPlacement(Point point, PopupPrimaryAxis primaryAxis) { _point = point; _primaryAxis = primaryAxis; }
// Returns the ith possible alignment for the given PlacementMode private PointCombination GetPointCombination(PlacementMode placement, int i, out PopupPrimaryAxis axis) { Debug.Assert(i >= 0 && i < GetNumberOfCombinations(placement)); bool dropFromRight = SystemParameters.MenuDropAlignment; switch (placement) { case PlacementMode.Bottom: case PlacementMode.Mouse: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) { return(new PointCombination(InterestPoint.BottomRight, InterestPoint.TopRight)); } if (i == 1) { return(new PointCombination(InterestPoint.TopRight, InterestPoint.BottomRight)); } } else { if (i == 0) { return(new PointCombination(InterestPoint.BottomLeft, InterestPoint.TopLeft)); } if (i == 1) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft)); } } break; case PlacementMode.Top: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) { return(new PointCombination(InterestPoint.TopRight, InterestPoint.BottomRight)); } if (i == 1) { return(new PointCombination(InterestPoint.BottomRight, InterestPoint.TopRight)); } } else { if (i == 0) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft)); } if (i == 1) { return(new PointCombination(InterestPoint.BottomLeft, InterestPoint.TopLeft)); } } break; case PlacementMode.Right: case PlacementMode.Left: axis = PopupPrimaryAxis.Vertical; dropFromRight |= DropOpposite; if (dropFromRight && placement == PlacementMode.Right || !dropFromRight && placement == PlacementMode.Left) { if (i == 0) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight)); } if (i == 1) { return(new PointCombination(InterestPoint.BottomLeft, InterestPoint.BottomRight)); } if (i == 2) { return(new PointCombination(InterestPoint.TopRight, InterestPoint.TopLeft)); } if (i == 3) { return(new PointCombination(InterestPoint.BottomRight, InterestPoint.BottomLeft)); } } else { if (i == 0) { return(new PointCombination(InterestPoint.TopRight, InterestPoint.TopLeft)); } if (i == 1) { return(new PointCombination(InterestPoint.BottomRight, InterestPoint.BottomLeft)); } if (i == 2) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight)); } if (i == 3) { return(new PointCombination(InterestPoint.BottomLeft, InterestPoint.BottomRight)); } } break; case PlacementMode.Relative: case PlacementMode.RelativePoint: case PlacementMode.MousePoint: case PlacementMode.AbsolutePoint: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight)); } if (i == 1) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft)); } if (i == 2) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomRight)); } if (i == 3) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft)); } } else { if (i == 0) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft)); } if (i == 1) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight)); } if (i == 2) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft)); } if (i == 3) { return(new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomRight)); } } break; case PlacementMode.Center: axis = PopupPrimaryAxis.None; return(new PointCombination(InterestPoint.Center, InterestPoint.Center)); case PlacementMode.Absolute: case PlacementMode.Custom: default: axis = PopupPrimaryAxis.None; return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft)); } return(new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight)); }
// 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()); } }
public CustomPopupPlacement(System.Windows.Point point, PopupPrimaryAxis primaryAxis) { }
/// <summary>Initializes a new instance of the <see cref="T:System.Windows.Controls.Primitives.CustomPopupPlacement" /> structure.</summary> /// <param name="point">The <see cref="T:System.Windows.Point" /> that is relative to the <see cref="P:System.Windows.Controls.Primitives.Popup.PlacementTarget" /> where the upper-left corner of the <see cref="T:System.Windows.Controls.Primitives.Popup" /> is placed.</param> /// <param name="primaryAxis">The <see cref="T:System.Windows.Controls.Primitives.PopupPrimaryAxis" /> along which a <see cref="T:System.Windows.Controls.Primitives.Popup" /> control moves when it is obstructed by a screen edge.</param> // Token: 0x06005C81 RID: 23681 RVA: 0x001A08A4 File Offset: 0x0019EAA4 public CustomPopupPlacement(Point point, PopupPrimaryAxis primaryAxis) { this._point = point; this._primaryAxis = primaryAxis; }
// Returns the ith possible alignment for the given PlacementMode private PointCombination GetPointCombination(PlacementMode placement, int i, out PopupPrimaryAxis axis) { Debug.Assert(i >= 0 && i < GetNumberOfCombinations(placement)); bool dropFromRight = SystemParameters.MenuDropAlignment; switch (placement) { case PlacementMode.Bottom: case PlacementMode.Mouse: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) return new PointCombination(InterestPoint.BottomRight, InterestPoint.TopRight); if (i == 1) return new PointCombination(InterestPoint.TopRight, InterestPoint.BottomRight); } else { if (i == 0) return new PointCombination(InterestPoint.BottomLeft, InterestPoint.TopLeft); if (i == 1) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft); } break; case PlacementMode.Top: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) return new PointCombination(InterestPoint.TopRight, InterestPoint.BottomRight); if (i == 1) return new PointCombination(InterestPoint.BottomRight, InterestPoint.TopRight); } else { if (i == 0) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft); if (i == 1) return new PointCombination(InterestPoint.BottomLeft, InterestPoint.TopLeft); } break; case PlacementMode.Right: case PlacementMode.Left: axis = PopupPrimaryAxis.Vertical; dropFromRight |= DropOpposite; if ((dropFromRight && placement == PlacementMode.Right) || (!dropFromRight && placement == PlacementMode.Left)) { if (i == 0) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight); if (i == 1) return new PointCombination(InterestPoint.BottomLeft, InterestPoint.BottomRight); if (i == 2) return new PointCombination(InterestPoint.TopRight, InterestPoint.TopLeft); if (i == 3) return new PointCombination(InterestPoint.BottomRight, InterestPoint.BottomLeft); } else { if (i == 0) return new PointCombination(InterestPoint.TopRight, InterestPoint.TopLeft); if (i == 1) return new PointCombination(InterestPoint.BottomRight, InterestPoint.BottomLeft); if (i == 2) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight); if (i == 3) return new PointCombination(InterestPoint.BottomLeft, InterestPoint.BottomRight); } break; case PlacementMode.Relative: case PlacementMode.RelativePoint: case PlacementMode.MousePoint: case PlacementMode.AbsolutePoint: axis = PopupPrimaryAxis.Horizontal; if (dropFromRight) { if (i == 0) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight); if (i == 1) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft); if (i == 2) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomRight); if (i == 3) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft); } else { if (i == 0) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft); if (i == 1) return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight); if (i == 2) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomLeft); if (i == 3) return new PointCombination(InterestPoint.TopLeft, InterestPoint.BottomRight); } break; case PlacementMode.Center: axis = PopupPrimaryAxis.None; return new PointCombination(InterestPoint.Center, InterestPoint.Center); case PlacementMode.Absolute: case PlacementMode.Custom: default: axis = PopupPrimaryAxis.None; return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopLeft); } return new PointCombination(InterestPoint.TopLeft, InterestPoint.TopRight); }