in src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs [102:231]
private Rect Calculate(Size translatedSize,
Rect anchorRect, PopupAnchor anchor, PopupGravity 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.ContainsExclusive(anchorRect.TopLeft))
?? screens.FirstOrDefault(s => s.Bounds.Intersects(anchorRect))
?? screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(parentGeometry.TopLeft))
?? screens.FirstOrDefault(s => s.Bounds.Intersects(parentGeometry))
?? screens.FirstOrDefault();
if (targetScreen != null &&
(targetScreen.WorkingArea.Width == 0 && targetScreen.WorkingArea.Height == 0))
{
return targetScreen.Bounds;
}
return targetScreen?.WorkingArea
?? new Rect(0, 0, double.MaxValue, double.MaxValue);
}
var bounds = GetBounds();
bool FitsInBounds(Rect rc, PopupAnchor edge = PopupAnchor.AllMask)
{
if (edge.HasAllFlags(PopupAnchor.Left) && rc.X < bounds.X ||
edge.HasAllFlags(PopupAnchor.Top) && rc.Y < bounds.Y ||
edge.HasAllFlags(PopupAnchor.Right) && rc.Right > bounds.Right ||
edge.HasAllFlags(PopupAnchor.Bottom) && rc.Bottom > bounds.Bottom)
{
return false;
}
return true;
}
static bool IsValid(in Rect rc) => rc.Width > 0 && rc.Height > 0;
Rect GetUnconstrained(PopupAnchor a, PopupGravity 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, PopupAnchor.HorizontalMask)
&& constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.FlipX))
{
var flipped = GetUnconstrained(anchor.FlipX(), gravity.FlipX());
if (FitsInBounds(flipped, PopupAnchor.HorizontalMask))
geo = geo.WithX(flipped.X);
}
// If sliding is allowed, try moving the rect into the bounds
if (constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.SlideX))
{
geo = geo.WithX(Math.Max(geo.X, bounds.X));
if (geo.Right > bounds.Right)
geo = geo.WithX(bounds.Right - geo.Width);
}
// Resize the rect horizontally if allowed.
if (constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.ResizeX))
{
var unconstrainedRect = geo;
if (!FitsInBounds(unconstrainedRect, PopupAnchor.Left))
{
unconstrainedRect = unconstrainedRect.WithX(bounds.X);
}
if (!FitsInBounds(unconstrainedRect, PopupAnchor.Right))
{
unconstrainedRect = unconstrainedRect.WithWidth(bounds.Width - unconstrainedRect.X);
}
if (IsValid(unconstrainedRect))
{
geo = unconstrainedRect;
}
}
// If flipping geometry and anchor is allowed and helps, use the flipped one,
// otherwise leave it as is
if (!FitsInBounds(geo, PopupAnchor.VerticalMask)
&& constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.FlipY))
{
var flipped = GetUnconstrained(anchor.FlipY(), gravity.FlipY());
if (FitsInBounds(flipped, PopupAnchor.VerticalMask))
geo = geo.WithY(flipped.Y);
}
// If sliding is allowed, try moving the rect into the bounds
if (constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.SlideY))
{
geo = geo.WithY(Math.Max(geo.Y, bounds.Y));
if (geo.Bottom > bounds.Bottom)
geo = geo.WithY(bounds.Bottom - geo.Height);
}
// Resize the rect vertically if allowed.
if (constraintAdjustment.HasAllFlags(PopupPositionerConstraintAdjustment.ResizeY))
{
var unconstrainedRect = geo;
if (!FitsInBounds(unconstrainedRect, PopupAnchor.Top))
{
unconstrainedRect = unconstrainedRect.WithY(bounds.Y);
}
if (!FitsInBounds(unconstrainedRect, PopupAnchor.Bottom))
{
unconstrainedRect = unconstrainedRect.WithHeight(bounds.Bottom - unconstrainedRect.Y);
}
if (IsValid(unconstrainedRect))
{
geo = unconstrainedRect;
}
}
return geo;
}