Browse Source

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`.
pull/4091/head
Steven Kirk 6 years ago
parent
commit
094cbb899f
  1. 77
      src/Avalonia.Controls/Repeater/ViewportManager.cs

77
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)

Loading…
Cancel
Save