Browse Source

Move ILayoutRoot to PresentationSource

# Conflicts:
#	tests/Avalonia.Controls.UnitTests/TabControlTests.cs
feature/presentation-source-2
Nikita Tsukanov 1 month ago
parent
commit
844542862c
  1. 2
      src/Avalonia.Base/Layout/ILayoutManager.cs
  2. 6
      src/Avalonia.Base/Layout/ILayoutRoot.cs
  3. 3
      src/Avalonia.Base/Layout/LayoutHelper.cs
  4. 20
      src/Avalonia.Base/Layout/LayoutManager.cs
  5. 20
      src/Avalonia.Base/Layout/Layoutable.cs
  6. 3
      src/Avalonia.Base/Rendering/IPresentationSource.cs
  7. 9
      src/Avalonia.Base/VisualTree/VisualExtensions.cs
  8. 3
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  9. 3
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  10. 2
      src/Avalonia.Controls/Grid.cs
  11. 3
      src/Avalonia.Controls/GridSplitter.cs
  12. 69
      src/Avalonia.Controls/PresentationSource/PresentationSource.Layout.cs
  13. 7
      src/Avalonia.Controls/PresentationSource/PresentationSource.cs
  14. 78
      src/Avalonia.Controls/TopLevel.cs
  15. 2
      src/Avalonia.Controls/TreeView.cs
  16. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  17. 2
      src/Avalonia.Controls/Window.cs
  18. 11
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  19. 2
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  20. 2
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  21. 2
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  22. 11
      tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs
  23. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  24. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  25. 12
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  26. 11
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
  27. 42
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  28. 2
      tests/Avalonia.Controls.UnitTests/TransitioningContentControlTests.cs
  29. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  30. 2
      tests/Avalonia.Controls.UnitTests/VirtualizingCarouselPanelTests.cs
  31. 3
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs
  32. 15
      tests/Avalonia.RenderTests/TestRenderRoot.cs
  33. 5
      tests/Avalonia.UnitTests/TestRoot.cs

2
src/Avalonia.Base/Layout/ILayoutManager.cs

@ -6,7 +6,7 @@ namespace Avalonia.Layout
/// <summary>
/// Manages measuring and arranging of controls.
/// </summary>
internal interface ILayoutManager : IDisposable
public interface ILayoutManager : IDisposable
{
/// <summary>
/// Raised when the layout manager completes a layout pass.

6
src/Avalonia.Base/Layout/ILayoutRoot.cs

@ -10,11 +10,13 @@ namespace Avalonia.Layout
/// <summary>
/// The scaling factor to use in layout.
/// </summary>
double LayoutScaling { get; }
public double LayoutScaling { get; }
/// <summary>
/// Associated instance of layout manager
/// </summary>
internal ILayoutManager LayoutManager { get; }
public ILayoutManager LayoutManager { get; }
public Layoutable RootVisual { get; }
}
}

3
src/Avalonia.Base/Layout/LayoutHelper.cs

@ -140,8 +140,7 @@ namespace Avalonia.Layout
/// </summary>
/// <param name="control">The control.</param>
/// <exception cref="Exception">Thrown when control has no root or returned layout scaling is invalid.</exception>
public static double GetLayoutScale(Layoutable control)
=> control.VisualRoot is ILayoutRoot layoutRoot ? layoutRoot.LayoutScaling : 1.0;
public static double GetLayoutScale(Layoutable control) => control.GetLayoutRoot()?.LayoutScaling ?? 1.0;
/// <summary>
/// Rounds a size to integer values for layout purposes, compensating for high DPI screen

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

