| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace Avalonia.Controls.Primitives.PopupPositioning
- {
- public interface IManagedPopupPositionerPopup
- {
- IReadOnlyList<ManagedPopupPositionerScreenInfo> Screens { get; }
- Rect ParentClientAreaScreenGeometry { get; }
- void MoveAndResize(Point devicePoint, Size virtualSize);
- Point TranslatePoint(Point pt);
- Size TranslateSize(Size size);
- }
- public class ManagedPopupPositionerScreenInfo
- {
- public Rect Bounds { get; }
- public Rect WorkingArea { get; }
- public ManagedPopupPositionerScreenInfo(Rect bounds, Rect workingArea)
- {
- Bounds = bounds;
- WorkingArea = workingArea;
- }
- }
- public class ManagedPopupPositioner : IPopupPositioner
- {
- private readonly IManagedPopupPositionerPopup _popup;
- public ManagedPopupPositioner(IManagedPopupPositionerPopup popup)
- {
- _popup = popup;
- }
- private static Point GetAnchorPoint(Rect anchorRect, PopupPositioningEdge edge)
- {
- double x, y;
- if ((edge & PopupPositioningEdge.Left) != 0)
- x = anchorRect.X;
- else if ((edge & PopupPositioningEdge.Right) != 0)
- x = anchorRect.Right;
- else
- x = anchorRect.X + anchorRect.Width / 2;
-
- if ((edge & PopupPositioningEdge.Top) != 0)
- y = anchorRect.Y;
- else if ((edge & PopupPositioningEdge.Bottom) != 0)
- y = anchorRect.Bottom;
- else
- y = anchorRect.Y + anchorRect.Height / 2;
- return new Point(x, y);
- }
- private static Point Gravitate(Point anchorPoint, Size size, PopupPositioningEdge gravity)
- {
- double x, y;
- if ((gravity & PopupPositioningEdge.Left) != 0)
- x = -size.Width;
- else if ((gravity & PopupPositioningEdge.Right) != 0)
- x = 0;
- else
- x = -size.Width / 2;
-
- if ((gravity & PopupPositioningEdge.Top) != 0)
- y = -size.Height;
- else if ((gravity & PopupPositioningEdge.Bottom) != 0)
- y = 0;
- else
- y = -size.Height / 2;
- return anchorPoint + new Point(x, y);
- }
- public void Update(PopupPositionerParameters parameters)
- {
- Update(_popup.TranslateSize(parameters.Size), parameters.Size,
- new Rect(_popup.TranslatePoint(parameters.AnchorRectangle.TopLeft),
- _popup.TranslateSize(parameters.AnchorRectangle.Size)),
- parameters.Anchor, parameters.Gravity, parameters.ConstraintAdjustment,
- _popup.TranslatePoint(parameters.Offset));
- }
-
- private void Update(Size translatedSize, Size originalSize,
- Rect anchorRect, PopupPositioningEdge anchor, PopupPositioningEdge gravity,
- PopupPositionerConstraintAdjustment constraintAdjustment, Point offset)
- {
- var parentGeometry = _popup.ParentClientAreaScreenGeometry;
- anchorRect = anchorRect.Translate(parentGeometry.TopLeft);
-
- Rect GetBounds()
- {
- var screens = _popup.Screens;
-
- var targetScreen = screens.FirstOrDefault(s => s.Bounds.Contains(anchorRect.TopLeft))
- ?? screens.FirstOrDefault(s => s.Bounds.Intersects(anchorRect))
- ?? screens.FirstOrDefault(s => s.Bounds.Contains(parentGeometry.TopLeft))
- ?? screens.FirstOrDefault(s => s.Bounds.Intersects(parentGeometry))
- ?? screens.FirstOrDefault();
- if (targetScreen != null && targetScreen.WorkingArea.IsEmpty)
- {
- return targetScreen.Bounds;
- }
-
- return targetScreen?.WorkingArea
- ?? new Rect(0, 0, double.MaxValue, double.MaxValue);
- }
- var bounds = GetBounds();
- bool FitsInBounds(Rect rc, PopupPositioningEdge edge = PopupPositioningEdge.AllMask)
- {
- if ((edge & PopupPositioningEdge.Left) != 0
- && rc.X < bounds.X)
- return false;
- if ((edge & PopupPositioningEdge.Top) != 0
- && rc.Y < bounds.Y)
- return false;
- if ((edge & PopupPositioningEdge.Right) != 0
- && rc.Right > bounds.Right)
- return false;
- if ((edge & PopupPositioningEdge.Bottom) != 0
- && rc.Bottom > bounds.Bottom)
- return false;
- return true;
- }
- Rect GetUnconstrained(PopupPositioningEdge a, PopupPositioningEdge g) =>
- new Rect(Gravitate(GetAnchorPoint(anchorRect, a), translatedSize, g) + offset, translatedSize);
- var geo = GetUnconstrained(anchor, gravity);
- // If flipping geometry and anchor is allowed and helps, use the flipped one,
- // otherwise leave it as is
- if (!FitsInBounds(geo, PopupPositioningEdge.HorizontalMask)
- && (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipX) != 0)
- {
- var flipped = GetUnconstrained(anchor.FlipX(), gravity.FlipX());
- if (FitsInBounds(flipped, PopupPositioningEdge.HorizontalMask))
- geo = geo.WithX(flipped.X);
- }
- // If sliding is allowed, try moving the rect into the bounds
- if ((constraintAdjustment & PopupPositionerConstraintAdjustment.SlideX) != 0)
- {
- geo = geo.WithX(Math.Max(geo.X, bounds.X));
- if (geo.Right > bounds.Right)
- geo = geo.WithX(bounds.Right - geo.Width);
- }
-
- // If flipping geometry and anchor is allowed and helps, use the flipped one,
- // otherwise leave it as is
- if (!FitsInBounds(geo, PopupPositioningEdge.VerticalMask)
- && (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipY) != 0)
- {
- var flipped = GetUnconstrained(anchor.FlipY(), gravity.FlipY());
- if (FitsInBounds(flipped, PopupPositioningEdge.VerticalMask))
- geo = geo.WithY(flipped.Y);
- }
- // If sliding is allowed, try moving the rect into the bounds
- if ((constraintAdjustment & PopupPositionerConstraintAdjustment.SlideY) != 0)
- {
- geo = geo.WithY(Math.Max(geo.Y, bounds.Y));
- if (geo.Bottom > bounds.Bottom)
- geo = geo.WithY(bounds.Bottom - geo.Height);
- }
- _popup.MoveAndResize(geo.TopLeft, originalSize);
- }
- }
- }
|