diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 1329d58dba..762d8d37a6 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -124,10 +124,8 @@ namespace Avalonia.Controls.Primitives }, DispatcherPriority.Layout); } - Point IManagedPopupPositionerPopup.TranslatePoint(Point pt) => pt; - - Size IManagedPopupPositionerPopup.TranslateSize(Size size) => size; - + double IManagedPopupPositionerPopup.Scaling => 1; + public static IPopupHost CreatePopupHost(IVisual target, IAvaloniaDependencyResolver dependencyResolver) { var platform = (target.GetVisualRoot() as TopLevel)?.PlatformImpl?.CreatePopup(); diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 0c81548231..ac4f805174 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -50,7 +50,8 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty PlacementConstraintAdjustmentProperty = AvaloniaProperty.Register( nameof(PlacementConstraintAdjustment), - PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY); + PopupPositionerConstraintAdjustment.FlipX | PopupPositionerConstraintAdjustment.FlipY | + PopupPositionerConstraintAdjustment.ResizeX | PopupPositionerConstraintAdjustment.ResizeY); /// /// Defines the property. diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 306ea69846..aed7dff0fe 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -230,7 +230,23 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// FlipY = 8, - All = SlideX|SlideY|FlipX|FlipY + /// + /// Horizontally resize the surface + /// + /// + /// Resize the surface horizontally so that it is completely unconstrained. + /// + ResizeX = 16, + + /// + /// Vertically resize the surface + /// + /// + /// Resize the surface vertically so that it is completely unconstrained. + /// + ResizeY = 16, + + All = SlideX|SlideY|FlipX|FlipY|ResizeX|ResizeY } static class PopupPositioningEdgeHelper diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs index 04167bbd15..8c464c7aad 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Transactions; namespace Avalonia.Controls.Primitives.PopupPositioning { @@ -8,9 +9,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning { IReadOnlyList Screens { get; } Rect ParentClientAreaScreenGeometry { get; } + double Scaling { get; } void MoveAndResize(Point devicePoint, Size virtualSize); - Point TranslatePoint(Point pt); - Size TranslateSize(Size size); } public class ManagedPopupPositionerScreenInfo @@ -79,16 +79,23 @@ namespace Avalonia.Controls.Primitives.PopupPositioning public void Update(PopupPositionerParameters parameters) { - - Update(_popup.TranslateSize(parameters.Size), parameters.Size, - new Rect(_popup.TranslatePoint(parameters.AnchorRectangle.TopLeft), - _popup.TranslateSize(parameters.AnchorRectangle.Size)), - parameters.Anchor, parameters.Gravity, parameters.ConstraintAdjustment, - _popup.TranslatePoint(parameters.Offset)); + var rect = Calculate( + parameters.Size * _popup.Scaling, + new Rect( + parameters.AnchorRectangle.TopLeft * _popup.Scaling, + parameters.AnchorRectangle.Size * _popup.Scaling), + parameters.Anchor, + parameters.Gravity, + parameters.ConstraintAdjustment, + parameters.Offset * _popup.Scaling); + + _popup.MoveAndResize( + rect.Position, + rect.Size / _popup.Scaling); } - private void Update(Size translatedSize, Size originalSize, + private Rect Calculate(Size translatedSize, Rect anchorRect, PopupAnchor anchor, PopupGravity gravity, PopupPositionerConstraintAdjustment constraintAdjustment, Point offset) { @@ -137,6 +144,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning return true; } + static bool IsValid(in Rect rc) => rc.Width > 0 && rc.Height > 0; + Rect GetUnconstrained(PopupAnchor a, PopupGravity g) => new Rect(Gravitate(GetAnchorPoint(anchorRect, a), translatedSize, g) + offset, translatedSize); @@ -161,6 +170,27 @@ namespace Avalonia.Controls.Primitives.PopupPositioning geo = geo.WithX(bounds.Right - geo.Width); } + // Resize the rect horizontally if allowed. + if ((constraintAdjustment & PopupPositionerConstraintAdjustment.ResizeX) != 0) + { + var unconstrainedRect = geo; + + if (!FitsInBounds(unconstrainedRect, PopupAnchor.Left)) + { + unconstrainedRect = unconstrainedRect.WithX(bounds.X); + } + + if (!FitsInBounds(unconstrainedRect, PopupAnchor.Right)) + { + unconstrainedRect = unconstrainedRect.WithWidth(bounds.Width - unconstrainedRect.X); + } + + if (IsValid(unconstrainedRect)) + { + geo = unconstrainedRect; + } + } + // If flipping geometry and anchor is allowed and helps, use the flipped one, // otherwise leave it as is if (!FitsInBounds(geo, PopupAnchor.VerticalMask) @@ -179,7 +209,28 @@ namespace Avalonia.Controls.Primitives.PopupPositioning geo = geo.WithY(bounds.Bottom - geo.Height); } - _popup.MoveAndResize(geo.TopLeft, originalSize); + // Resize the rect vertically if allowed. + if ((constraintAdjustment & PopupPositionerConstraintAdjustment.ResizeY) != 0) + { + var unconstrainedRect = geo; + + if (!FitsInBounds(unconstrainedRect, PopupAnchor.Top)) + { + unconstrainedRect = unconstrainedRect.WithY(bounds.Y); + } + + if (!FitsInBounds(unconstrainedRect, PopupAnchor.Bottom)) + { + unconstrainedRect = unconstrainedRect.WithHeight(bounds.Height - unconstrainedRect.Y); + } + + if (IsValid(unconstrainedRect)) + { + geo = unconstrainedRect; + } + } + + return geo; } } } diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs index 8e7e429a73..b0e3d1ab08 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning { // Popup positioner operates with abstract coordinates, but in our case they are pixel ones var point = _parent.PointToScreen(default); - var size = TranslateSize(_parent.ClientSize); + var size = _parent.ClientSize * Scaling; return new Rect(point.X, point.Y, size.Width, size.Height); } @@ -43,8 +43,6 @@ namespace Avalonia.Controls.Primitives.PopupPositioning _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.Scaling); } - public virtual Point TranslatePoint(Point pt) => pt * _parent.Scaling; - - public virtual Size TranslateSize(Size size) => size * _parent.Scaling; + public virtual double Scaling => _parent.Scaling; } } diff --git a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs index e81c8853e8..8aa9b1a122 100644 --- a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs @@ -9,8 +9,7 @@ namespace Avalonia.Native { } - public override Point TranslatePoint(Point pt) => pt; - public override Size TranslateSize(Size size) => size; + public override double Scaling => 1; } }