@ -9,6 +9,7 @@ using Avalonia.Metadata;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.VisualTree;
#nullable enable
@ -20,7 +21,7 @@ namespace Avalonia.Layout
internal class LayoutManager : ILayoutManager, IDisposable
{
private const int MaxPasses = 10;
private readonly Layoutable _owner;
private readonly ILayoutRoot _owner;
private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid);
private readonly List<Layoutable> _toArrangeAfterMeasure = new();
@ -33,7 +34,7 @@ namespace Avalonia.Layout
public LayoutManager(ILayoutRoot owner)
{
_owner = owner as Layoutable ?? throw new ArgumentNullException(nameof(owner));
_owner = owner;
_invokeOnRender = ExecuteQueuedLayoutPass;
}
@ -62,7 +63,7 @@ namespace Avalonia.Layout
#endif
}
if (control.VisualRoot != _owner)
if (control.GetLayoutRoot() != _owner)
{
throw new ArgumentException("Attempt to call InvalidateMeasure on wrong LayoutManager.");
}
@ -92,7 +93,7 @@ namespace Avalonia.Layout
#endif
}
if (control.VisualRoot != _owner)
if (control.GetLayoutRoot() != _owner)
{
throw new ArgumentException("Attempt to call InvalidateArrange on wrong LayoutManager.");
}
@ -187,9 +188,12 @@ namespace Avalonia.Layout
try
{
if (_owner?.RootVisual == null)
return;
var root = _owner.RootVisual;
_running = true;
Measure(_owner);
Arrange(_owner);
Measure(root);
Arrange(root);
}
finally
{
@ -299,7 +303,7 @@ namespace Avalonia.Layout
// control to be removed.
if (!control.IsMeasureValid)
{
if (control is ILayoutRoot root)
if (control.GetLayoutRoot()?.RootVisual == control)
{
control.Measure(Size.Infinity);
}
@ -328,7 +332,7 @@ namespace Avalonia.Layout
if (!control.IsArrangeValid)
{
if (control is ILayoutRoot root)
if (control.GetLayoutRoot()?.RootVisual == control)
control.Arrange(new Rect(control.DesiredSize));
else if (control.PreviousArrange != null)
{

20
src/Avalonia.Base/Layout/Layoutable.cs

@ -168,7 +168,7 @@ namespace Avalonia.Layout
{
add
{
if (_effectiveViewportChanged is null && VisualRoot is ILayoutRoot r && !_isAttachingToVisualTree)
if (_effectiveViewportChanged is null && this.GetLayoutRoot() is {} r && !_isAttachingToVisualTree)
{
r.LayoutManager.RegisterEffectiveViewportListener(this);
}
@ -180,7 +180,7 @@ namespace Avalonia.Layout
{
_effectiveViewportChanged -= value;
if (_effectiveViewportChanged is null && VisualRoot is ILayoutRoot r)
if (_effectiveViewportChanged is null && this.GetLayoutRoot() is {} r)
{
r.LayoutManager.UnregisterEffectiveViewportListener(this);
}
@ -194,7 +194,7 @@ namespace Avalonia.Layout
{
add
{
if (_layoutUpdated is null && VisualRoot is ILayoutRoot r && !_isAttachingToVisualTree)
if (_layoutUpdated is null && this.GetLayoutRoot() is {} r && !_isAttachingToVisualTree)
{
r.LayoutManager.LayoutUpdated += LayoutManagedLayoutUpdated;
}
@ -206,7 +206,7 @@ namespace Avalonia.Layout
{
_layoutUpdated -= value;
if (_layoutUpdated is null && VisualRoot is ILayoutRoot r)
if (_layoutUpdated is null && this.GetLayoutRoot() is {} r)
{
r.LayoutManager.LayoutUpdated -= LayoutManagedLayoutUpdated;
}
@ -221,7 +221,7 @@ namespace Avalonia.Layout
/// schedule layout passes itself.
/// </remarks>
public void UpdateLayout() => (this.GetPresentationSource()?.RootVisual as ILayoutRoot)?.LayoutManager?.ExecuteLayoutPass();
public void UpdateLayout() => this.GetLayoutManager()?.ExecuteLayoutPass();
/// <summary>
/// Gets or sets the width of the element.
@ -449,7 +449,7 @@ namespace Avalonia.Layout
if (IsAttachedToVisualTree)
{
(VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this);
this.GetLayoutManager()?.InvalidateMeasure(this);
InvalidateVisual();
}
OnMeasureInvalidated();
@ -466,7 +466,7 @@ namespace Avalonia.Layout
Logger.TryGet(LogEventLevel.Verbose, LogArea.Layout)?.Log(this, "Invalidated arrange");
IsArrangeValid = false;
(VisualRoot as ILayoutRoot)?.LayoutManager?.InvalidateArrange(this);
this.GetLayoutManager()?.InvalidateArrange(this);
InvalidateVisual();
}
}
@ -794,7 +794,7 @@ namespace Avalonia.Layout
_isAttachingToVisualTree = false;
}
if (e.Root is ILayoutRoot r)
if (this.GetLayoutRoot() is {} r)
{
if (_layoutUpdated is object)
{
@ -810,7 +810,7 @@ namespace Avalonia.Layout
protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
if (e.Root is ILayoutRoot r)
if (this.GetLayoutRoot() is {} r)
{
if (_layoutUpdated is object)
{
@ -853,7 +853,7 @@ namespace Avalonia.Layout
// they will need to be registered with the layout manager now that they
// are again effectively visible. If IsEffectivelyVisible becomes an observable
// property then we can piggy-pack on that; for the moment we do this manually.
if (VisualRoot is ILayoutRoot layoutRoot)
if (this.GetLayoutRoot() is {} layoutRoot)
{
var count = VisualChildren.Count;

3
src/Avalonia.Base/Rendering/IPresentationSource.cs

@ -1,5 +1,6 @@
using System;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Platform;
namespace Avalonia.Rendering;
@ -13,6 +14,8 @@ public interface IPresentationSource
internal IHitTester HitTester { get; }
internal IInputRoot InputRoot { get; }
internal ILayoutRoot LayoutRoot { get; }
public Visual? RootVisual { get; }

9
src/Avalonia.Base/VisualTree/VisualExtensions.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Utilities;
@ -464,6 +465,14 @@ namespace Avalonia.VisualTree
// TODO: Verify all usages, this is no longer necessary a TopLevel
internal static Visual? GetVisualRoot(this Visual visual) => visual.PresentationSource?.RootVisual;
internal static ILayoutRoot? GetLayoutRoot(this Visual visual) => visual.PresentationSource?.LayoutRoot;
/// <summary>
/// Gets the layout manager for the visual's presentation source, or null if the visual is not attached to a visual root.
/// </summary>
public static ILayoutManager? GetLayoutManager(this Visual visual) =>
visual.PresentationSource?.LayoutRoot.LayoutManager;
/// <summary>
/// Attempts to obtain platform settings from the visual's root.
/// This will return null if the visual is not attached to a visual root.

3
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -8,6 +8,7 @@ using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -406,7 +407,7 @@ namespace Avalonia.Controls
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
this.GetLayoutManager()?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();

3
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -7,6 +7,7 @@ using System;
using System.Globalization;
using Avalonia.Controls.Utils;
using Avalonia.Automation.Peers;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -380,7 +381,7 @@ namespace Avalonia.Controls
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
this.GetLayoutManager()?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();

2
src/Avalonia.Controls/Grid.cs

@ -2117,7 +2117,7 @@ namespace Avalonia.Controls
{
// DpiScale dpiScale = GetDpi();
// double dpi = columns ? dpiScale.DpiScaleX : dpiScale.DpiScaleY;
var dpi = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;
var dpi = this.GetLayoutRoot()?.LayoutScaling ?? 1.0;
double[] roundingErrors = RoundingErrors;
double roundedTakenSize = 0;

3
src/Avalonia.Controls/GridSplitter.cs

@ -13,6 +13,7 @@ using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@ -226,7 +227,7 @@ namespace Avalonia.Controls
ResizeDirection = resizeDirection,
SplitterLength = Math.Min(Bounds.Width, Bounds.Height),
ResizeBehavior = GetEffectiveResizeBehavior(resizeDirection),
Scaling = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1,
Scaling = this.GetLayoutRoot()?.LayoutScaling ?? 1,
};
// Store the rows and columns to resize on drag events.

69
src/Avalonia.Controls/PresentationSource/PresentationSource.Layout.cs

@ -0,0 +1,69 @@
using System;
using System.ComponentModel;
using Avalonia.Layout;
using Avalonia.Rendering;
namespace Avalonia.Controls;
internal partial class PresentationSource : ILayoutRoot
{
private LayoutDiagnosticBridge? _layoutDiagnosticBridge;
public double LayoutScaling => RenderScaling;
public ILayoutManager LayoutManager { get; }
ILayoutRoot IPresentationSource.LayoutRoot => this;
Layoutable ILayoutRoot.RootVisual => RootVisual;
private ILayoutManager CreateLayoutManager()
{
var manager = new LayoutManager(this);
_layoutDiagnosticBridge = new LayoutDiagnosticBridge(Renderer.Diagnostics, manager);
_layoutDiagnosticBridge.SetupBridge();
return manager;
}
/// <summary>
/// Provides layout pass timing from the layout manager to the renderer, for diagnostics purposes.
/// </summary>
private sealed class LayoutDiagnosticBridge : IDisposable
{
private readonly RendererDiagnostics _diagnostics;
private readonly LayoutManager _layoutManager;
private bool _isHandling;
public LayoutDiagnosticBridge(RendererDiagnostics diagnostics, LayoutManager layoutManager)
{
_diagnostics = diagnostics;
_layoutManager = layoutManager;
diagnostics.PropertyChanged += OnDiagnosticsPropertyChanged;
}
public void SetupBridge()
{
var needsHandling = (_diagnostics.DebugOverlays & RendererDebugOverlays.LayoutTimeGraph) != 0;
if (needsHandling != _isHandling)
{
_isHandling = needsHandling;
_layoutManager.LayoutPassTimed = needsHandling
? timing => _diagnostics.LastLayoutPassTiming = timing
: null;
}
}
private void OnDiagnosticsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(RendererDiagnostics.DebugOverlays))
{
SetupBridge();
}
}
public void Dispose()
{
_diagnostics.PropertyChanged -= OnDiagnosticsPropertyChanged;
_layoutManager.LayoutPassTimed = null;
}
}
}

7
src/Avalonia.Controls/PresentationSource/PresentationSource.cs

@ -1,6 +1,7 @@
using System;
using Avalonia.Input;
using Avalonia.Input.TextInput;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.Platform;
using Avalonia.Rendering;
@ -37,8 +38,9 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
Renderer = new CompositingRenderer(this, PlatformImpl.Compositor, () => PlatformImpl.Surfaces ?? []);
Renderer.SceneInvalidated += SceneInvalidated;
RootVisual = rootVisual;
LayoutManager = CreateLayoutManager();
RootVisual = rootVisual;
}
// In WPF it's a Visual and it's nullable. For now we have it as non-nullable InputElement since
@ -81,6 +83,9 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
public void Dispose()
{
_layoutDiagnosticBridge?.Dispose();
_layoutDiagnosticBridge = null;
LayoutManager.Dispose();
Renderer.SceneInvalidated -= SceneInvalidated;
// We need to wait for the renderer to complete any in-flight operations
Renderer.Dispose();

78
src/Avalonia.Controls/TopLevel.cs

@ -39,7 +39,6 @@ namespace Avalonia.Controls
/// </remarks>
[TemplatePart("PART_TransparencyFallback", typeof(Border))]
public abstract class TopLevel : ContentControl,
ILayoutRoot,
ICloseable,
IStyleHost,
ILogicalRoot
@ -126,12 +125,10 @@ namespace Avalonia.Controls
private Size _clientSize;
private Size? _frameSize;
private WindowTransparencyLevel _actualTransparencyLevel;
private ILayoutManager? _layoutManager;
private Border? _transparencyFallbackBorder;
private TargetWeakEventSubscriber<TopLevel, ResourcesChangedEventArgs>? _resourcesChangesSubscriber;
private IStorageProvider? _storageProvider;
private Screens? _screens;
private LayoutDiagnosticBridge? _layoutDiagnosticBridge;
private PresentationSource _source;
internal PresentationSource PresentationSource => _source;
internal IInputRoot InputRoot => _source;
@ -384,26 +381,7 @@ namespace Avalonia.Controls
remove => RemoveHandler(BackRequestedEvent, value);
}
internal ILayoutManager LayoutManager
{
get
{
if (_layoutManager is null)
{
_layoutManager = CreateLayoutManager();
if (_layoutManager is LayoutManager typedLayoutManager)
{
_layoutDiagnosticBridge = new LayoutDiagnosticBridge(Renderer.Diagnostics, typedLayoutManager);
_layoutDiagnosticBridge.SetupBridge();
}
}
return _layoutManager;
}
}
ILayoutManager ILayoutRoot.LayoutManager => LayoutManager;
internal ILayoutManager LayoutManager => _source.LayoutManager;
/// <summary>
/// Gets the platform-specific window implementation.
@ -504,8 +482,6 @@ namespace Avalonia.Controls
return control.GetValue(AutoSafeAreaPaddingProperty);
}
double ILayoutRoot.LayoutScaling => _scaling;
/// <inheritdoc/>
public double RenderScaling => _scaling;
@ -641,11 +617,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Creates the layout manager for this <see cref="TopLevel" />.
/// </summary>
private protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this);
/// <summary>
/// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>.
/// </summary>
@ -682,9 +653,6 @@ namespace Avalonia.Controls
_applicationThemeHost.ActualThemeVariantChanged -= GlobalActualThemeVariantChanged;
}
_layoutDiagnosticBridge?.Dispose();
_layoutDiagnosticBridge = null;
_backGestureSubscription?.Dispose();
var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null);
@ -863,49 +831,5 @@ namespace Avalonia.Controls
return scaling;
}
/// <summary>
/// Provides layout pass timing from the layout manager to the renderer, for diagnostics purposes.
/// </summary>
private sealed class LayoutDiagnosticBridge : IDisposable
{
private readonly RendererDiagnostics _diagnostics;
private readonly LayoutManager _layoutManager;
private bool _isHandling;
public LayoutDiagnosticBridge(RendererDiagnostics diagnostics, LayoutManager layoutManager)
{
_diagnostics = diagnostics;
_layoutManager = layoutManager;
diagnostics.PropertyChanged += OnDiagnosticsPropertyChanged;
}
public void SetupBridge()
{
var needsHandling = (_diagnostics.DebugOverlays & RendererDebugOverlays.LayoutTimeGraph) != 0;
if (needsHandling != _isHandling)
{
_isHandling = needsHandling;
_layoutManager.LayoutPassTimed = needsHandling
? timing => _diagnostics.LastLayoutPassTiming = timing
: null;
}
}
private void OnDiagnosticsPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(RendererDiagnostics.DebugOverlays))
{
SetupBridge();
}
}
public void Dispose()
{
_diagnostics.PropertyChanged -= OnDiagnosticsPropertyChanged;
_layoutManager.LayoutPassTimed = null;
}
}
}
}

