|
@@ -50,46 +50,48 @@ using Avalonia.VisualTree;
|
|
|
namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
{
|
|
|
/// <summary>
|
|
|
- ///
|
|
|
- /// The IPopupPositioner provides a collection of rules for the placement of a
|
|
|
- /// a popup relative to its parent. Rules can be defined to ensure
|
|
|
- /// the popup remains within the visible area's borders, and to
|
|
|
- /// specify how the popup changes its position, such as sliding along
|
|
|
- /// an axis, or flipping around a rectangle. These positioner-created rules are
|
|
|
- /// constrained by the requirement that a popup must intersect with or
|
|
|
- /// be at least partially adjacent to its parent surface.
|
|
|
+ /// Provides positioning parameters to <see cref="IPopupPositioner"/>.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// The IPopupPositioner provides a collection of rules for the placement of a a popup relative
|
|
|
+ /// to its parent. Rules can be defined to ensure the popup remains within the visible area's
|
|
|
+ /// borders, and to specify how the popup changes its position, such as sliding along an axis,
|
|
|
+ /// or flipping around a rectangle. These positioner-created rules are constrained by the
|
|
|
+ /// requirement that a popup must intersect with or be at least partially adjacent to its parent
|
|
|
+ /// surface.
|
|
|
+ /// </remarks>
|
|
|
public struct PopupPositionerParameters
|
|
|
{
|
|
|
- private PopupPositioningEdge _gravity;
|
|
|
- private PopupPositioningEdge _anchor;
|
|
|
+ private PopupGravity _gravity;
|
|
|
+ private PopupAnchor _anchor;
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Set the size of the popup that is to be positioned with the positioner
|
|
|
- /// object. The size is in scaled coordinates.
|
|
|
+ /// Set the size of the popup that is to be positioned with the positioner object, in device-
|
|
|
+ /// independent pixels.
|
|
|
/// </summary>
|
|
|
public Size Size { get; set; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Specify the anchor rectangle within the parent that the popup
|
|
|
- /// will be placed relative to. The rectangle is relative to the
|
|
|
- /// parent geometry
|
|
|
- ///
|
|
|
- /// The anchor rectangle may not extend outside the window geometry of the
|
|
|
- /// popup's parent. The anchor rectangle is in scaled coordinates
|
|
|
+ /// Specifies the anchor rectangle within the parent that the popup will be placed relative
|
|
|
+ /// to, in device-independent pixels.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// The rectangle is relative to the parent geometry and may not extend outside the window
|
|
|
+ /// geometry of the popup's parent.
|
|
|
+ /// </remarks>
|
|
|
public Rect AnchorRectangle { get; set; }
|
|
|
|
|
|
-
|
|
|
/// <summary>
|
|
|
- /// Defines the anchor point for the anchor rectangle. The specified anchor
|
|
|
- /// is used derive an anchor point that the popup will be
|
|
|
- /// positioned relative to. If a corner anchor is set (e.g. 'TopLeft' or
|
|
|
- /// 'BottomRight'), the anchor point will be at the specified corner;
|
|
|
- /// otherwise, the derived anchor point will be centered on the specified
|
|
|
- /// edge, or in the center of the anchor rectangle if no edge is specified.
|
|
|
+ /// Defines the anchor point for the anchor rectangle.
|
|
|
/// </summary>
|
|
|
- public PopupPositioningEdge Anchor
|
|
|
+ /// <remarks>
|
|
|
+ /// The specified anchor is used derive an anchor point that the popup will be positioned
|
|
|
+ /// relative to. If a corner anchor is set (e.g. 'TopLeft' or 'BottomRight'), the anchor
|
|
|
+ /// point will be at the specified corner; otherwise, the derived anchor point will be
|
|
|
+ /// centered on the specified edge, or in the center of the anchor rectangle if no edge is
|
|
|
+ /// specified.
|
|
|
+ /// </remarks>
|
|
|
+ public PopupAnchor Anchor
|
|
|
{
|
|
|
get => _anchor;
|
|
|
set
|
|
@@ -100,66 +102,70 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Defines in what direction a popup should be positioned, relative to
|
|
|
- /// the anchor point of the parent. If a corner gravity is
|
|
|
- /// specified (e.g. 'BottomRight' or 'TopLeft'), then the popup
|
|
|
- /// will be placed towards the specified gravity; otherwise, the popup
|
|
|
- /// will be centered over the anchor point on any axis that had no
|
|
|
- /// gravity specified.
|
|
|
+ /// Defines in what direction a popup should be positioned, relative to the anchor point of
|
|
|
+ /// the parent.
|
|
|
/// </summary>
|
|
|
- public PopupPositioningEdge Gravity
|
|
|
+ /// <remarks>
|
|
|
+ /// If a corner gravity is specified (e.g. 'BottomRight' or 'TopLeft'), then the popup will
|
|
|
+ /// be placed towards the specified gravity; otherwise, the popup will be centered over the
|
|
|
+ /// anchor point on any axis that had no gravity specified.
|
|
|
+ /// </remarks>
|
|
|
+ public PopupGravity Gravity
|
|
|
{
|
|
|
get => _gravity;
|
|
|
set
|
|
|
{
|
|
|
- PopupPositioningEdgeHelper.ValidateEdge(value);
|
|
|
+ PopupPositioningEdgeHelper.ValidateGravity(value);
|
|
|
_gravity = value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Specify how the popup should be positioned if the originally intended
|
|
|
- /// position caused the popup to be constrained, meaning at least
|
|
|
- /// partially outside positioning boundaries set by the positioner. The
|
|
|
- /// adjustment is set by constructing a bitmask describing the adjustment to
|
|
|
- /// be made when the popup is constrained on that axis.
|
|
|
+ /// Specify how the popup should be positioned if the originally intended position caused
|
|
|
+ /// the popup to be constrained.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Adjusts the popup position if the intended position caused the popup to be constrained;
|
|
|
+ /// meaning at least partially outside positioning boundaries set by the positioner. The
|
|
|
+ /// adjustment is set by constructing a bitmask describing the adjustment to be made when
|
|
|
+ /// the popup is constrained on that axis.
|
|
|
///
|
|
|
- /// If no bit for one axis is set, the positioner will assume that the child
|
|
|
- /// surface should not change its position on that axis when constrained.
|
|
|
+ /// If no bit for one axis is set, the positioner will assume that the child surface should
|
|
|
+ /// not change its position on that axis when constrained.
|
|
|
///
|
|
|
- /// If more than one bit for one axis is set, the order of how adjustments
|
|
|
- /// are applied is specified in the corresponding adjustment descriptions.
|
|
|
+ /// If more than one bit for one axis is set, the order of how adjustments are applied is
|
|
|
+ /// specified in the corresponding adjustment descriptions.
|
|
|
///
|
|
|
/// The default adjustment is none.
|
|
|
- /// </summary>
|
|
|
+ /// </remarks>
|
|
|
public PopupPositionerConstraintAdjustment ConstraintAdjustment { get; set; }
|
|
|
-
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Specify the popup position offset relative to the position of the
|
|
|
- /// anchor on the anchor rectangle and the anchor on the popup. For
|
|
|
- /// example if the anchor of the anchor rectangle is at (x, y), the popup
|
|
|
- /// has the gravity bottom|right, and the offset is (ox, oy), the calculated
|
|
|
- /// surface position will be (x + ox, y + oy). The offset position of the
|
|
|
- /// surface is the one used for constraint testing. See
|
|
|
- /// set_constraint_adjustment.
|
|
|
- ///
|
|
|
- /// An example use case is placing a popup menu on top of a user interface
|
|
|
- /// element, while aligning the user interface element of the parent surface
|
|
|
- /// with some user interface element placed somewhere in the popup.
|
|
|
+ /// anchor on the anchor rectangle and the anchor on the popup.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// For example if the anchor of the anchor rectangle is at (x, y), the popup has the
|
|
|
+ /// gravity bottom|right, and the offset is (ox, oy), the calculated surface position will
|
|
|
+ /// be (x + ox, y + oy). The offset position of the surface is the one used for constraint
|
|
|
+ /// testing. See set_constraint_adjustment.
|
|
|
+ ///
|
|
|
+ /// An example use case is placing a popup menu on top of a user interface element, while
|
|
|
+ /// aligning the user interface element of the parent surface with some user interface
|
|
|
+ /// element placed somewhere in the popup.
|
|
|
+ /// </remarks>
|
|
|
public Point Offset { get; set; }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/// <summary>
|
|
|
- /// The constraint adjustment value define ways how popup position will
|
|
|
- /// be adjusted if the unadjusted position would result in the popup
|
|
|
- /// being partly constrained.
|
|
|
- ///
|
|
|
- /// Whether a popup is considered 'constrained' is left to the positioner
|
|
|
- /// to determine. For example, the popup may be partly outside the
|
|
|
- /// target platform defined 'work area', thus necessitating the popup's
|
|
|
- /// position be adjusted until it is entirely inside the work area.
|
|
|
+ /// Defines how a popup position will be adjusted if the unadjusted position would result in
|
|
|
+ /// the popup being partly constrained.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Whether a popup is considered 'constrained' is left to the positioner to determine. For
|
|
|
+ /// example, the popup may be partly outside the target platform defined 'work area', thus
|
|
|
+ /// necessitating the popup's position be adjusted until it is entirely inside the work area.
|
|
|
+ /// </remarks>
|
|
|
[Flags]
|
|
|
public enum PopupPositionerConstraintAdjustment
|
|
|
{
|
|
@@ -171,79 +177,97 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
|
|
|
/// <summary>
|
|
|
/// Slide the surface along the x axis until it is no longer constrained.
|
|
|
- /// First try to slide towards the direction of the gravity on the x axis
|
|
|
- /// until either the edge in the opposite direction of the gravity is
|
|
|
- /// unconstrained or the edge in the direction of the gravity is
|
|
|
- /// constrained.
|
|
|
- ///
|
|
|
- /// Then try to slide towards the opposite direction of the gravity on the
|
|
|
- /// x axis until either the edge in the direction of the gravity is
|
|
|
- /// unconstrained or the edge in the opposite direction of the gravity is
|
|
|
- /// constrained.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// First try to slide towards the direction of the gravity on the x axis until either the
|
|
|
+ /// edge in the opposite direction of the gravity is unconstrained or the edge in the
|
|
|
+ /// direction of the gravity is constrained.
|
|
|
+ ///
|
|
|
+ /// Then try to slide towards the opposite direction of the gravity on the x axis until
|
|
|
+ /// either the edge in the direction of the gravity is unconstrained or the edge in the
|
|
|
+ /// opposite direction of the gravity is constrained.
|
|
|
+ /// </remarks>
|
|
|
SlideX = 1,
|
|
|
|
|
|
-
|
|
|
/// <summary>
|
|
|
- /// Slide the surface along the y axis until it is no longer constrained.
|
|
|
- ///
|
|
|
- /// First try to slide towards the direction of the gravity on the y axis
|
|
|
- /// until either the edge in the opposite direction of the gravity is
|
|
|
- /// unconstrained or the edge in the direction of the gravity is
|
|
|
- /// constrained.
|
|
|
- ///
|
|
|
- /// Then try to slide towards the opposite direction of the gravity on the
|
|
|
- /// y axis until either the edge in the direction of the gravity is
|
|
|
- /// unconstrained or the edge in the opposite direction of the gravity is
|
|
|
- /// constrained.
|
|
|
- /// */
|
|
|
+ /// Slide the surface along the y axis until it is no longer constrained.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// First try to slide towards the direction of the gravity on the y axis until either the
|
|
|
+ /// edge in the opposite direction of the gravity is unconstrained or the edge in the
|
|
|
+ /// direction of the gravity is constrained.
|
|
|
+ ///
|
|
|
+ /// Then try to slide towards the opposite direction of the gravity on the y axis until
|
|
|
+ /// either the edge in the direction of the gravity is unconstrained or the edge in the
|
|
|
+ /// opposite direction of the gravity is constrained.
|
|
|
+ /// </remarks>
|
|
|
SlideY = 2,
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Invert the anchor and gravity on the x axis if the surface is
|
|
|
- /// constrained on the x axis. For example, if the left edge of the
|
|
|
- /// surface is constrained, the gravity is 'left' and the anchor is
|
|
|
- /// 'left', change the gravity to 'right' and the anchor to 'right'.
|
|
|
- ///
|
|
|
- /// If the adjusted position also ends up being constrained, the resulting
|
|
|
- /// position of the flip_x adjustment will be the one before the
|
|
|
- /// adjustment.
|
|
|
+ /// Invert the anchor and gravity on the x axis if the surface is constrained on the x axis.
|
|
|
/// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// For example, if the left edge of the surface is constrained, the gravity is 'left' and
|
|
|
+ /// the anchor is 'left', change the gravity to 'right' and the anchor to 'right'.
|
|
|
+ ///
|
|
|
+ /// If the adjusted position also ends up being constrained, the resulting position of the
|
|
|
+ /// FlipX adjustment will be the one before the adjustment.
|
|
|
+ /// /// </remarks>
|
|
|
FlipX = 4,
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Invert the anchor and gravity on the y axis if the surface is
|
|
|
- /// constrained on the y axis. For example, if the bottom edge of the
|
|
|
- /// surface is constrained, the gravity is 'bottom' and the anchor is
|
|
|
- /// 'bottom', change the gravity to 'top' and the anchor to 'top'.
|
|
|
+ /// Invert the anchor and gravity on the y axis if the surface is constrained on the y axis.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// For example, if the bottom edge of the surface is constrained, the gravity is 'bottom'
|
|
|
+ /// and the anchor is 'bottom', change the gravity to 'top' and the anchor to 'top'.
|
|
|
///
|
|
|
- /// The adjusted position is calculated given the original anchor
|
|
|
- /// rectangle and offset, but with the new flipped anchor and gravity
|
|
|
- /// values.
|
|
|
+ /// The adjusted position is calculated given the original anchor rectangle and offset, but
|
|
|
+ /// with the new flipped anchor and gravity values.
|
|
|
///
|
|
|
- /// If the adjusted position also ends up being constrained, the resulting
|
|
|
- /// position of the flip_y adjustment will be the one before the
|
|
|
- /// adjustment.
|
|
|
- /// </summary>
|
|
|
+ /// If the adjusted position also ends up being constrained, the resulting position of the
|
|
|
+ /// FlipY adjustment will be the one before the adjustment.
|
|
|
+ /// </remarks>
|
|
|
FlipY = 8,
|
|
|
- All = SlideX|SlideY|FlipX|FlipY
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Horizontally resize the surface
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Resize the surface horizontally so that it is completely unconstrained.
|
|
|
+ /// </remarks>
|
|
|
+ ResizeX = 16,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Vertically resize the surface
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// Resize the surface vertically so that it is completely unconstrained.
|
|
|
+ /// </remarks>
|
|
|
+ ResizeY = 16,
|
|
|
+
|
|
|
+ All = SlideX|SlideY|FlipX|FlipY|ResizeX|ResizeY
|
|
|
}
|
|
|
|
|
|
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");
|
|
|
}
|
|
|
|
|
|
- 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 vmask = PopupPositioningEdge.Top | PopupPositioningEdge.Bottom;
|
|
|
+ var hmask = PopupAnchor.Left | PopupAnchor.Right;
|
|
|
+ var vmask = PopupAnchor.Top | PopupAnchor.Bottom;
|
|
|
if ((edge & hmask) != 0)
|
|
|
edge ^= hmask;
|
|
|
if ((edge & vmask) != 0)
|
|
@@ -251,43 +275,167 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
return edge;
|
|
|
}
|
|
|
|
|
|
- public static PopupPositioningEdge FlipX(this PopupPositioningEdge edge)
|
|
|
+ public static PopupAnchor FlipX(this PopupAnchor edge)
|
|
|
{
|
|
|
- if ((edge & PopupPositioningEdge.HorizontalMask) != 0)
|
|
|
- edge ^= PopupPositioningEdge.HorizontalMask;
|
|
|
+ if ((edge & PopupAnchor.HorizontalMask) != 0)
|
|
|
+ edge ^= PopupAnchor.HorizontalMask;
|
|
|
return edge;
|
|
|
}
|
|
|
|
|
|
- public static PopupPositioningEdge FlipY(this PopupPositioningEdge edge)
|
|
|
+ public static PopupAnchor FlipY(this PopupAnchor edge)
|
|
|
{
|
|
|
- if ((edge & PopupPositioningEdge.VerticalMask) != 0)
|
|
|
- edge ^= PopupPositioningEdge.VerticalMask;
|
|
|
+ if ((edge & PopupAnchor.VerticalMask) != 0)
|
|
|
+ edge ^= PopupAnchor.VerticalMask;
|
|
|
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>
|
|
|
+ /// Defines the edges around an anchor rectangle on which a popup will open.
|
|
|
+ /// </summary>
|
|
|
[Flags]
|
|
|
- public enum PopupPositioningEdge
|
|
|
+ public enum PopupAnchor
|
|
|
{
|
|
|
+ /// <summary>
|
|
|
+ /// The center of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
None,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The top edge of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
Top = 1,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The bottom edge of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
Bottom = 2,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The left edge of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
Left = 4,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The right edge of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
Right = 8,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The top-left corner of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
TopLeft = Top | Left,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The top-right corner of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
TopRight = Top | Right,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The bottom-left corner of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
BottomLeft = Bottom | Left,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The bottom-right corner of the anchor rectangle.
|
|
|
+ /// </summary>
|
|
|
BottomRight = Bottom | Right,
|
|
|
|
|
|
-
|
|
|
+ /// <summary>
|
|
|
+ /// A mask for the vertical component flags.
|
|
|
+ /// </summary>
|
|
|
VerticalMask = Top | Bottom,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// A mask for the horizontal component flags.
|
|
|
+ /// </summary>
|
|
|
HorizontalMask = Left | Right,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// A mask for all flags.
|
|
|
+ /// </summary>
|
|
|
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>
|
|
|
+ /// Positions an <see cref="IPopupHost"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// <see cref="IPopupPositioner"/> is an abstraction of the wayland xdg_positioner spec.
|
|
|
+ ///
|
|
|
+ /// The popup positioner implementation is determined by the platform implementation. A default
|
|
|
+ /// managed implementation is provided in <see cref="ManagedPopupPositioner"/> for platforms
|
|
|
+ /// on which popups can be arbitrarily positioned.
|
|
|
+ /// </remarks>
|
|
|
public interface IPopupPositioner
|
|
|
{
|
|
|
+ /// <summary>
|
|
|
+ /// Updates the position of the associated <see cref="IPopupHost"/> according to the
|
|
|
+ /// specified parameters.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="parameters">The positioning parameters.</param>
|
|
|
void Update(PopupPositionerParameters parameters);
|
|
|
}
|
|
|
|
|
@@ -296,18 +444,19 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters,
|
|
|
TopLevel topLevel,
|
|
|
IVisual target, PlacementMode placement, Point offset,
|
|
|
- PopupPositioningEdge anchor, PopupPositioningEdge gravity)
|
|
|
+ PopupAnchor anchor, PopupGravity gravity,
|
|
|
+ PopupPositionerConstraintAdjustment constraintAdjustment, Rect? rect)
|
|
|
{
|
|
|
// We need a better way for tracking the last pointer position
|
|
|
var pointer = topLevel.PointToClient(topLevel.PlatformImpl.MouseDevice.Position);
|
|
|
|
|
|
positionerParameters.Offset = offset;
|
|
|
- positionerParameters.ConstraintAdjustment = PopupPositionerConstraintAdjustment.All;
|
|
|
+ positionerParameters.ConstraintAdjustment = constraintAdjustment;
|
|
|
if (placement == PlacementMode.Pointer)
|
|
|
{
|
|
|
positionerParameters.AnchorRectangle = new Rect(pointer, new Size(1, 1));
|
|
|
- positionerParameters.Anchor = PopupPositioningEdge.TopLeft;
|
|
|
- positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ positionerParameters.Anchor = PopupAnchor.TopLeft;
|
|
|
+ positionerParameters.Gravity = PopupGravity.BottomRight;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -317,32 +466,33 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
|
|
|
if (matrix == null)
|
|
|
{
|
|
|
if (target.GetVisualRoot() == null)
|
|
|
- throw new InvalidCastException("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 attached to the visual tree");
|
|
|
+ throw new InvalidOperationException("Target control is not in the same tree as the popup parent");
|
|
|
}
|
|
|
|
|
|
- positionerParameters.AnchorRectangle = new Rect(default, target.Bounds.Size)
|
|
|
- .TransformToAABB(matrix.Value);
|
|
|
+ var bounds = new Rect(default, target.Bounds.Size);
|
|
|
+ var anchorRect = rect ?? bounds;
|
|
|
+ positionerParameters.AnchorRectangle = anchorRect.Intersect(bounds).TransformToAABB(matrix.Value);
|
|
|
|
|
|
if (placement == PlacementMode.Right)
|
|
|
{
|
|
|
- positionerParameters.Anchor = PopupPositioningEdge.TopRight;
|
|
|
- positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ positionerParameters.Anchor = PopupAnchor.TopRight;
|
|
|
+ positionerParameters.Gravity = PopupGravity.BottomRight;
|
|
|
}
|
|
|
else if (placement == PlacementMode.Bottom)
|
|
|
{
|
|
|
- positionerParameters.Anchor = PopupPositioningEdge.BottomLeft;
|
|
|
- positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ positionerParameters.Anchor = PopupAnchor.BottomLeft;
|
|
|
+ positionerParameters.Gravity = PopupGravity.BottomRight;
|
|
|
}
|
|
|
else if (placement == PlacementMode.Left)
|
|
|
{
|
|
|
- positionerParameters.Anchor = PopupPositioningEdge.TopLeft;
|
|
|
- positionerParameters.Gravity = PopupPositioningEdge.BottomLeft;
|
|
|
+ positionerParameters.Anchor = PopupAnchor.TopLeft;
|
|
|
+ positionerParameters.Gravity = PopupGravity.BottomLeft;
|
|
|
}
|
|
|
else if (placement == PlacementMode.Top)
|
|
|
{
|
|
|
- positionerParameters.Anchor = PopupPositioningEdge.TopLeft;
|
|
|
- positionerParameters.Gravity = PopupPositioningEdge.TopRight;
|
|
|
+ positionerParameters.Anchor = PopupAnchor.TopLeft;
|
|
|
+ positionerParameters.Gravity = PopupGravity.TopRight;
|
|
|
}
|
|
|
else if (placement == PlacementMode.AnchorAndGravity)
|
|
|
{
|