Browse Source

Added additional placement properties to PopupRoot.

And split `PopupPositioningEdge` into `PopupAnchor`  and `PopupGravity` so their values can be documented more clearly.
pull/3970/head
Steven Kirk 6 years ago
parent
commit
07d1f16fa0
  1. 10
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  2. 5
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  3. 97
      src/Avalonia.Controls/Primitives/Popup.cs
  4. 138
      src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
  5. 42
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
  6. 7
      src/Avalonia.Controls/Primitives/PopupRoot.cs

10
src/Avalonia.Controls/Primitives/IPopupHost.cs

@ -45,10 +45,14 @@ namespace Avalonia.Controls.Primitives
/// <param name="placement">The placement mode.</param> /// <param name="placement">The placement mode.</param>
/// <param name="offset">The offset, in device-independent pixels.</param> /// <param name="offset">The offset, in device-independent pixels.</param>
/// <param name="anchor">The anchor point.</param> /// <param name="anchor">The anchor point.</param>
/// <param name="gravity">The anchor gravity.</param> /// <param name="gravity">The popup gravity.</param>
/// <param name="rect">
/// The anchor rect. If null, the bounds of <paramref name="target"/> will be used.
/// </param>
void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, void ConfigurePosition(IVisual target, PlacementMode placement, Point offset,
PopupPositioningEdge anchor = PopupPositioningEdge.None, PopupAnchor anchor = PopupAnchor.None,
PopupPositioningEdge gravity = PopupPositioningEdge.None); PopupGravity gravity = PopupGravity.None,
Rect? rect = null);
/// <summary> /// <summary>
/// Shows the popup. /// Shows the popup.

5
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@ -71,10 +71,11 @@ namespace Avalonia.Controls.Primitives
} }
public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset,
PopupPositioningEdge anchor = PopupPositioningEdge.None, PopupPositioningEdge gravity = PopupPositioningEdge.None) PopupAnchor anchor = PopupAnchor.None, PopupGravity gravity = PopupGravity.None,
Rect? rect = null)
{ {
_positionerParameters.ConfigurePosition((TopLevel)_overlayLayer.GetVisualRoot(), target, placement, offset, anchor, _positionerParameters.ConfigurePosition((TopLevel)_overlayLayer.GetVisualRoot(), target, placement, offset, anchor,
gravity); gravity, rect);
UpdatePosition(); UpdatePosition();
} }