2
src/Avalonia.Controls/TreeView.cs

@ -168,7 +168,7 @@ namespace Avalonia.Controls
item.IsExpanded = true;
if (item.Presenter?.Panel is null)
(this.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
this.GetLayoutManager()?.ExecuteLayoutPass();
if (item.Presenter?.Panel is { } panel)
{

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -459,7 +459,7 @@ namespace Avalonia.Controls
element.BringIntoView();
return element;
}
else if (this.GetVisualRoot() is ILayoutRoot root)
else if (this.GetLayoutRoot() is {} root)
{
// Create and measure the element to be brought into view. Store it in a field so that
// it can be re-used in the layout pass.

2
src/Avalonia.Controls/Window.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls
/// <summary>
/// A top-level window.
/// </summary>
public class Window : WindowBase, IFocusScope, ILayoutRoot
public class Window : WindowBase, IFocusScope
{
private static readonly Lazy<WindowIcon?> s_defaultIcon = new(LoadDefaultIcon);
private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>();

11
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -759,18 +759,9 @@ namespace Avalonia.Controls.UnitTests
}
}
private class TestTopLevel : TopLevel
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
private readonly ILayoutManager _layoutManager;
public bool IsClosed { get; private set; }
public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
: base(impl)
{
_layoutManager = layoutManager ?? new LayoutManager(this);
}
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
}
}

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

