Browse Source

Ensure IRenderRoot.Renderer is never null

pull/10231/head
Julien Lebosquain 3 years ago
parent
commit
7eda49e061
  1. 82
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  2. 2
      src/Avalonia.Base/Rendering/IRenderRoot.cs
  3. 3
      src/Avalonia.Base/Rendering/IRenderer.cs
  4. 10
      src/Avalonia.Base/Visual.cs
  5. 40
      src/Avalonia.Controls/TopLevel.cs
  6. 6
      src/Avalonia.Controls/Window.cs
  7. 6
      src/Avalonia.Controls/WindowBase.cs
  8. 2
      src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs
  9. 3
      src/Avalonia.Native/WindowImpl.cs
  10. 20
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  11. 39
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  12. 12
      tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
  13. 14
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  14. 2
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  15. 35
      tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs
  16. 54
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs

82
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -20,15 +20,17 @@ public class CompositingRenderer : IRendererWithCompositor
{
private readonly IRenderRoot _root;
private readonly Compositor _compositor;
CompositionDrawingContext _recorder = new();
DrawingContext _recordingContext;
private HashSet<Visual> _dirty = new();
private HashSet<Visual> _recalculateChildren = new();
private readonly CompositionDrawingContext _recorder = new();
private readonly DrawingContext _recordingContext;
private readonly HashSet<Visual> _dirty = new();
private readonly HashSet<Visual> _recalculateChildren = new();
private readonly Action _update;
private bool _queuedUpdate;
private Action _update;
private bool _updating;
private bool _isDisposed;
internal CompositionTarget CompositionTarget;
internal CompositionTarget CompositionTarget { get; }
/// <summary>
/// Asks the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered.
@ -38,6 +40,17 @@ public class CompositingRenderer : IRendererWithCompositor
/// <inheritdoc/>
public RendererDiagnostics Diagnostics { get; }
/// <inheritdoc />
public Compositor Compositor => _compositor;
/// <summary>
/// Initializes a new instance of <see cref="CompositingRenderer"/>
/// </summary>
/// <param name="root">The render root using this renderer.</param>
/// <param name="compositor">The associated compositors.</param>
/// <param name="surfaces">
/// A function returning the list of native platform's surfaces that can be consumed by rendering subsystems.
/// </param>
public CompositingRenderer(IRenderRoot root, Compositor compositor, Func<IEnumerable<object>> surfaces)
{
_root = root;
@ -66,7 +79,7 @@ public class CompositingRenderer : IRendererWithCompositor
/// <inheritdoc/>
public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
void QueueUpdate()
private void QueueUpdate()
{
if(_queuedUpdate)
return;
@ -77,9 +90,11 @@ public class CompositingRenderer : IRendererWithCompositor
/// <inheritdoc/>
public void AddDirty(Visual visual)
{
if (_isDisposed)
return;
if (_updating)
throw new InvalidOperationException("Visual was invalidated during the render pass");
_dirty.Add((Visual)visual);
_dirty.Add(visual);
QueueUpdate();
}
@ -126,9 +141,11 @@ public class CompositingRenderer : IRendererWithCompositor
/// <inheritdoc/>
public void RecalculateChildren(Visual visual)
{
if (_isDisposed)
return;
if (_updating)
throw new InvalidOperationException("Visual was invalidated during the render pass");
_recalculateChildren.Add((Visual)visual);
_recalculateChildren.Add(visual);
QueueUpdate();
}
@ -171,7 +188,7 @@ public class CompositingRenderer : IRendererWithCompositor
if (sortedChildren != null)
for (var c = 0; c < visualChildren.Count; c++)
{
if (!ReferenceEquals(compositionChildren[c], ((Visual)sortedChildren[c].visual).CompositionVisual))
if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual))
{
mismatch = true;
break;
@ -179,7 +196,7 @@ public class CompositingRenderer : IRendererWithCompositor
}
else
for (var c = 0; c < visualChildren.Count; c++)
if (!ReferenceEquals(compositionChildren[c], ((Visual)visualChildren[c]).CompositionVisual))
if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual))
{
mismatch = true;
break;
@ -201,7 +218,7 @@ public class CompositingRenderer : IRendererWithCompositor
{
foreach (var ch in sortedChildren)
{
var compositionChild = ((Visual)ch.visual).CompositionVisual;
var compositionChild = ch.visual.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
@ -210,7 +227,7 @@ public class CompositingRenderer : IRendererWithCompositor
else
foreach (var ch in v.GetVisualChildren())
{
var compositionChild = ((Visual)ch).CompositionVisual;
var compositionChild = ch.CompositionVisual;
if (compositionChild != null)
compositionChildren.Add(compositionChild);
}
@ -289,13 +306,18 @@ public class CompositingRenderer : IRendererWithCompositor
_updating = false;
}
}
/// <inheritdoc />
public void Resized(Size size)
{
}
/// <inheritdoc />
public void Paint(Rect rect)
{
if (_isDisposed)
return;
QueueUpdate();
CompositionTarget.RequestRedraw();
if(RenderOnlyOnRenderThread && Compositor.Loop.RunsInBackground)
@ -304,17 +326,34 @@ public class CompositingRenderer : IRendererWithCompositor
CompositionTarget.ImmediateUIThreadRender();
}
public void Start() => CompositionTarget.IsEnabled = true;
public void Stop()
/// <inheritdoc />
public void Start()
{
CompositionTarget.IsEnabled = false;
if (_isDisposed)
return;
CompositionTarget.IsEnabled = true;
}
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType) => Compositor.TryGetRenderInterfaceFeature(featureType);
/// <inheritdoc />
public void Stop()
=> CompositionTarget.IsEnabled = false;
/// <inheritdoc />
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType)
=> Compositor.TryGetRenderInterfaceFeature(featureType);
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
_dirty.Clear();
_recalculateChildren.Clear();
SceneInvalidated = null;
Stop();
CompositionTarget.Dispose();
@ -323,9 +362,4 @@ public class CompositingRenderer : IRendererWithCompositor
if (Compositor.Loop.RunsInBackground)
_compositor.Commit().Wait();
}
/// <summary>
/// The associated <see cref="Avalonia.Rendering.Composition.Compositor"/> object
/// </summary>
public Compositor Compositor => _compositor;
}