97
src/Avalonia.Controls/Primitives/Popup.cs

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -34,12 +35,36 @@ namespace Avalonia.Controls.Primitives
o => o.IsOpen, o => o.IsOpen,
(o, v) => o.IsOpen = v); (o, v) => o.IsOpen = v);
/// <summary>
/// Defines the <see cref="PlacementAnchor"/> property.
/// </summary>
public static readonly StyledProperty<PopupAnchor> PlacementAnchorProperty =
AvaloniaProperty.Register<Popup, PopupAnchor>(nameof(PlacementAnchor));
/// <summary>
/// Defines the <see cref="PlacementGravity"/> property.
/// </summary>
public static readonly StyledProperty<PopupGravity> PlacementGravityProperty =
AvaloniaProperty.Register<Popup, PopupGravity>(nameof(PlacementGravity));
/// <summary> /// <summary>
/// Defines the <see cref="PlacementMode"/> property. /// Defines the <see cref="PlacementMode"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<PlacementMode> PlacementModeProperty = public static readonly StyledProperty<PlacementMode> PlacementModeProperty =
AvaloniaProperty.Register<Popup, PlacementMode>(nameof(PlacementMode), defaultValue: PlacementMode.Bottom); AvaloniaProperty.Register<Popup, PlacementMode>(nameof(PlacementMode), defaultValue: PlacementMode.Bottom);
/// <summary>
/// Defines the <see cref="PlacementRect"/> property.
/// </summary>
public static readonly StyledProperty<Rect?> PlacementRectProperty =
AvaloniaProperty.Register<Popup, Rect?>(nameof(PlacementRect));
/// <summary>
/// Defines the <see cref="PlacementTarget"/> property.
/// </summary>
public static readonly StyledProperty<Control?> PlacementTargetProperty =
AvaloniaProperty.Register<Popup, Control?>(nameof(PlacementTarget));
#pragma warning disable 618 #pragma warning disable 618
/// <summary> /// <summary>
/// Defines the <see cref="ObeyScreenEdges"/> property. /// Defines the <see cref="ObeyScreenEdges"/> property.
@ -60,12 +85,6 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<double> VerticalOffsetProperty = public static readonly StyledProperty<double> VerticalOffsetProperty =
AvaloniaProperty.Register<Popup, double>(nameof(VerticalOffset)); AvaloniaProperty.Register<Popup, double>(nameof(VerticalOffset));
/// <summary>
/// Defines the <see cref="PlacementTarget"/> property.
/// </summary>
public static readonly StyledProperty<Control?> PlacementTargetProperty =
AvaloniaProperty.Register<Popup, Control?>(nameof(PlacementTarget));
/// <summary> /// <summary>
/// Defines the <see cref="StaysOpen"/> property. /// Defines the <see cref="StaysOpen"/> property.
/// </summary> /// </summary>
@ -136,6 +155,26 @@ namespace Avalonia.Controls.Primitives
set { SetAndRaise(IsOpenProperty, ref _isOpen, value); } set { SetAndRaise(IsOpenProperty, ref _isOpen, value); }
} }
/// <summary>
/// Gets or sets the anchor point on the <see cref="PlacementRect"/> when <see cref="PlacementMode"/>
/// is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
public PopupAnchor PlacementAnchor
{
get { return GetValue(PlacementAnchorProperty); }
set { SetValue(PlacementAnchorProperty, value); }
}
/// <summary>
/// Gets or sets a value which defines in what direction the popup should open
/// when <see cref="PlacementMode"/> is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
public PopupGravity PlacementGravity
{
get { return GetValue(PlacementGravityProperty); }
set { SetValue(PlacementGravityProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the placement mode of the popup in relation to the <see cref="PlacementTarget"/>. /// Gets or sets the placement mode of the popup in relation to the <see cref="PlacementTarget"/>.
/// </summary> /// </summary>
@ -145,6 +184,32 @@ namespace Avalonia.Controls.Primitives
set { SetValue(PlacementModeProperty, value); } set { SetValue(PlacementModeProperty, value); }
} }
/// <summary>
/// Gets or sets the the anchor rectangle within the parent that the popup will be placed
/// relative to when <see cref="PlacementMode"/> is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
/// <remarks>
/// The placement rect defines a rectangle relative to <see cref="PlacementTarget"/> around
/// which the popup will be opened, with <see cref="PlacementAnchor"/> determining which edge
/// of the placement target is used.
///
/// If unset, the anchor rectangle will be the bounds of the <see cref="PlacementTarget"/>.
/// </remarks>
public Rect? PlacementRect
{
get { return GetValue(PlacementRectProperty); }
set { SetValue(PlacementRectProperty, value); }
}
/// <summary>
/// Gets or sets the control that is used to determine the popup's position.
/// </summary>
public Control? PlacementTarget
{
get { return GetValue(PlacementTargetProperty); }
set { SetValue(PlacementTargetProperty, value); }
}
[Obsolete("This property has no effect")] [Obsolete("This property has no effect")]
public bool ObeyScreenEdges public bool ObeyScreenEdges
{ {
@ -153,7 +218,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <summary> /// <summary>
/// Gets or sets the Horizontal offset of the popup in relation to the <see cref="PlacementTarget"/> /// Gets or sets the Horizontal offset of the popup in relation to the <see cref="PlacementTarget"/>.
/// </summary> /// </summary>
public double HorizontalOffset public double HorizontalOffset
{ {
@ -162,7 +227,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <summary> /// <summary>
/// Gets or sets the Vertical offset of the popup in relation to the <see cref="PlacementTarget"/> /// Gets or sets the Vertical offset of the popup in relation to the <see cref="PlacementTarget"/>.
/// </summary> /// </summary>
public double VerticalOffset public double VerticalOffset
{ {
@ -170,15 +235,6 @@ namespace Avalonia.Controls.Primitives
set { SetValue(VerticalOffsetProperty, value); } set { SetValue(VerticalOffsetProperty, value); }
} }
/// <summary>
/// Gets or sets the control that is used to determine the popup's position.
/// </summary>
public Control? PlacementTarget
{
get { return GetValue(PlacementTargetProperty); }
set { SetValue(PlacementTargetProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the popup should stay open when the popup is /// Gets or sets a value indicating whether the popup should stay open when the popup is
/// pressed or loses focus. /// pressed or loses focus.
@ -251,8 +307,11 @@ namespace Avalonia.Controls.Primitives
popupHost.ConfigurePosition( popupHost.ConfigurePosition(
placementTarget, placementTarget,
PlacementMode, PlacementMode,
new Point(HorizontalOffset, VerticalOffset)); new Point(HorizontalOffset, VerticalOffset),
PlacementAnchor,
PlacementGravity,
PlacementRect);
DeferCleanup(SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied, DeferCleanup(SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied,
(x, handler) => x.TemplateApplied += handler, (x, handler) => x.TemplateApplied += handler,

138
src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs

@ -62,8 +62,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
/// </remarks> /// </remarks>
public struct PopupPositionerParameters public struct PopupPositionerParameters
{ {
private PopupPositioningEdge _gravity; private PopupGravity _gravity;
private PopupPositioningEdge _anchor; private PopupAnchor _anchor;
/// <summary> /// <summary>
/// Set the size of the popup that is to be positioned with the positioner object, in device- /// Set the size of the popup that is to be positioned with the positioner object, in device-
@ -91,7 +91,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
/// centered on the specified edge, or in the center of the anchor rectangle if no edge is /// centered on the specified edge, or in the center of the anchor rectangle if no edge is
/// specified. /// specified.
/// </remarks> /// </remarks>
public PopupPositioningEdge Anchor public PopupAnchor Anchor
{ {
get => _anchor; get => _anchor;
set set
@ -110,12 +110,12 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
/// be placed towards the specified gravity; otherwise, the popup will be centered over the /// be placed towards the specified gravity; otherwise, the popup will be centered over the
/// anchor point on any axis that had no gravity specified. /// anchor point on any axis that had no gravity specified.
/// </remarks> /// </remarks>
public PopupPositioningEdge Gravity public PopupGravity Gravity
{ {
get => _gravity; get => _gravity;
set set
{ {
PopupPositioningEdgeHelper.ValidateEdge(value); PopupPositioningEdgeHelper.ValidateGravity(value);
_gravity = value; _gravity = value;
} }
} }
@ -235,18 +235,23 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
static class PopupPositioningEdgeHelper static class PopupPositioningEdgeHelper
{ {
public static void ValidateEdge(this PopupPositioningEdge edge) public static void ValidateEdge(this PopupAnchor edge)
{ {
if (((edge & PopupPositioningEdge.Left) != 0 && (edge & PopupPositioningEdge.Right) != 0) if (((edge & PopupAnchor.Left) != 0 && (edge & PopupAnchor.Right) != 0)
|| ||
((edge & PopupPositioningEdge.Top) != 0 && (edge & PopupPositioningEdge.Bottom) != 0)) ((edge & PopupAnchor.Top) != 0 && (edge & PopupAnchor.Bottom) != 0))
throw new ArgumentException("Opposite edges specified"); throw new ArgumentException("Opposite edges specified");
} }
public static PopupPositioningEdge Flip(this PopupPositioningEdge edge) public static void ValidateGravity(this PopupGravity gravity)
{
ValidateEdge((PopupAnchor)gravity);
}
public static PopupAnchor Flip(this PopupAnchor edge)
{ {
var hmask = PopupPositioningEdge.Left | PopupPositioningEdge.Right; var hmask = PopupAnchor.Left | PopupAnchor.Right;
var vmask = PopupPositioningEdge.Top | PopupPositioningEdge.Bottom; var vmask = PopupAnchor.Top | PopupAnchor.Bottom;
if ((edge & hmask) != 0) if ((edge & hmask) != 0)
edge ^= hmask; edge ^= hmask;
if ((edge & vmask) != 0) if ((edge & vmask) != 0)
@ -254,28 +259,36 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
return edge; return edge;
} }
public static PopupPositioningEdge FlipX(this PopupPositioningEdge edge) public static PopupAnchor FlipX(this PopupAnchor edge)
{ {
if ((edge & PopupPositioningEdge.HorizontalMask) != 0) if ((edge & PopupAnchor.HorizontalMask) != 0)
edge ^= PopupPositioningEdge.HorizontalMask; edge ^= PopupAnchor.HorizontalMask;
return edge; return edge;
} }
public static PopupPositioningEdge FlipY(this PopupPositioningEdge edge) public static PopupAnchor FlipY(this PopupAnchor edge)
{ {
if ((edge & PopupPositioningEdge.VerticalMask) != 0) if ((edge & PopupAnchor.VerticalMask) != 0)
edge ^= PopupPositioningEdge.VerticalMask; edge ^= PopupAnchor.VerticalMask;
return edge; return edge;
} }
public static PopupGravity FlipX(this PopupGravity gravity)
{
return (PopupGravity)FlipX((PopupAnchor)gravity);
}
public static PopupGravity FlipY(this PopupGravity gravity)
{
return (PopupGravity)FlipY((PopupAnchor)gravity);
}
} }
/// <summary> /// <summary>
/// Defines the popup position edge for <see cref="PopupPositionerParameters.Anchor"/> and /// Defines the edges around an anchor rectangle on which a popup will open.
/// <see cref="PopupPositionerParameters.Gravity"/>.
/// </summary> /// </summary>
[Flags] [Flags]
public enum PopupPositioningEdge public enum PopupAnchor
{ {
/// <summary> /// <summary>
/// The center of the anchor rectangle. /// The center of the anchor rectangle.
@ -338,6 +351,58 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
AllMask = VerticalMask|HorizontalMask AllMask = VerticalMask|HorizontalMask
} }
/// <summary>
/// Defines the direction in which a popup will open.
/// </summary>
[Flags]
public enum PopupGravity
{
/// <summary>
/// The popup will be centered over the anchor edge.
/// </summary>
None,
/// <summary>
/// The popup will be positioned above the anchor edge
/// </summary>
Top = 1,
/// <summary>
/// The popup will be positioned below the anchor edge
/// </summary>
Bottom = 2,
/// <summary>
/// The popup will be positioned to the left of the anchor edge
/// </summary>
Left = 4,
/// <summary>
/// The popup will be positioned to the right of the anchor edge
/// </summary>
Right = 8,
/// <summary>
/// The popup will be positioned to the top-left of the anchor edge
/// </summary>
TopLeft = Top | Left,
/// <summary>
/// The popup will be positioned to the top-right of the anchor edge
/// </summary>
TopRight = Top | Right,
/// <summary>
/// The popup will be positioned to the bottom-left of the anchor edge
/// </summary>
BottomLeft = Bottom | Left,
/// <summary>
/// The popup will be positioned to the bottom-right of the anchor edge
/// </summary>
BottomRight = Bottom | Right,
}
/// <summary> /// <summary>
/// Positions an <see cref="IPopupHost"/>. /// Positions an <see cref="IPopupHost"/>.
/// </summary> /// </summary>
@ -363,7 +428,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters, public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters,
TopLevel topLevel, TopLevel topLevel,
IVisual target, PlacementMode placement, Point offset, IVisual target, PlacementMode placement, Point offset,
PopupPositioningEdge anchor, PopupPositioningEdge gravity) PopupAnchor anchor, PopupGravity gravity, Rect? rect)
{ {
// We need a better way for tracking the last pointer position // We need a better way for tracking the last pointer position
var pointer = topLevel.PointToClient(topLevel.PlatformImpl.MouseDevice.Position); var pointer = topLevel.PointToClient(topLevel.PlatformImpl.MouseDevice.Position);
@ -373,8 +438,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
if (placement == PlacementMode.Pointer) if (placement == PlacementMode.Pointer)
{ {
positionerParameters.AnchorRectangle = new Rect(pointer, new Size(1, 1)); positionerParameters.AnchorRectangle = new Rect(pointer, new Size(1, 1));
positionerParameters.Anchor = PopupPositioningEdge.TopLeft; positionerParameters.Anchor = PopupAnchor.TopLeft;
positionerParameters.Gravity = PopupPositioningEdge.BottomRight; positionerParameters.Gravity = PopupGravity.BottomRight;
} }
else else
{ {
@ -384,32 +449,33 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
if (matrix == null) if (matrix == null)
{ {
if (target.GetVisualRoot() == null) if (target.GetVisualRoot() == null)
throw new InvalidCastException("Target control is not attached to the visual tree"); throw new InvalidOperationException("Target control is not attached to the visual tree");
throw new InvalidCastException("Target control is not in the same tree as the popup parent"); throw new InvalidOperationException("Target control is not in the same tree as the popup parent");
} }
positionerParameters.AnchorRectangle = new Rect(default, target.Bounds.Size) var bounds = new Rect(default, target.Bounds.Size);
.TransformToAABB(matrix.Value); var anchorRect = rect ?? bounds;
positionerParameters.AnchorRectangle = anchorRect.Intersect(bounds).TransformToAABB(matrix.Value);
if (placement == PlacementMode.Right) if (placement == PlacementMode.Right)
{ {
positionerParameters.Anchor = PopupPositioningEdge.TopRight; positionerParameters.Anchor = PopupAnchor.TopRight;
positionerParameters.Gravity = PopupPositioningEdge.BottomRight; positionerParameters.Gravity = PopupGravity.BottomRight;
} }
else if (placement == PlacementMode.Bottom) else if (placement == PlacementMode.Bottom)
{ {
positionerParameters.Anchor = PopupPositioningEdge.BottomLeft; positionerParameters.Anchor = PopupAnchor.BottomLeft;
positionerParameters.Gravity = PopupPositioningEdge.BottomRight; positionerParameters.Gravity = PopupGravity.BottomRight;
} }
else if (placement == PlacementMode.Left) else if (placement == PlacementMode.Left)
{ {
positionerParameters.Anchor = PopupPositioningEdge.TopLeft; positionerParameters.Anchor = PopupAnchor.TopLeft;
positionerParameters.Gravity = PopupPositioningEdge.BottomLeft; positionerParameters.Gravity = PopupGravity.BottomLeft;
} }
else if (placement == PlacementMode.Top) else if (placement == PlacementMode.Top)
{ {
positionerParameters.Anchor = PopupPositioningEdge.TopLeft; positionerParameters.Anchor = PopupAnchor.TopLeft;
positionerParameters.Gravity = PopupPositioningEdge.TopRight; positionerParameters.Gravity = PopupGravity.TopRight;
} }
else if (placement == PlacementMode.AnchorAndGravity) else if (placement == PlacementMode.AnchorAndGravity)
{ {

42
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs

@ -39,38 +39,38 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
} }
private static Point GetAnchorPoint(Rect anchorRect, PopupPositioningEdge edge) private static Point GetAnchorPoint(Rect anchorRect, PopupAnchor edge)
{ {
double x, y; double x, y;
if ((edge & PopupPositioningEdge.Left) != 0) if ((edge & PopupAnchor.Left) != 0)
x = anchorRect.X; x = anchorRect.X;
else if ((edge & PopupPositioningEdge.Right) != 0) else if ((edge & PopupAnchor.Right) != 0)
x = anchorRect.Right; x = anchorRect.Right;
else else
x = anchorRect.X + anchorRect.Width / 2; x = anchorRect.X + anchorRect.Width / 2;
if ((edge & PopupPositioningEdge.Top) != 0) if ((edge & PopupAnchor.Top) != 0)
y = anchorRect.Y; y = anchorRect.Y;
else if ((edge & PopupPositioningEdge.Bottom) != 0) else if ((edge & PopupAnchor.Bottom) != 0)
y = anchorRect.Bottom; y = anchorRect.Bottom;
else else
y = anchorRect.Y + anchorRect.Height / 2; y = anchorRect.Y + anchorRect.Height / 2;
return new Point(x, y); return new Point(x, y);
} }
private static Point Gravitate(Point anchorPoint, Size size, PopupPositioningEdge gravity) private static Point Gravitate(Point anchorPoint, Size size, PopupGravity gravity)
{ {
double x, y; double x, y;
if ((gravity & PopupPositioningEdge.Left) != 0) if ((gravity & PopupGravity.Left) != 0)
x = -size.Width; x = -size.Width;
else if ((gravity & PopupPositioningEdge.Right) != 0) else if ((gravity & PopupGravity.Right) != 0)
x = 0; x = 0;
else else
x = -size.Width / 2; x = -size.Width / 2;
if ((gravity & PopupPositioningEdge.Top) != 0) if ((gravity & PopupGravity.Top) != 0)
y = -size.Height; y = -size.Height;
else if ((gravity & PopupPositioningEdge.Bottom) != 0) else if ((gravity & PopupGravity.Bottom) != 0)
y = 0; y = 0;
else else
y = -size.Height / 2; y = -size.Height / 2;
@ -89,7 +89,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
private void Update(Size translatedSize, Size originalSize, private void Update(Size translatedSize, Size originalSize,
Rect anchorRect, PopupPositioningEdge anchor, PopupPositioningEdge gravity, Rect anchorRect, PopupAnchor anchor, PopupGravity gravity,
PopupPositionerConstraintAdjustment constraintAdjustment, Point offset) PopupPositionerConstraintAdjustment constraintAdjustment, Point offset)
{ {
var parentGeometry = _popup.ParentClientAreaScreenGeometry; var parentGeometry = _popup.ParentClientAreaScreenGeometry;
@ -116,28 +116,28 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
var bounds = GetBounds(); var bounds = GetBounds();
bool FitsInBounds(Rect rc, PopupPositioningEdge edge = PopupPositioningEdge.AllMask) bool FitsInBounds(Rect rc, PopupAnchor edge = PopupAnchor.AllMask)
{ {
if ((edge & PopupPositioningEdge.Left) != 0 if ((edge & PopupAnchor.Left) != 0
&& rc.X < bounds.X) && rc.X < bounds.X)
return false; return false;
if ((edge & PopupPositioningEdge.Top) != 0 if ((edge & PopupAnchor.Top) != 0
&& rc.Y < bounds.Y) && rc.Y < bounds.Y)
return false; return false;
if ((edge & PopupPositioningEdge.Right) != 0 if ((edge & PopupAnchor.Right) != 0
&& rc.Right > bounds.Right) && rc.Right > bounds.Right)
return false; return false;
if ((edge & PopupPositioningEdge.Bottom) != 0 if ((edge & PopupAnchor.Bottom) != 0
&& rc.Bottom > bounds.Bottom) && rc.Bottom > bounds.Bottom)
return false; return false;
return true; return true;
} }
Rect GetUnconstrained(PopupPositioningEdge a, PopupPositioningEdge g) => Rect GetUnconstrained(PopupAnchor a, PopupGravity g) =>
new Rect(Gravitate(GetAnchorPoint(anchorRect, a), translatedSize, g) + offset, translatedSize); new Rect(Gravitate(GetAnchorPoint(anchorRect, a), translatedSize, g) + offset, translatedSize);
@ -145,11 +145,11 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
// If flipping geometry and anchor is allowed and helps, use the flipped one, // If flipping geometry and anchor is allowed and helps, use the flipped one,
// otherwise leave it as is // otherwise leave it as is
if (!FitsInBounds(geo, PopupPositioningEdge.HorizontalMask) if (!FitsInBounds(geo, PopupAnchor.HorizontalMask)
&& (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipX) != 0) && (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipX) != 0)
{ {
var flipped = GetUnconstrained(anchor.FlipX(), gravity.FlipX()); var flipped = GetUnconstrained(anchor.FlipX(), gravity.FlipX());
if (FitsInBounds(flipped, PopupPositioningEdge.HorizontalMask)) if (FitsInBounds(flipped, PopupAnchor.HorizontalMask))
geo = geo.WithX(flipped.X); geo = geo.WithX(flipped.X);
} }
@ -163,11 +163,11 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
// If flipping geometry and anchor is allowed and helps, use the flipped one, // If flipping geometry and anchor is allowed and helps, use the flipped one,
// otherwise leave it as is // otherwise leave it as is
if (!FitsInBounds(geo, PopupPositioningEdge.VerticalMask) if (!FitsInBounds(geo, PopupAnchor.VerticalMask)
&& (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipY) != 0) && (constraintAdjustment & PopupPositionerConstraintAdjustment.FlipY) != 0)
{ {
var flipped = GetUnconstrained(anchor.FlipY(), gravity.FlipY()); var flipped = GetUnconstrained(anchor.FlipY(), gravity.FlipY());
if (FitsInBounds(flipped, PopupPositioningEdge.VerticalMask)) if (FitsInBounds(flipped, PopupAnchor.VerticalMask))
geo = geo.WithY(flipped.Y); geo = geo.WithY(flipped.Y);
} }

7
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -82,11 +82,12 @@ namespace Avalonia.Controls.Primitives
} }
public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset,
PopupPositioningEdge anchor = PopupPositioningEdge.None, PopupAnchor anchor = PopupAnchor.None,
PopupPositioningEdge gravity = PopupPositioningEdge.None) PopupGravity gravity = PopupGravity.None,
Rect? rect = null)
{ {
_positionerParameters.ConfigurePosition(_parent, target, _positionerParameters.ConfigurePosition(_parent, target,
placement, offset, anchor, gravity); placement, offset, anchor, gravity, rect);
if (_positionerParameters.Size != default) if (_positionerParameters.Size != default)
UpdatePosition(); UpdatePosition();

Loading…
Cancel
Save