@ -336,7 +336,7 @@ namespace Avalonia.Controls.UnitTests
private static void Layout(Carousel target)
{
((ILayoutRoot)target.GetVisualRoot()!).LayoutManager.ExecuteLayoutPass();
target.GetLayoutManager()?.ExecuteLayoutPass();
}
private static IControlTemplate CarouselTemplate()

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

@ -1218,7 +1218,7 @@ namespace Avalonia.Controls.UnitTests
private static void Layout(Control c)
{
(c.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
private static ContentPresenter GetContainer(ItemsControl target, int index = 0)

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

@ -642,7 +642,7 @@ namespace Avalonia.Controls.UnitTests
private static void Layout(Control c)
{
((ILayoutRoot)c.GetVisualRoot()!).LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
private class Item

11
tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs

@ -1002,17 +1002,8 @@ namespace Avalonia.Controls.UnitTests
}
}
private class TestTopLevel : TopLevel
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
private readonly ILayoutManager _layoutManager;
public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
: base(impl)
{
_layoutManager = layoutManager ?? new LayoutManager(this);
}
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()

2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -2555,7 +2555,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
private static void Layout(Control c)
{
((ILayoutRoot)c.GetVisualRoot()!).LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
private static FuncControlTemplate Template()

2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -1341,7 +1341,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
private static void Layout(Control c)
{
(c.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
public static IDisposable Start()

12
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -883,18 +883,8 @@ namespace Avalonia.Controls.UnitTests
};
}
private class TestTopLevel : TopLevel
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
private readonly ILayoutManager _layoutManager;
public bool IsClosed { get; private set; }
public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
: base(impl)
{
_layoutManager = layoutManager ?? new LayoutManager(this);
}
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
private static void Prepare(TabControl target)

11
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@ -2284,17 +2284,8 @@ namespace Avalonia.Controls.UnitTests
}
}
private class TestTopLevel : TopLevel
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
private readonly ILayoutManager _layoutManager;
public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
: base(impl)
{
_layoutManager = layoutManager ?? new LayoutManager(this);
}
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()

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