2
src/Avalonia.Base/Rendering/IRenderRoot.cs

@ -1,6 +1,4 @@
using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{

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

@ -90,6 +90,9 @@ namespace Avalonia.Rendering
public interface IRendererWithCompositor : IRenderer
{
/// <summary>
/// The associated <see cref="Avalonia.Rendering.Composition.Compositor"/> object
/// </summary>
Compositor Compositor { get; }
}
}

10
src/Avalonia.Base/Visual.cs

@ -348,7 +348,7 @@ namespace Avalonia
/// </summary>
public void InvalidateVisual()
{
VisualRoot?.Renderer?.AddDirty(this);
VisualRoot?.Renderer.AddDirty(this);
}
/// <summary>
@ -449,7 +449,7 @@ namespace Avalonia
protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
base.LogicalChildrenCollectionChanged(sender, e);
VisualRoot?.Renderer?.RecalculateChildren(this);
VisualRoot?.Renderer.RecalculateChildren(this);
}
/// <summary>
@ -477,7 +477,7 @@ namespace Avalonia
OnAttachedToVisualTree(e);
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
_visualRoot.Renderer?.RecalculateChildren(_visualParent!);
_visualRoot.Renderer.RecalculateChildren(_visualParent!);
if (ZIndex != 0 && VisualParent is Visual parent)
parent.HasNonUniformZIndexChildren = true;
@ -540,7 +540,7 @@ namespace Avalonia
}
DetachedFromVisualTree?.Invoke(this, e);
e.Root?.Renderer?.AddDirty(this);
e.Root.Renderer.AddDirty(this);
var visualChildren = VisualChildren;
@ -659,7 +659,7 @@ namespace Avalonia
parentVisual.HasNonUniformZIndexChildren = true;
sender?.InvalidateVisual();
parent?.VisualRoot?.Renderer?.RecalculateChildren(parent);
parent?.VisualRoot?.Renderer.RecalculateChildren(parent);
}
/// <summary>

