Browse Source

Merge pull request #11233 from AvaloniaUI/fixes/7706-scrollbar-stuck

Fix scrollbar getting stuck in certain situations
pull/11247/head
Max Katz 3 years ago
committed by GitHub
parent
commit
cc2d755449
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/Avalonia.Base/Layout/LayoutManager.cs
  2. 33
      tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs

27
src/Avalonia.Base/Layout/LayoutManager.cs

@ -21,6 +21,7 @@ namespace Avalonia.Layout
private readonly Layoutable _owner; private readonly Layoutable _owner;
private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid); private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid); private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid);
private readonly List<Layoutable> _toArrangeAfterMeasure = new();
private readonly Action _executeLayoutPass; private readonly Action _executeLayoutPass;
private List<EffectiveViewportChangedListener>? _effectiveViewportChangedListeners; private List<EffectiveViewportChangedListener>? _effectiveViewportChangedListeners;
private bool _disposed; private bool _disposed;
@ -266,9 +267,14 @@ namespace Avalonia.Layout
if (!control.IsArrangeValid) if (!control.IsArrangeValid)
{ {
Arrange(control); if (Arrange(control) == ArrangeResult.AncestorMeasureInvalid)
_toArrangeAfterMeasure.Add(control);
} }
} }
foreach (var i in _toArrangeAfterMeasure)
InvalidateArrange(i);
_toArrangeAfterMeasure.Clear();
} }
private bool Measure(Layoutable control) private bool Measure(Layoutable control)
@ -304,19 +310,19 @@ namespace Avalonia.Layout
return true; return true;
} }
private bool Arrange(Layoutable control) private ArrangeResult Arrange(Layoutable control)
{ {
if (!control.IsVisible || !control.IsAttachedToVisualTree) if (!control.IsVisible || !control.IsAttachedToVisualTree)
return false; return ArrangeResult.NotVisible;
if (control.VisualParent is Layoutable parent) if (control.VisualParent is Layoutable parent)
{ {
if (!Arrange(parent)) if (Arrange(parent) is var parentResult && parentResult != ArrangeResult.Arranged)
return false; return parentResult;
} }
if (!control.IsMeasureValid) if (!control.IsMeasureValid)
return false; return ArrangeResult.AncestorMeasureInvalid;
if (!control.IsArrangeValid) if (!control.IsArrangeValid)
{ {
@ -332,7 +338,7 @@ namespace Avalonia.Layout
} }
} }
return true; return ArrangeResult.Arranged;
} }
private void QueueLayoutPass() private void QueueLayoutPass()
@ -435,5 +441,12 @@ namespace Avalonia.Layout
public Layoutable Listener { get; } public Layoutable Listener { get; }
public Rect Viewport { get; set; } public Rect Viewport { get; set; }
} }
private enum ArrangeResult
{
Arranged,
NotVisible,
AncestorMeasureInvalid,
}
} }
} }

33
tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs

@ -547,5 +547,38 @@ namespace Avalonia.Base.UnitTests.Layout
Assert.True(root.IsMeasureValid); Assert.True(root.IsMeasureValid);
Assert.True(root.IsArrangeValid); Assert.True(root.IsArrangeValid);
} }
[Fact]
public void GreatGrandparent_Can_Invalidate_Grandparent_Measure_During_Arrange()
{
// Issue #7706 (second part: scrollbar gets stuck)
var child = new LayoutTestControl();
var parent = new LayoutTestControl { Child = child };
var grandparent = new LayoutTestControl { Child = parent };
var greatGrandparent = new LayoutTestControl { Child = grandparent };
var root = new LayoutTestRoot { Child = greatGrandparent };
root.LayoutManager.ExecuteInitialLayoutPass();
greatGrandparent.DoArrangeOverride = (_, s) =>
{
grandparent.InvalidateMeasure();
return s;
};
child.InvalidateArrange();
greatGrandparent.InvalidateArrange();
root.LayoutManager.ExecuteLayoutPass();
Assert.True(child.IsMeasureValid);
Assert.True(child.IsArrangeValid);
Assert.True(parent.IsMeasureValid);
Assert.True(parent.IsArrangeValid);
Assert.True(greatGrandparent.IsMeasureValid);
Assert.True(greatGrandparent.IsArrangeValid);
Assert.True(root.IsMeasureValid);
Assert.True(root.IsArrangeValid);
}
} }
} }

Loading…
Cancel
Save