@ -82,11 +82,11 @@ namespace Avalonia.Controls.UnitTests
{
var impl = CreateMockTopLevelImpl();
var target = new TestTopLevel(impl.Object, Mock.Of<ILayoutManager>());
var target = new TestTopLevel(impl.Object);
// The layout pass should be scheduled by the derived class.
var layoutManagerMock = Mock.Get(target.LayoutManager);
layoutManagerMock.Verify(x => x.ExecuteLayoutPass(), Times.Never);
Assert.Equal(0, target.Measured);
Assert.Equal(0, target.Arranged);
}
}
@ -254,22 +254,6 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Close_Should_Dispose_LayoutManager()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = CreateMockTopLevelImpl(true);
var layoutManager = new Mock<ILayoutManager>();
var target = new TestTopLevel(impl.Object, layoutManager.Object);
impl.Object.Closed!();
layoutManager.Verify(x => x.Dispose());
}
}
[Fact]
public void Reacts_To_Changes_In_Global_Styles()
{
@ -329,18 +313,20 @@ namespace Avalonia.Controls.UnitTests
return topLevel;
}
private class TestTopLevel : TopLevel
private class TestTopLevel(ITopLevelImpl impl) : TopLevel(impl)
{
private readonly ILayoutManager _layoutManager;
public bool IsClosed { get; private set; }
public TestTopLevel(ITopLevelImpl impl, ILayoutManager? layoutManager = null)
: base(impl)
public int Measured, Arranged;
protected override Size MeasureCore(Size availableSize)
{
_layoutManager = layoutManager ?? new LayoutManager(this);
Measured++;
return base.MeasureCore(availableSize);
}
private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
protected override void ArrangeCore(Rect finalRect)
{
Arranged++;
base.ArrangeCore(finalRect);
}
}
}
}

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