40
src/Avalonia.Controls/TopLevel.cs

@ -94,7 +94,6 @@ namespace Avalonia.Controls
private readonly IInputManager? _inputManager;
private readonly IAccessKeyHandler? _accessKeyHandler;
private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler;
private readonly IPlatformRenderInterface? _renderInterface;
private readonly IGlobalStyles? _globalStyles;
private readonly IGlobalThemeVariantProvider? _applicationThemeHost;
private readonly PointerOverPreProcessor? _pointerOverPreProcessor;
@ -136,36 +135,21 @@ namespace Avalonia.Controls
/// </param>
public TopLevel(ITopLevelImpl impl, IAvaloniaDependencyResolver? dependencyResolver)
{
if (impl == null)
{
throw new InvalidOperationException(
"Could not create window implementation: maybe no windowing subsystem was initialized?");
}
PlatformImpl = impl;
PlatformImpl = impl ?? throw new InvalidOperationException(
"Could not create window implementation: maybe no windowing subsystem was initialized?");
_actualTransparencyLevel = PlatformImpl.TransparencyLevel;
dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
dependencyResolver ??= AvaloniaLocator.Current;
_accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
_inputManager = TryGetService<IInputManager>(dependencyResolver);
_keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
_renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
_globalStyles = TryGetService<IGlobalStyles>(dependencyResolver);
_applicationThemeHost = TryGetService<IGlobalThemeVariantProvider>(dependencyResolver);
Renderer = impl.CreateRenderer(this);
if (Renderer != null)
{
Renderer.SceneInvalidated += SceneInvalidated;
}
else
{
// Prevent nullable error.
Renderer = null!;
}
Renderer.SceneInvalidated += SceneInvalidated;
impl.SetInputRoot(this);
@ -216,7 +200,7 @@ namespace Avalonia.Controls
if(impl.TryGetFeature<ISystemNavigationManagerImpl>() is {} systemNavigationManager)
{
systemNavigationManager.BackRequested += (s, e) =>
systemNavigationManager.BackRequested += (_, e) =>
{
e.RoutedEvent = BackRequestedEvent;
RaiseEvent(e);
@ -337,7 +321,7 @@ namespace Avalonia.Controls
{
_layoutManager = CreateLayoutManager();
if (_layoutManager is LayoutManager typedLayoutManager && Renderer is not null)
if (_layoutManager is LayoutManager typedLayoutManager)
{
_layoutDiagnosticBridge = new LayoutDiagnosticBridge(Renderer.Diagnostics, typedLayoutManager);
_layoutDiagnosticBridge.SetupBridge();
@ -356,7 +340,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the renderer for the window.
/// </summary>
public IRenderer Renderer { get; private set; }
public IRenderer Renderer { get; }
internal PixelPoint? LastPointerPosition => _pointerOverPreProcessor?.LastPosition;
@ -450,7 +434,7 @@ namespace Avalonia.Controls
/// <param name="rect">The dirty area.</param>
protected virtual void HandlePaint(Rect rect)
{
Renderer?.Paint(rect);
Renderer.Paint(rect);
}
/// <summary>
@ -468,8 +452,8 @@ namespace Avalonia.Controls
_applicationThemeHost.ActualThemeVariantChanged -= GlobalActualThemeVariantChanged;
}
Renderer?.Dispose();
Renderer = null!;
Renderer.SceneInvalidated -= SceneInvalidated;
Renderer.Dispose();
_layoutDiagnosticBridge?.Dispose();
_layoutDiagnosticBridge = null;
@ -488,7 +472,7 @@ namespace Avalonia.Controls
OnClosed(EventArgs.Empty);
LayoutManager?.Dispose();
LayoutManager.Dispose();
}
/// <summary>
@ -503,7 +487,7 @@ namespace Avalonia.Controls
Width = clientSize.Width;
Height = clientSize.Height;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
Renderer.Resized(clientSize);
}
/// <summary>

6
src/Avalonia.Controls/Window.cs

@ -573,7 +573,7 @@ namespace Avalonia.Controls
return;
}
Renderer?.Stop();
Renderer.Stop();
if (Owner is Window owner)
{
@ -721,7 +721,7 @@ namespace Avalonia.Controls
SetWindowStartupLocation(owner?.PlatformImpl);
PlatformImpl?.Show(ShowActivated, false);
Renderer?.Start();
Renderer.Start();
OnOpened(EventArgs.Empty);
}
}
@ -798,7 +798,7 @@ namespace Avalonia.Controls
PlatformImpl?.Show(ShowActivated, true);
Renderer?.Start();
Renderer.Start();
Observable.FromEventPattern(
x => Closed += x,

6
src/Avalonia.Controls/WindowBase.cs

@ -129,7 +129,7 @@ namespace Avalonia.Controls
{
using (FreezeVisibilityChangeHandling())
{
Renderer?.Stop();
Renderer.Stop();
PlatformImpl?.Hide();
IsVisible = false;
}
@ -153,7 +153,7 @@ namespace Avalonia.Controls
}
PlatformImpl?.Show(true, false);
Renderer?.Start();
Renderer.Start();
OnOpened(EventArgs.Empty);
}
}
@ -219,7 +219,7 @@ namespace Avalonia.Controls
{
ClientSize = clientSize;
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
Renderer.Resized(clientSize);
}
}

