From 094cbb899f1dca2d201e1617f73a148259989c12 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 3 Jun 2020 18:41:32 +0200 Subject: [PATCH] Adjust `EffectiveViewportChanged` calculation. Instead of simply reading old `TranslatedBounds` value when `Bounds` changes, use the clip and transform from the previous `TranslatedBounds` with the new `Bounds`. --- .../Repeater/ViewportManager.cs | 77 ++++++++++++++++--- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index e473954e60..fe67a1449d 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -5,10 +5,13 @@ using System; using System.Collections.Generic; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Layout; using Avalonia.Logging; +using Avalonia.Media; +using Avalonia.Reactive; using Avalonia.Threading; using Avalonia.VisualTree; @@ -345,7 +348,6 @@ namespace Avalonia.Controls ////} } - // Register action to go back to how things were before where any child can be the anchor. // Register action to go back to how things were before where any child can be the anchor. Here, // WinUI uses CompositionTarget.Rendering but we don't currently have that, so post an action to // run *after* rendering has completed (priority needs to be lower than Render as Transformed @@ -404,17 +406,12 @@ namespace Avalonia.Controls _ensuredScroller = false; } - private void OnEffectiveViewportChanged(TransformedBounds? bounds) + private void OnEffectiveViewportChanged(TransformedBounds tb) { - if (!bounds.HasValue) - { - return; - } - - var globalClip = bounds.Value.Clip; + var globalClip = tb.Clip; var transform = _owner.GetVisualRoot().TransformToVisual(_owner).Value; var clip = globalClip.TransformToAABB(transform); - var effectiveViewport = clip.Intersect(bounds.Value.Bounds); + var effectiveViewport = clip.Intersect(tb.Bounds); Logger.TryGet(LogEventLevel.Verbose)?.Log("Repeater", this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId); UpdateViewport(effectiveViewport); @@ -532,12 +529,68 @@ namespace Avalonia.Controls // // UWP uses the EffectiveViewportChanged event (which I think was implemented specially // for this case): we need to implement that in Avalonia. - return control.GetObservable(Visual.TransformedBoundsProperty) - .Merge(control.GetObservable(Visual.BoundsProperty).Select(_ => control.TransformedBounds)) - .Skip(1) + return new EffectiveViewportChangedObservable(control) .Subscribe(OnEffectiveViewportChanged); } + private class EffectiveViewportChangedObservable : SingleSubscriberObservableBase, + IObserver, + IObserver + { + private readonly IControl _control; + private IDisposable _subscription; + private TransformedBounds? _lastBounds; + private bool _skipBounds; + + public EffectiveViewportChangedObservable(IControl control) => _control = control; + + void IObserver.OnNext(TransformedBounds? value) + { + _lastBounds = value; + + if (value != null) + { + PublishNext(value.Value); + } + } + + void IObserver.OnNext(Rect value) + { + if (_lastBounds.HasValue) + { + if (!_skipBounds) + { + PublishNext(new TransformedBounds( + new Rect(value.Size), + _lastBounds.Value.Clip, + _lastBounds.Value.Transform)); + } + + _skipBounds = false; + } + } + + protected override void Subscribed() + { + _subscription = new CompositeDisposable( + _control.GetObservable(Visual.TransformedBoundsProperty).Subscribe(this), + _control.GetObservable(Visual.BoundsProperty).Subscribe(this)); + _lastBounds = null; + _skipBounds = true; + } + + protected override void Unsubscribed() + { + _subscription.Dispose(); + _subscription = null; + } + + void IObserver.OnCompleted() { } + void IObserver.OnError(Exception error) { } + void IObserver.OnCompleted() { } + void IObserver.OnError(Exception error) { } + } + private class ScrollerInfo { public ScrollerInfo(ScrollViewer scroller)