|
|
|
@ -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)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|