|
|
@@ -4,6 +4,7 @@
|
|
|
using System;
|
|
|
using Avalonia.Controls.Platform;
|
|
|
using Avalonia.Controls.Presenters;
|
|
|
+using Avalonia.Controls.Primitives.PopupPositioning;
|
|
|
using Avalonia.Interactivity;
|
|
|
using Avalonia.Media;
|
|
|
using Avalonia.Platform;
|
|
|
@@ -18,7 +19,9 @@ namespace Avalonia.Controls.Primitives
|
|
|
/// </summary>
|
|
|
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost
|
|
|
{
|
|
|
+ private readonly TopLevel _parent;
|
|
|
private IDisposable _presenterSubscription;
|
|
|
+ private PopupPositionerParameters _positionerParameters;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Initializes static members of the <see cref="PopupRoot"/> class.
|
|
|
@@ -45,6 +48,7 @@ namespace Avalonia.Controls.Primitives
|
|
|
public PopupRoot(TopLevel parent, IAvaloniaDependencyResolver dependencyResolver)
|
|
|
: base(parent.PlatformImpl.CreatePopup(), dependencyResolver)
|
|
|
{
|
|
|
+ _parent = parent;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -74,33 +78,6 @@ namespace Avalonia.Controls.Primitives
|
|
|
/// <inheritdoc/>
|
|
|
public void Dispose() => PlatformImpl?.Dispose();
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Moves the Popups position so that it doesnt overlap screen edges.
|
|
|
- /// This method can be called immediately after Show has been called.
|
|
|
- /// </summary>
|
|
|
- public void SnapInsideScreenEdges()
|
|
|
- {
|
|
|
- var screen = (VisualRoot as WindowBase)?.Screens?.ScreenFromPoint(Position);
|
|
|
-
|
|
|
- if (screen != null)
|
|
|
- {
|
|
|
- var scaling = VisualRoot.RenderScaling;
|
|
|
- var bounds = PixelRect.FromRect(Bounds, scaling);
|
|
|
- var screenX = Position.X + bounds.Width - screen.Bounds.X;
|
|
|
- var screenY = Position.Y + bounds.Height - screen.Bounds.Y;
|
|
|
-
|
|
|
- if (screenX > screen.Bounds.Width)
|
|
|
- {
|
|
|
- Position = Position.WithX(Position.X - (screenX - screen.Bounds.Width));
|
|
|
- }
|
|
|
-
|
|
|
- if (screenY > screen.Bounds.Height)
|
|
|
- {
|
|
|
- Position = Position.WithY(Position.Y - (screenY - screen.Bounds.Height));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/// <inheritdoc/>
|
|
|
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
|
|
|
{
|
|
|
@@ -142,5 +119,85 @@ namespace Avalonia.Controls.Primitives
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ void UpdatePosition()
|
|
|
+ {
|
|
|
+ PlatformImpl?.PopupPositioner.Update(_positionerParameters);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ConfigurePosition(Control target, PlacementMode placement, Point offset,
|
|
|
+ PopupPositioningEdge anchor = PopupPositioningEdge.None,
|
|
|
+ PopupPositioningEdge gravity = PopupPositioningEdge.None)
|
|
|
+ {
|
|
|
+ // We need a better way for tracking the last pointer position
|
|
|
+ var pointer = _parent.PointToClient(_parent.PlatformImpl.MouseDevice.Position);
|
|
|
+
|
|
|
+ _positionerParameters.Offset = offset;
|
|
|
+ _positionerParameters.ConstraintAdjustment = PopupPositionerConstraintAdjustment.All;
|
|
|
+ if (placement == PlacementMode.Pointer)
|
|
|
+ {
|
|
|
+ _positionerParameters.AnchorRectangle = new Rect(pointer, new Size(1, 1));
|
|
|
+ _positionerParameters.Anchor = PopupPositioningEdge.BottomRight;
|
|
|
+ _positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (target == null)
|
|
|
+ throw new InvalidOperationException("Placement mode is not Pointer and PlacementTarget is null");
|
|
|
+ var matrix = target.TransformToVisual(_parent);
|
|
|
+ if (matrix == null)
|
|
|
+ throw new InvalidCastException("Target control is not in the same tree as the popup parent");
|
|
|
+
|
|
|
+ _positionerParameters.AnchorRectangle = new Rect(default, target.Bounds.Size)
|
|
|
+ .TransformToAABB(matrix.Value);
|
|
|
+
|
|
|
+ if (placement == PlacementMode.Right)
|
|
|
+ {
|
|
|
+ _positionerParameters.Anchor = PopupPositioningEdge.TopRight;
|
|
|
+ _positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ }
|
|
|
+ else if (placement == PlacementMode.Bottom)
|
|
|
+ {
|
|
|
+ _positionerParameters.Anchor = PopupPositioningEdge.BottomLeft;
|
|
|
+ _positionerParameters.Gravity = PopupPositioningEdge.BottomRight;
|
|
|
+ }
|
|
|
+ else if (placement == PlacementMode.Left)
|
|
|
+ {
|
|
|
+ _positionerParameters.Anchor = PopupPositioningEdge.TopLeft;
|
|
|
+ _positionerParameters.Gravity = PopupPositioningEdge.BottomLeft;
|
|
|
+ }
|
|
|
+ else if (placement == PlacementMode.Top)
|
|
|
+ {
|
|
|
+ _positionerParameters.Anchor = PopupPositioningEdge.TopLeft;
|
|
|
+ _positionerParameters.Gravity = PopupPositioningEdge.TopRight;
|
|
|
+ }
|
|
|
+ else if (placement == PlacementMode.AnchorAndGravity)
|
|
|
+ {
|
|
|
+ _positionerParameters.Anchor = anchor;
|
|
|
+ _positionerParameters.Gravity = gravity;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ throw new InvalidOperationException("Invalid value for Popup.PlacementMode");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_positionerParameters.Size != default)
|
|
|
+ UpdatePosition();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Carries out the arrange pass of the window.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="finalSize">The final window size.</param>
|
|
|
+ /// <returns>The <paramref name="finalSize"/> parameter unchanged.</returns>
|
|
|
+ protected override Size ArrangeOverride(Size finalSize)
|
|
|
+ {
|
|
|
+ using (BeginAutoSizing())
|
|
|
+ {
|
|
|
+ _positionerParameters.Size = finalSize;
|
|
|
+ UpdatePosition();
|
|
|
+ }
|
|
|
+
|
|
|
+ return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size));
|
|
|
+ }
|
|
|
}
|
|
|
}
|