2
src/Avalonia.Diagnostics/Diagnostics/Controls/Application.cs

@ -33,7 +33,7 @@ namespace Avalonia.Diagnostics.Controls
RendererRoot = application.ApplicationLifetime switch
{
Lifetimes.IClassicDesktopStyleApplicationLifetime classic => classic.MainWindow?.Renderer,
Lifetimes.ISingleViewApplicationLifetime single => (single.MainView as Visual)?.VisualRoot?.Renderer,
Lifetimes.ISingleViewApplicationLifetime single => single.MainView?.VisualRoot?.Renderer,
_ => null
};

3
src/Avalonia.Native/WindowImpl.cs

@ -119,7 +119,8 @@ namespace Avalonia.Native
{
if(e.Type == RawPointerEventType.LeftButtonDown)
{
var visual = (_inputRoot as Window).Renderer.HitTestFirst(e.Position, _inputRoot as Window, x =>
var window = _inputRoot as Window;
var visual = window?.Renderer.HitTestFirst(e.Position, window, x =>
{
if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsEffectivelyVisible))
{

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

@ -140,10 +140,9 @@ namespace Avalonia.Controls.UnitTests
.Returns<Point, Visual, Func<Visual, bool>>((p, r, f) =>
r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]);
var target = new TestButton()
var target = new TestButton(renderer.Object)
{
Bounds = new Rect(0, 0, 100, 100),
Renderer = renderer.Object
Bounds = new Rect(0, 0, 100, 100)
};
bool clicked = false;
@ -172,10 +171,9 @@ namespace Avalonia.Controls.UnitTests
.Returns<Point, Visual, Func<Visual, bool>>((p, r, f) =>
r.Bounds.Contains(p) ? new Visual[] { r } : new Visual[0]);
var target = new TestButton()
var target = new TestButton(renderer.Object)
{
Bounds = new Rect(0, 0, 100, 100),
Renderer = renderer.Object
Bounds = new Rect(0, 0, 100, 100)
};
bool clicked = false;
@ -206,11 +204,10 @@ namespace Avalonia.Controls.UnitTests
r.Bounds.Contains(p.Transform(r.RenderTransform.Value.Invert())) ?
new Visual[] { r } : new Visual[0]);
var target = new TestButton()
var target = new TestButton(renderer.Object)
{
Bounds = new Rect(0, 0, 100, 100),
RenderTransform = new TranslateTransform { X = 100, Y = 0 },
Renderer = renderer.Object
RenderTransform = new TranslateTransform { X = 100, Y = 0 }
};
//actual bounds of button should be 100,0,100,100 x -> translated 100 pixels
@ -386,9 +383,10 @@ namespace Avalonia.Controls.UnitTests
private class TestButton : Button, IRenderRoot
{
public TestButton()
public TestButton(IRenderer renderer)
{
IsVisible = true;
Renderer = renderer;
}
public new Rect Bounds
@ -399,7 +397,7 @@ namespace Avalonia.Controls.UnitTests
public Size ClientSize => throw new NotImplementedException();
public IRenderer Renderer { get; set; }
public IRenderer Renderer { get; }
public double RenderScaling => throw new NotImplementedException();

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

@ -6,6 +6,7 @@ using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
@ -20,7 +21,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
var target = new TestTopLevel(impl.Object);
Assert.True(((ILogical)target).IsAttachedToLogicalTree);
@ -32,7 +33,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
var target = new TestTopLevel(impl.Object);
@ -46,7 +47,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
var target = new TestTopLevel(impl.Object);
@ -60,7 +61,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
var target = new TestTopLevel(impl.Object);
@ -76,7 +77,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(services))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
var target = new TestTopLevel(impl.Object, Mock.Of<ILayoutManager>());
@ -91,7 +92,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupProperty(x => x.Resized);
impl.SetupGet(x => x.RenderScaling).Returns(1);
@ -117,7 +118,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
var target = new TestTopLevel(impl.Object);
@ -133,7 +134,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
@ -151,7 +152,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
bool raised = false;
@ -169,7 +170,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
var target = new TestTopLevel(impl.Object);
@ -200,7 +201,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(services))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
var target = new TestTopLevel(impl.Object);
@ -222,7 +223,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
var target = new TestTopLevel(impl.Object);
var child = new TestTopLevel(impl.Object);
@ -240,7 +241,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
var target = new TestTopLevel(impl.Object);
var raised = false;
@ -257,7 +258,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupAllProperties();
var layoutManager = new Mock<ILayoutManager>();
@ -274,7 +275,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var impl = CreateMockTopLevelImpl();
impl.SetupGet(x => x.RenderScaling).Returns(1);
var child = new Border { Classes = { "foo" } };
@ -317,6 +318,14 @@ namespace Avalonia.Controls.UnitTests
}.RegisterInNameScope(scope));
}
private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()
{
var renderer = new Mock<ITopLevelImpl>();
renderer.Setup(r => r.CreateRenderer(It.IsAny<IRenderRoot>()))
.Returns(RendererMocks.CreateRenderer().Object);
return renderer;
}
private class TestTopLevel : TopLevel
{
private readonly ILayoutManager _layoutManager;

12
tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs

@ -4,9 +4,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
using Xunit;
using Factory = System.Func<int, System.Action<object>, Avalonia.Controls.Window, Avalonia.AvaloniaObject>;
@ -20,7 +18,7 @@ namespace Avalonia.Controls.UnitTests.Utils
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
.Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control);
var gesture2 = new KeyGesture(Key.B, KeyModifiers.Control);
@ -64,7 +62,7 @@ namespace Avalonia.Controls.UnitTests.Utils
var commandResult = 0;
var expectedParameter = 1;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
.Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -106,7 +104,7 @@ namespace Avalonia.Controls.UnitTests.Utils
var target = new KeyboardDevice();
var isExecuted = false;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
.Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -146,7 +144,7 @@ namespace Avalonia.Controls.UnitTests.Utils
var target = new KeyboardDevice();
var clickExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
.Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -199,7 +197,7 @@ namespace Avalonia.Controls.UnitTests.Utils
var clickExecutedCount = 0;
var commandExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
.Bind<IWindowingPlatform>().ToConstant(new MockWindowingPlatform());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);

