Browse Source

Don't run layout passes on hidden TopLevels.

To do this we need to pass the root to `LayoutManager`.

Fixes #4161
pull/4163/head
Steven Kirk 6 years ago
parent
commit
7694fc0484
  1. 2
      src/Avalonia.Controls/TopLevel.cs
  2. 10
      src/Avalonia.Layout/ILayoutManager.cs
  3. 28
      src/Avalonia.Layout/LayoutManager.cs
  4. 7
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  5. 7
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  6. 2
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  7. 4
      tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
  8. 4
      tests/Avalonia.UnitTests/TestRoot.cs
  9. 3
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs

2
src/Avalonia.Controls/TopLevel.cs

@ -315,7 +315,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Creates the layout manager for this <see cref="TopLevel" />. /// Creates the layout manager for this <see cref="TopLevel" />.
/// </summary> /// </summary>
protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(); protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this);
/// <summary> /// <summary>
/// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>. /// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>.

10
src/Avalonia.Layout/ILayoutManager.cs

@ -35,6 +35,15 @@ namespace Avalonia.Layout
/// </remarks> /// </remarks>
void ExecuteLayoutPass(); void ExecuteLayoutPass();
/// <summary>
/// Executes the initial layout pass on a layout root.
/// </summary>
/// <remarks>
/// You should not usually need to call this method explictly, the layout root will call
/// it to carry out the initial layout of the control.
/// </remarks>
void ExecuteInitialLayoutPass();
/// <summary> /// <summary>
/// Executes the initial layout pass on a layout root. /// Executes the initial layout pass on a layout root.
/// </summary> /// </summary>
@ -43,6 +52,7 @@ namespace Avalonia.Layout
/// You should not usually need to call this method explictly, the layout root will call /// You should not usually need to call this method explictly, the layout root will call
/// it to carry out the initial layout of the control. /// it to carry out the initial layout of the control.
/// </remarks> /// </remarks>
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
void ExecuteInitialLayoutPass(ILayoutRoot root); void ExecuteInitialLayoutPass(ILayoutRoot root);
} }
} }

28
src/Avalonia.Layout/LayoutManager.cs

