Преглед изворни кода

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`.
Steven Kirk пре 5 година
родитељ
комит
094cbb899f
1 измењених фајлова са 65 додато и 12 уклоњено
  1. 65 12
      src/Avalonia.Controls/Repeater/ViewportManager.cs

+ 65 - 12
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<TransformedBounds>,
+            IObserver<TransformedBounds?>,
+            IObserver<Rect>
+        {
+            private readonly IControl _control;
+            private IDisposable _subscription;
+            private TransformedBounds? _lastBounds;
+            private bool _skipBounds;
+
+            public EffectiveViewportChangedObservable(IControl control) => _control = control;
+
+            void IObserver<TransformedBounds?>.OnNext(TransformedBounds? value)
+            {
+                _lastBounds = value;
+
+                if (value != null)
+                {
+                    PublishNext(value.Value);
+                }
+            }
+
+            void IObserver<Rect>.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<TransformedBounds?>.OnCompleted() { }
+            void IObserver<TransformedBounds?>.OnError(Exception error) { }
+            void IObserver<Rect>.OnCompleted() { }
+            void IObserver<Rect>.OnError(Exception error) { }
+        }
+
         private class ScrollerInfo
         {
             public ScrollerInfo(ScrollViewer scroller)