14
tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs

@ -22,7 +22,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
var impl = CreateMockWindowBaseImpl();
var target = new TestWindowBase(impl.Object);
target.Activate();
@ -36,7 +36,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
var impl = CreateMockWindowBaseImpl();
impl.SetupAllProperties();
bool raised = false;
@ -55,7 +55,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<IWindowBaseImpl>();
var impl = CreateMockWindowBaseImpl();
impl.SetupAllProperties();
bool raised = false;
@ -241,6 +241,14 @@ namespace Avalonia.Controls.UnitTests
}.RegisterInNameScope(scope));
}
private static Mock<IWindowBaseImpl> CreateMockWindowBaseImpl()
{
var renderer = new Mock<IWindowBaseImpl>();
renderer.Setup(r => r.CreateRenderer(It.IsAny<IRenderRoot>()))
.Returns(RendererMocks.CreateRenderer().Object);
return renderer;
}
private class TestWindowBase : WindowBase
{
public bool IsClosed { get; private set; }

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

@ -15,6 +15,8 @@ namespace Avalonia.Controls.UnitTests
public void Setting_Title_Should_Set_Impl_Title()
{
var windowImpl = new Mock<IWindowImpl>();
windowImpl.Setup(r => r.CreateRenderer(It.IsAny<IRenderRoot>()))
.Returns(RendererMocks.CreateRenderer().Object);
var windowingPlatform = new MockWindowingPlatform(() => windowImpl.Object);
using (UnitTestApplication.Start(new TestServices(windowingPlatform: windowingPlatform)))

35
tests/Avalonia.Controls.UnitTests/WindowingPlatformMock.cs

@ -1,35 +0,0 @@
using System;
using Moq;
using Avalonia.Platform;
namespace Avalonia.Controls.UnitTests
{
public class WindowingPlatformMock : IWindowingPlatform
{
private readonly Func<IWindowImpl> _windowImpl;
private readonly Func<IPopupImpl> _popupImpl;
public WindowingPlatformMock(Func<IWindowImpl> windowImpl = null, Func<IPopupImpl> popupImpl = null )
{
_windowImpl = windowImpl;
_popupImpl = popupImpl;
}
public IWindowImpl CreateWindow()
{
return _windowImpl?.Invoke() ?? Mock.Of<IWindowImpl>(x => x.RenderScaling == 1);
}
public IWindowImpl CreateEmbeddableWindow()
{
throw new NotImplementedException();
}
public ITrayIconImpl CreateTrayIcon()
{
return null;
}
public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of<IPopupImpl>(x => x.RenderScaling == 1);
}
}

54
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@ -1,54 +0,0 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
namespace Avalonia.UnitTests
{
public class TestTemplatedRoot : ContentControl, ILayoutRoot, IRenderRoot, ILogicalRoot
{
private readonly NameScope _nameScope = new NameScope();
public TestTemplatedRoot()
{
LayoutManager = new LayoutManager(this);
Template = new FuncControlTemplate<TestTemplatedRoot>((x, scope) => new ContentPresenter
{
Name = "PART_ContentPresenter",
}.RegisterInNameScope(scope));
}
public Size ClientSize => new Size(100, 100);
public Size MaxClientSize => Size.Infinity;
public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; set; }
public double RenderScaling => 1;
public IRenderTarget RenderTarget => null;
public IRenderer Renderer => null;
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public Point PointToClient(PixelPoint p) => p.ToPoint(1);
public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
}
}
Loading…
Cancel
Save