From 6872b2a8a46cb47eb18cdc85cdff91e7e4394db2 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 15 Jun 2021 11:53:40 +0200 Subject: [PATCH 1/7] Ensure we unsubscribe from PropertyChanged. --- src/Avalonia.Animation/AnimationInstance`1.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 0e1882ce75..b9ba49e510 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -46,9 +46,6 @@ namespace Avalonia.Animation _onCompleteAction = OnComplete; _interpolator = Interpolator; _baseClock = baseClock; - control.PropertyChanged += ControlPropertyChanged; - - UpdateNeutralValue(); FetchProperties(); } @@ -82,6 +79,7 @@ namespace Avalonia.Animation // Animation may have been stopped before it has finished. ApplyFinalFill(); + _targetControl.PropertyChanged -= ControlPropertyChanged; _timerSub?.Dispose(); _clock.PlayState = PlayState.Stop; } @@ -90,6 +88,8 @@ namespace Avalonia.Animation { _clock = new Clock(_baseClock); _timerSub = _clock.Subscribe(Step); + _targetControl.PropertyChanged += ControlPropertyChanged; + UpdateNeutralValue(); } public void Step(TimeSpan frameTick) From 038805bb26c74f8896093fb0204c73082de34268 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 16 Jun 2021 17:23:48 +0200 Subject: [PATCH 2/7] Cache property changed delegate. --- src/Avalonia.Animation/AnimationInstance`1.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index b9ba49e510..cf79640150 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -37,6 +37,7 @@ namespace Avalonia.Animation private IDisposable _timerSub; private readonly IClock _baseClock; private IClock _clock; + private EventHandler _propertyChangedDelegate; public AnimationInstance(Animation animation, Animatable control, Animator animator, IClock baseClock, Action OnComplete, Func Interpolator) { @@ -79,7 +80,7 @@ namespace Avalonia.Animation // Animation may have been stopped before it has finished. ApplyFinalFill(); - _targetControl.PropertyChanged -= ControlPropertyChanged; + _targetControl.PropertyChanged -= _propertyChangedDelegate; _timerSub?.Dispose(); _clock.PlayState = PlayState.Stop; } @@ -88,7 +89,8 @@ namespace Avalonia.Animation { _clock = new Clock(_baseClock); _timerSub = _clock.Subscribe(Step); - _targetControl.PropertyChanged += ControlPropertyChanged; + _propertyChangedDelegate ??= ControlPropertyChanged; + _targetControl.PropertyChanged += _propertyChangedDelegate; UpdateNeutralValue(); } From f10f54682161bdcfc87ea26d823924691bf90885 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Jun 2021 12:49:51 +0100 Subject: [PATCH 3/7] scale flyout transient area. --- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index 4725d17d65..ea47d15914 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -252,7 +252,9 @@ namespace Avalonia.Controls.Primitives if (Popup?.Host is PopupRoot root) { // Get the popup root bounds and convert to screen coordinates - var tmp = root.Bounds.Inflate(100); + var topLevel = root.Parent as TopLevel; + + var tmp = root.Bounds.Inflate(topLevel.PointToClient(new PixelPoint(100, 100)).X); var scPt = root.PointToScreen(tmp.TopLeft); enlargedPopupRect = new Rect(scPt.X, scPt.Y, tmp.Width, tmp.Height); } From b80ee8672954999a2c52212bff0deb5f458078eb Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Jun 2021 12:53:06 +0100 Subject: [PATCH 4/7] no need for toplevel. --- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index ea47d15914..b3b5b5238e 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -252,9 +252,7 @@ namespace Avalonia.Controls.Primitives if (Popup?.Host is PopupRoot root) { // Get the popup root bounds and convert to screen coordinates - var topLevel = root.Parent as TopLevel; - - var tmp = root.Bounds.Inflate(topLevel.PointToClient(new PixelPoint(100, 100)).X); + var tmp = root.Bounds.Inflate(root.PointToClient(new PixelPoint(100, 100)).X); var scPt = root.PointToScreen(tmp.TopLeft); enlargedPopupRect = new Rect(scPt.X, scPt.Y, tmp.Width, tmp.Height); } From 45299c15059732404ef07a88e0d2987af0186433 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Jun 2021 13:04:28 +0100 Subject: [PATCH 5/7] use renderscaling. --- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index b3b5b5238e..73f2329513 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.Logging; +using Avalonia.Rendering; #nullable enable @@ -252,7 +253,8 @@ namespace Avalonia.Controls.Primitives if (Popup?.Host is PopupRoot root) { // Get the popup root bounds and convert to screen coordinates - var tmp = root.Bounds.Inflate(root.PointToClient(new PixelPoint(100, 100)).X); + + var tmp = root.Bounds.Inflate(100 * (root as IRenderRoot).RenderScaling); var scPt = root.PointToScreen(tmp.TopLeft); enlargedPopupRect = new Rect(scPt.X, scPt.Y, tmp.Width, tmp.Height); } From b78d2a0e06808c32ef1573d7dda249fc4605f342 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 17 Jun 2021 14:44:43 +0100 Subject: [PATCH 6/7] use Pixel api types when working in screen coordinates. --- src/Avalonia.Controls/Flyouts/FlyoutBase.cs | 32 +++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index 73f2329513..e4b68c62fd 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -52,8 +52,9 @@ namespace Avalonia.Controls.Primitives private bool _isOpen; private Control? _target; private FlyoutShowMode _showMode = FlyoutShowMode.Standard; - private Rect? enlargedPopupRect; - private IDisposable? transientDisposable; + private Rect? _enlargedPopupRect; + private PixelRect? _enlargePopupRectScreenPixelRect; + private IDisposable? _transientDisposable; protected Popup? Popup { get; private set; } @@ -164,8 +165,10 @@ namespace Avalonia.Controls.Primitives Popup.IsOpen = false; // Ensure this isn't active - transientDisposable?.Dispose(); - transientDisposable = null; + _transientDisposable?.Dispose(); + _transientDisposable = null; + _enlargedPopupRect = null; + _enlargePopupRectScreenPixelRect = null; OnClosed(); } @@ -231,7 +234,7 @@ namespace Avalonia.Controls.Primitives } else if (ShowMode == FlyoutShowMode.TransientWithDismissOnPointerMoveAway) { - transientDisposable = InputManager.Instance?.Process.Subscribe(HandleTransientDismiss); + _transientDisposable = InputManager.Instance?.Process.Subscribe(HandleTransientDismiss); } } @@ -247,21 +250,20 @@ namespace Avalonia.Controls.Primitives // For windowed popups, enlargedPopupRect is in screen coordinates, // for overlay popups, its in OverlayLayer coordinates - if (enlargedPopupRect == null) + if (_enlargedPopupRect == null && _enlargePopupRectScreenPixelRect == null) { // Only do this once when the Flyout opens & cache the result if (Popup?.Host is PopupRoot root) { // Get the popup root bounds and convert to screen coordinates - var tmp = root.Bounds.Inflate(100 * (root as IRenderRoot).RenderScaling); - var scPt = root.PointToScreen(tmp.TopLeft); - enlargedPopupRect = new Rect(scPt.X, scPt.Y, tmp.Width, tmp.Height); + var tmp = root.Bounds.Inflate(100); + _enlargePopupRectScreenPixelRect = new PixelRect(root.PointToScreen(tmp.TopLeft), root.PointToScreen(tmp.BottomRight)); } else if (Popup?.Host is OverlayPopupHost host) { // Overlay popups are in OverlayLayer coordinates, just use that - enlargedPopupRect = host.Bounds.Inflate(100); + _enlargedPopupRect = host.Bounds.Inflate(100); } return; @@ -275,24 +277,18 @@ namespace Avalonia.Controls.Primitives // window will not close this (as pointer events stop), which // does match UWP var pt = pArgs.Root.PointToScreen(pArgs.Position); - if (!enlargedPopupRect?.Contains(new Point(pt.X, pt.Y)) ?? false) + if (!_enlargePopupRectScreenPixelRect?.Contains(pt) ?? false) { HideCore(false); - enlargedPopupRect = null; - transientDisposable?.Dispose(); - transientDisposable = null; } } else if (Popup?.Host is OverlayPopupHost) { // Same as above here, but just different coordinate space // so we don't need to translate - if (!enlargedPopupRect?.Contains(pArgs.Position) ?? false) + if (!_enlargedPopupRect?.Contains(pArgs.Position) ?? false) { HideCore(false); - enlargedPopupRect = null; - transientDisposable?.Dispose(); - transientDisposable = null; } } } From b37ddadf811f97dd6ebaf181460b349d1c49e5f1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 17 Jun 2021 16:31:57 +0200 Subject: [PATCH 7/7] Implement measure pass in AdornerLayer. Previously the `AdornerLayer` was falling back to the base class implementation of `MeasureOverride`, in `Canvas`. This meant that at times, adorner controls were being measured with a larger constraint than the final size given to them. `Grid` doesn't like it when this happens which causes problems with various controls. Add a measure pass to `AdornerLayer` which passes the adorned control's bounds as the constraint (which is the value that the arrange pass will use). --- .../Primitives/AdornerLayer.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 9834bf3d3b..a397608aba 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Specialized; using System.Linq; +using System.Resources; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.Utilities; @@ -45,10 +46,27 @@ namespace Avalonia.Controls.Primitives ?.AdornerLayer; } - protected override Size ArrangeOverride(Size finalSize) + protected override Size MeasureOverride(Size availableSize) { - var parent = Parent; + foreach (var child in Children) + { + var info = child.GetValue(s_adornedElementInfoProperty); + + if (info != null && info.Bounds.HasValue) + { + child.Measure(info.Bounds.Value.Bounds.Size); + } + else + { + child.Measure(availableSize); + } + } + return default; + } + + protected override Size ArrangeOverride(Size finalSize) + { foreach (var child in Children) { var info = child.GetValue(s_adornedElementInfoProperty);