|
|
@ -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) |
|
|
{ |
|
|
{ |
|
|
|