@ -377,7 +377,7 @@ namespace Avalonia.Controls.UnitTests
private void Layout(Control c)
{
(c.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
private class TestTransition : IPageTransition

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

@ -1781,7 +1781,7 @@ namespace Avalonia.Controls.UnitTests
private void Layout(Control c)
{
(c.GetVisualRoot() as ILayoutRoot)?.LayoutManager.ExecuteLayoutPass();
c.GetLayoutManager()?.ExecuteLayoutPass();
}
private void ClickContainer(Control container, KeyModifiers modifiers)

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

@ -344,6 +344,6 @@ namespace Avalonia.Controls.UnitTests
});
}
private static void Layout(Control c) => ((ILayoutRoot)c.GetVisualRoot()!).LayoutManager.ExecuteLayoutPass();
private static void Layout(Control c) => c.GetLayoutManager()?.ExecuteLayoutPass();
}
}

3
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@ -2352,8 +2352,7 @@ namespace Avalonia.Controls.UnitTests
private static void Layout(Control target)
{
var root = (ILayoutRoot?)target.GetVisualRoot();
root?.LayoutManager.ExecuteLayoutPass();
target.GetLayoutManager()?.ExecuteLayoutPass();
}
private static IControlTemplate ListBoxItemTemplate()

15
tests/Avalonia.RenderTests/TestRenderRoot.cs