@ -12,14 +12,16 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public class LayoutManager : ILayoutManager public class LayoutManager : ILayoutManager
{ {
private readonly ILayoutRoot _owner;
private readonly LayoutQueue<ILayoutable> _toMeasure = new LayoutQueue<ILayoutable>(v => !v.IsMeasureValid); private readonly LayoutQueue<ILayoutable> _toMeasure = new LayoutQueue<ILayoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<ILayoutable> _toArrange = new LayoutQueue<ILayoutable>(v => !v.IsArrangeValid); private readonly LayoutQueue<ILayoutable> _toArrange = new LayoutQueue<ILayoutable>(v => !v.IsArrangeValid);
private readonly Action _executeLayoutPass; private readonly Action _executeLayoutPass;
private bool _queued; private bool _queued;
private bool _running; private bool _running;
public LayoutManager() public LayoutManager(ILayoutRoot owner)
{ {
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
_executeLayoutPass = ExecuteLayoutPass; _executeLayoutPass = ExecuteLayoutPass;
} }
@ -73,6 +75,11 @@ namespace Avalonia.Layout
Dispatcher.UIThread.VerifyAccess(); Dispatcher.UIThread.VerifyAccess();
if (!_owner.IsVisible)
{
return;
}
if (!_running) if (!_running)
{ {
_running = true; _running = true;
@ -131,13 +138,13 @@ namespace Avalonia.Layout
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual void ExecuteInitialLayoutPass(ILayoutRoot root) public virtual void ExecuteInitialLayoutPass()
{ {
try try
{ {
_running = true; _running = true;
Measure(root); Measure(_owner);
Arrange(root); Arrange(_owner);
} }
finally finally
{ {
@ -151,6 +158,17 @@ namespace Avalonia.Layout
ExecuteLayoutPass(); ExecuteLayoutPass();
} }
[Obsolete("Call ExecuteInitialLayoutPass without parameter")]
public void ExecuteInitialLayoutPass(ILayoutRoot root)
{
if (root != _owner)
{
throw new ArgumentException("ExecuteInitialLayoutPass called with incorrect root.");
}
ExecuteInitialLayoutPass();
}
private void ExecuteMeasurePass() private void ExecuteMeasurePass()
{ {
while (_toMeasure.Count > 0) while (_toMeasure.Count > 0)
@ -228,7 +246,7 @@ namespace Avalonia.Layout
private void QueueLayoutPass() private void QueueLayoutPass()
{ {
if (!_queued && !_running) if (!_queued && !_running && _owner.IsVisible)
{ {
Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout); Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout);
_queued = true; _queued = true;

7
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -324,6 +324,11 @@ namespace Avalonia.Controls.UnitTests.Presenters
private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot
{ {
public TestScroller()
{
LayoutManager = new LayoutManager(this);
}
public IRenderer Renderer { get; } public IRenderer Renderer { get; }
public Size ClientSize { get; } public Size ClientSize { get; }
public double RenderScaling => 1; public double RenderScaling => 1;
@ -332,7 +337,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
public double LayoutScaling => 1; public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; } = new LayoutManager(); public ILayoutManager LayoutManager { get; }
public IRenderTarget CreateRenderTarget() => throw new NotImplementedException(); public IRenderTarget CreateRenderTarget() => throw new NotImplementedException();
public void Invalidate(Rect rect) => throw new NotImplementedException(); public void Invalidate(Rect rect) => throw new NotImplementedException();

7
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -1062,6 +1062,11 @@ namespace Avalonia.Controls.UnitTests.Presenters
private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot, ILogicalRoot private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot, ILogicalRoot
{ {
public TestScroller()
{
LayoutManager = new LayoutManager(this);
}
public IRenderer Renderer { get; } public IRenderer Renderer { get; }
public Size ClientSize { get; } public Size ClientSize { get; }
public double RenderScaling => 1; public double RenderScaling => 1;
@ -1070,7 +1075,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
public double LayoutScaling => 1; public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; } = new LayoutManager(); public ILayoutManager LayoutManager { get; }
public IRenderTarget CreateRenderTarget() => throw new NotImplementedException(); public IRenderTarget CreateRenderTarget() => throw new NotImplementedException();
public void Invalidate(Rect rect) => throw new NotImplementedException(); public void Invalidate(Rect rect) => throw new NotImplementedException();

2
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -323,7 +323,7 @@ namespace Avalonia.Controls.UnitTests
public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null) public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null)
: base(impl) : base(impl)
{ {
_layoutManager = layoutManager ?? new LayoutManager(); _layoutManager = layoutManager ?? new LayoutManager(this);
} }
protected override ILayoutManager CreateLayoutManager() => _layoutManager; protected override ILayoutManager CreateLayoutManager() => _layoutManager;

4
tests/Avalonia.Layout.UnitTests/LayoutableTests.cs

@ -208,14 +208,12 @@ namespace Avalonia.Layout.UnitTests
{ {
Border border1; Border border1;
Border border2; Border border2;
var layoutManager = new LayoutManager();
var root = new TestRoot var root = new TestRoot
{ {
Child = border1 = new Border Child = border1 = new Border
{ {
Child = border2 = new Border(), Child = border2 = new Border(),
}, },
LayoutManager = layoutManager,
}; };
var raised = 0; var raised = 0;
@ -233,7 +231,7 @@ namespace Avalonia.Layout.UnitTests
root.Measure(new Size(100, 100)); root.Measure(new Size(100, 100));
root.Arrange(new Rect(0, 0, 100, 100)); root.Arrange(new Rect(0, 0, 100, 100));
layoutManager.ExecuteLayoutPass(); root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(3, raised); Assert.Equal(3, raised);
Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds); Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds);

4
tests/Avalonia.UnitTests/TestRoot.cs

@ -19,6 +19,8 @@ namespace Avalonia.UnitTests
public TestRoot() public TestRoot()
{ {
Renderer = Mock.Of<IRenderer>(); Renderer = Mock.Of<IRenderer>();
LayoutManager = new LayoutManager(this);
IsVisible = true;
} }
public TestRoot(IControl child) public TestRoot(IControl child)
@ -44,7 +46,7 @@ namespace Avalonia.UnitTests
public double LayoutScaling { get; set; } = 1; public double LayoutScaling { get; set; } = 1;
public ILayoutManager LayoutManager { get; set; } = new LayoutManager(); public ILayoutManager LayoutManager { get; set; }
public double RenderScaling => 1; public double RenderScaling => 1;

3
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@ -16,6 +16,7 @@ namespace Avalonia.UnitTests
public TestTemplatedRoot() public TestTemplatedRoot()
{ {
LayoutManager = new LayoutManager(this);
Template = new FuncControlTemplate<TestTemplatedRoot>((x, scope) => new ContentPresenter Template = new FuncControlTemplate<TestTemplatedRoot>((x, scope) => new ContentPresenter
{ {
Name = "PART_ContentPresenter", Name = "PART_ContentPresenter",
@ -28,7 +29,7 @@ namespace Avalonia.UnitTests
public double LayoutScaling => 1; public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; set; } = new LayoutManager(); public ILayoutManager LayoutManager { get; set; }
public double RenderScaling => 1; public double RenderScaling => 1;

Loading…
Cancel
Save