@ -5,13 +5,14 @@ using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.TextInput;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering.Composition;
namespace Avalonia.Skia.RenderTests
{
public class TestRenderRoot : Decorator, IPresentationSource, IInputRoot
public class TestRenderRoot : Decorator, IPresentationSource, IInputRoot, ILayoutRoot
{
private readonly IRenderTarget _renderTarget;
public Size ClientSize { get; private set; }
@ -19,6 +20,15 @@ namespace Avalonia.Skia.RenderTests
IRenderer IPresentationSource.Renderer => Renderer;
IHitTester IPresentationSource.HitTester => new NullHitTester();
public IInputRoot InputRoot => this;
ILayoutRoot IPresentationSource.LayoutRoot => this;
public double LayoutScaling => 1l;
public ILayoutManager LayoutManager { get; }
Layoutable ILayoutRoot.RootVisual => this;
public Visual? RootVisual => this;
public double RenderScaling { get; }
@ -26,7 +36,8 @@ namespace Avalonia.Skia.RenderTests
{
_renderTarget = renderTarget;
RenderScaling = scaling;
LayoutManager = new LayoutManager(this);
}
class NullHitTester : IHitTester

5
tests/Avalonia.UnitTests/TestRoot.cs

@ -58,6 +58,11 @@ namespace Avalonia.UnitTests
public double LayoutScaling { get; set; } = 1;
internal ILayoutManager LayoutManager { get; set; }
ILayoutRoot IPresentationSource.LayoutRoot => this;
Layoutable ILayoutRoot.RootVisual => RootElement;
ILayoutManager ILayoutRoot.LayoutManager => LayoutManager;
public Visual? RootVisual => this;

Loading…
Cancel
Save