Browse Source

Added a render loop.

Rather than repainting whenever, this adds a render loop which is called
N times per second. IRenderQueueManager and ITopLevelRenderer have been
removed, replaced by IRenderer.
pull/773/head
Steven Kirk 10 years ago
parent
commit
6afa000c42
  1. 1
      src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs
  2. 9
      src/Avalonia.Controls/Application.cs
  3. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  4. 66
      src/Avalonia.Controls/Platform/ITopLevelRenderer.cs
  5. 7
      src/Avalonia.Controls/Platform/PlatformManager.cs
  6. 33
      src/Avalonia.Controls/TopLevel.cs
  7. 8
      src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj
  8. 9
      src/Avalonia.SceneGraph/Rendering/IRenderLoop.cs
  9. 31
      src/Avalonia.SceneGraph/Rendering/IRenderQueueManager.cs
  10. 25
      src/Avalonia.SceneGraph/Rendering/IRenderRoot.cs
  11. 17
      src/Avalonia.SceneGraph/Rendering/IRenderer.cs
  12. 66
      src/Avalonia.SceneGraph/Rendering/RenderLoop.cs
  13. 51
      src/Avalonia.SceneGraph/Rendering/RenderQueueManager.cs
  14. 63
      src/Avalonia.SceneGraph/Rendering/Renderer.cs
  15. 2
      src/Avalonia.SceneGraph/Rendering/RendererMixin.cs
  16. 2
      src/Avalonia.SceneGraph/Visual.cs
  17. 14
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  18. 14
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  19. 1
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  20. 2
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  21. 13
      tests/Avalonia.SceneGraph.UnitTests/TestRoot.cs
  22. 12
      tests/Avalonia.UnitTests/TestRoot.cs
  23. 16
      tests/Avalonia.UnitTests/TestServices.cs
  24. 12
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs
  25. 3
      tests/Avalonia.UnitTests/UnitTestApplication.cs

1
src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs

@ -29,6 +29,5 @@ namespace Avalonia.Platform
bool CurrentThreadIsLoopThread { get; }
event Action Signaled;
}
}

9
src/Avalonia.Controls/Application.cs

@ -112,6 +112,11 @@ namespace Avalonia
/// </remarks>
public Styles Styles { get; } = new Styles();
/// <summary>
/// Gets the application's render loop.
/// </summary>
public IRenderLoop RenderLoop { get; private set; }
/// <summary>
/// Gets the styling parent of the application, which is null.
/// </summary>
@ -167,6 +172,7 @@ namespace Avalonia
AvaloniaSynchronizationContext.InstallIfNeeded();
FocusManager = new FocusManager();
InputManager = new InputManager();
RenderLoop = new RenderLoop(60);
AvaloniaLocator.CurrentMutable
.Bind<IAccessKeyHandler>().ToTransient<AccessKeyHandler>()
@ -177,7 +183,8 @@ namespace Avalonia
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IStyler>().ToConstant(_styler)
.Bind<ILayoutManager>().ToSingleton<LayoutManager>()
.Bind<IRenderQueueManager>().ToTransient<RenderQueueManager>()
.Bind<IRenderer>().ToTransient<Renderer>()
.Bind<IRenderLoop>().ToConstant(RenderLoop)
.Bind<IApplicationLifecycle>().ToConstant(this);
}
}

1
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -72,7 +72,6 @@
<Compile Include="Mixins\ContentControlMixin.cs" />
<Compile Include="Platform\IWindowIconImpl.cs" />
<Compile Include="Platform\IPlatformIconLoader.cs" />
<Compile Include="Platform\ITopLevelRenderer.cs" />
<Compile Include="Platform\IWindowingPlatform.cs" />
<Compile Include="Platform\PlatformManager.cs" />
<Compile Include="Presenters\IContentPresenterHost.cs" />

66
src/Avalonia.Controls/Platform/ITopLevelRenderer.cs

@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
namespace Avalonia.Controls.Platform
{
public interface ITopLevelRenderer
{
void Attach(TopLevel topLevel);
}
class DefaultTopLevelRenderer : ITopLevelRenderer
{
public void Attach(TopLevel topLevel)
{
var resources = new List<IDisposable>();
var initialClientSize = topLevel.PlatformImpl.ClientSize;
var queueManager = ((IRenderRoot)topLevel).RenderQueueManager;
if (queueManager == null)
return;
var viewport = PlatformManager.CreateRenderTarget(topLevel.PlatformImpl);
resources.Add(viewport);
resources.Add(queueManager.RenderNeeded.Subscribe(_
=>
Dispatcher.UIThread.InvokeAsync(() => topLevel.PlatformImpl.Invalidate(new Rect(topLevel.ClientSize)))));
topLevel.PlatformImpl.Paint = rect =>
{
try
{
viewport.Render(topLevel);
}
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Error("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
viewport.Dispose();
resources.Remove(viewport);
viewport = PlatformManager.CreateRenderTarget(topLevel.PlatformImpl);
resources.Add(viewport);
topLevel.PlatformImpl.Paint(rect); // Retry painting
}
queueManager.RenderFinished();
};
topLevel.Closed += delegate
{
foreach (var disposable in resources)
disposable.Dispose();
resources.Clear();
};
}
}
}

7
src/Avalonia.Controls/Platform/PlatformManager.cs

@ -13,13 +13,6 @@ namespace Avalonia.Controls.Platform
static bool s_designerMode;
private static double _designerScalingFactor = 1;
public static IRenderTarget CreateRenderTarget(ITopLevelImpl window)
{
return AvaloniaLocator.Current
.GetService<IPlatformRenderInterface>()
.CreateRenderer(window.Handle);
}
public static IDisposable DesignerMode()
{
s_designerMode = true;

33
src/Avalonia.Controls/TopLevel.cs

@ -45,11 +45,11 @@ namespace Avalonia.Controls
public static readonly StyledProperty<IInputElement> PointerOverElementProperty =
AvaloniaProperty.Register<TopLevel, IInputElement>(nameof(IInputRoot.PointerOverElement));
private readonly IRenderQueueManager _renderQueueManager;
private readonly IInputManager _inputManager;
private readonly IAccessKeyHandler _accessKeyHandler;
private readonly IKeyboardNavigationHandler _keyboardNavigationHandler;
private readonly IApplicationLifecycle _applicationLifecycle;
private readonly IPlatformRenderInterface _renderInterface;
private Size _clientSize;
private bool _isActive;
@ -86,23 +86,26 @@ namespace Avalonia.Controls
}
PlatformImpl = impl;
dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
var renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
var styler = TryGetService<IStyler>(dependencyResolver);
_accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
_inputManager = TryGetService<IInputManager>(dependencyResolver);
_keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
_renderQueueManager = TryGetService<IRenderQueueManager>(dependencyResolver);
_applicationLifecycle = TryGetService<IApplicationLifecycle>(dependencyResolver);
_renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
(dependencyResolver.GetService<ITopLevelRenderer>() ?? new DefaultTopLevelRenderer()).Attach(this);
var renderLoop = TryGetService<IRenderLoop>(dependencyResolver);
Renderer = TryGetService<IRenderer>(dependencyResolver);
Renderer?.Attach(this, renderLoop);
PlatformImpl.SetInputRoot(this);
PlatformImpl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated;
PlatformImpl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput;
PlatformImpl.Paint = Renderer != null ? (Action<Rect>)Renderer.Render : null;
PlatformImpl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged;
PlatformImpl.PositionChanged = HandlePositionChanged;
@ -180,9 +183,9 @@ namespace Avalonia.Controls
}
/// <summary>
/// Gets the window render manager.
/// Gets the renderer for the window.
/// </summary>
IRenderQueueManager IRenderRoot.RenderQueueManager => _renderQueueManager;
public IRenderer Renderer { get; }
/// <summary>
/// Gets the access key handler for the window.
@ -232,6 +235,18 @@ namespace Avalonia.Controls
private set;
}
/// <inheritdoc/>
IRenderTarget IRenderRoot.CreateRenderTarget()
{
return _renderInterface.CreateRenderer(PlatformImpl.Handle);
}
/// <inheritdoc/>
void IRenderRoot.Invalidate(Rect rect)
{
PlatformImpl.Invalidate(rect);
}
/// <inheritdoc/>
Point IRenderRoot.PointToClient(Point p)
{
@ -322,8 +337,8 @@ namespace Avalonia.Controls
}
/// <summary>
/// Tries to get a service from an <see cref="IAvaloniaDependencyResolver"/>, throwing an
/// exception if not found.
/// Tries to get a service from an <see cref="IAvaloniaDependencyResolver"/>, logging a
/// warning if not found.
/// </summary>
/// <typeparam name="T">The service type.</typeparam>
/// <param name="resolver">The resolver.</param>

8
src/Avalonia.SceneGraph/Avalonia.SceneGraph.csproj

@ -101,6 +101,11 @@
<Compile Include="Media\Geometry.cs" />
<Compile Include="Media\IDrawingContext.cs" />
<Compile Include="Platform\ExportRenderingSubsystemAttribute.cs" />
<Compile Include="Rendering\IRenderer.cs" />
<Compile Include="Rendering\IRenderLoop.cs" />
<Compile Include="Rendering\Renderer.cs" />
<Compile Include="Rendering\RendererMixin.cs" />
<Compile Include="Rendering\RenderLoop.cs" />
<Compile Include="RenderTargetCorruptedException.cs" />
<Compile Include="VisualTree\IVisual.cs" />
<Compile Include="Media\Imaging\Bitmap.cs" />
@ -138,9 +143,6 @@
<Compile Include="RelativeRect.cs" />
<Compile Include="Rect.cs" />
<Compile Include="Rendering\IRenderRoot.cs" />
<Compile Include="Rendering\IRenderQueueManager.cs" />
<Compile Include="Rendering\RendererMixin.cs" />
<Compile Include="Rendering\RenderQueueManager.cs" />
<Compile Include="Size.cs" />
<Compile Include="Thickness.cs" />
<Compile Include="Vector.cs" />

9
src/Avalonia.SceneGraph/Rendering/IRenderLoop.cs

@ -0,0 +1,9 @@
using System;
namespace Avalonia.Rendering
{
public interface IRenderLoop
{
event EventHandler<EventArgs> Tick;
}
}

31
src/Avalonia.SceneGraph/Rendering/IRenderQueueManager.cs

@ -1,31 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
/// <summary>
/// Defines the interface for a <see cref="RenderQueueManager"/>.
/// </summary>
public interface IRenderQueueManager
{
/// <summary>
/// Gets an observable that is fired whenever a render is required.
/// </summary>
IObservable<Unit> RenderNeeded { get; }
/// <summary>
/// Invalidates the render for the specified visual and raises <see cref="RenderNeeded"/>.
/// </summary>
/// <param name="visual">The visual.</param>
void InvalidateRender(IVisual visual);
/// <summary>
/// Called when rendering is finished.
/// </summary>
void RenderFinished();
}
}

25
src/Avalonia.SceneGraph/Rendering/IRenderRoot.cs

@ -1,19 +1,38 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
/// <summary>
/// Represents the root of a renderable tree.
/// </summary>
public interface IRenderRoot
public interface IRenderRoot : IVisual
{
/// <summary>
/// Gets the render manager which schedules renders.
/// Gets the client size of the window.
/// </summary>
IRenderQueueManager RenderQueueManager { get; }
Size ClientSize { get; }
/// <summary>
/// Gets the renderer for the window.
/// </summary>
IRenderer Renderer { get; }
/// <summary>
/// Creates a render target for the window.
/// </summary>
/// <returns>An <see cref="IRenderTarget"/>.</returns>
IRenderTarget CreateRenderTarget();
/// <summary>
/// Adds a rectangle to the window's dirty region.
/// </summary>
/// <param name="rect">The rectangle.</param>
void Invalidate(Rect rect);
/// <summary>
/// Converts a point from screen to client coordinates.

17
src/Avalonia.SceneGraph/Rendering/IRenderer.cs

@ -0,0 +1,17 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public interface IRenderer : IDisposable
{
void AddDirty(IVisual visual);
void Attach(IRenderRoot root, IRenderLoop renderLoop);
void Render(Rect rect);
}
}

66
src/Avalonia.SceneGraph/Rendering/RenderLoop.cs

@ -0,0 +1,66 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Platform;
namespace Avalonia.Rendering
{
public class RenderLoop : IRenderLoop
{
private readonly int _fps;
private IPlatformThreadingInterface _threading;
private int _subscriberCount;
private EventHandler<EventArgs> _tick;
private IDisposable _subscription;
public RenderLoop(int fps)
{
_fps = fps;
}
public event EventHandler<EventArgs> Tick
{
add
{
if (_subscriberCount++ == 0)
{
Start();
}
_tick += value;
}
remove
{
if (--_subscriberCount == 0)
{
Stop();
}
_tick -= value;
}
}
protected void Start()
{
if (_threading == null)
{
_threading = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
}
_subscription = _threading.StartTimer(TimeSpan.FromSeconds(1.0 / _fps), InternalTick);
}
protected void Stop()
{
_subscription.Dispose();
_subscription = null;
}
private void InternalTick()
{
_tick(this, EventArgs.Empty);
}
}
}

51
src/Avalonia.SceneGraph/Rendering/RenderQueueManager.cs

@ -1,51 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive;
using System.Reactive.Subjects;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
/// <summary>
/// Schedules the rendering of a tree.
/// </summary>
public class RenderQueueManager : IRenderQueueManager
{
private readonly Subject<Unit> _renderNeeded = new Subject<Unit>();
private bool _renderQueued;
/// <summary>
/// Gets an observable that is fired whenever a render is required.
/// </summary>
public IObservable<Unit> RenderNeeded => _renderNeeded;
/// <summary>
/// Gets a value indicating whether a render is queued.
/// </summary>
public bool RenderQueued => _renderQueued;
/// <summary>
/// Invalidates the render for the specified visual and raises <see cref="RenderNeeded"/>.
/// </summary>
/// <param name="visual">The visual.</param>
public void InvalidateRender(IVisual visual)
{
if (!_renderQueued)
{
_renderQueued = true;
_renderNeeded.OnNext(Unit.Default);
}
}
/// <summary>
/// Called when rendering is finished.
/// </summary>
public void RenderFinished()
{
_renderQueued = false;
}
}
}

63
src/Avalonia.SceneGraph/Rendering/Renderer.cs

@ -0,0 +1,63 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class Renderer : IDisposable, IRenderer
{
private IRenderLoop _renderLoop;
private IRenderRoot _root;
private IRenderTarget _renderTarget;
private bool _dirty;
public void Attach(IRenderRoot root, IRenderLoop renderLoop)
{
Contract.Requires<ArgumentNullException>(root != null);
_root = root;
_renderLoop = renderLoop;
_renderLoop.Tick += OnRenderLoopTick;
}
public void AddDirty(IVisual visual)
{
_dirty = true;
}
public void Dispose()
{
_renderLoop.Tick -= OnRenderLoopTick;
}
public void Render(Rect rect)
{
if (_renderTarget == null)
{
_renderTarget = _root.CreateRenderTarget();
}
try
{
_renderTarget.Render(_root);
}
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget.Dispose();
_renderTarget = null;
}
}
private void OnRenderLoopTick(object sender, EventArgs e)
{
if (_dirty)
{
_root.Invalidate(new Rect(_root.ClientSize));
}
}
}
}

2
src/Avalonia.SceneGraph/Rendering/RendererMixin.cs

@ -1,4 +1,4 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;

2
src/Avalonia.SceneGraph/Visual.cs

@ -259,7 +259,7 @@ namespace Avalonia
/// </summary>
public void InvalidateVisual()
{
VisualRoot?.RenderQueueManager?.InvalidateRender(this);
VisualRoot?.Renderer?.AddDirty(this);
}
/// <summary>

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

@ -10,6 +10,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
using Xunit;
@ -315,7 +316,18 @@ namespace Avalonia.Controls.UnitTests.Presenters
private class TestScroller : ScrollContentPresenter, IRenderRoot
{
public IRenderQueueManager RenderQueueManager { get; }
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public Point PointToClient(Point point)
{

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

@ -12,6 +12,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
using Xunit;
@ -977,7 +978,18 @@ namespace Avalonia.Controls.UnitTests.Presenters
private class TestScroller : ScrollContentPresenter, IRenderRoot
{
public IRenderQueueManager RenderQueueManager { get; }
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public Point PointToClient(Point point)
{

1
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -268,7 +268,6 @@ namespace Avalonia.Controls.UnitTests.Primitives
globalStyles.Setup(x => x.Styles).Returns(styles);
var renderInterface = new Mock<IPlatformRenderInterface>();
renderInterface.Setup(x => x.CreateRenderer(It.IsAny<IPlatformHandle>())).Returns(() => new Mock<IRenderTarget>().Object);
AvaloniaLocator.CurrentMutable
.Bind<ILayoutManager>().ToTransient<LayoutManager>()

2
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -137,7 +137,6 @@ namespace Avalonia.Layout.UnitTests
var formattedText = fixture.Create<IFormattedTextImpl>();
var globalStyles = new Mock<IGlobalStyles>();
var renderInterface = fixture.Create<IPlatformRenderInterface>();
var renderManager = fixture.Create<IRenderQueueManager>();
var windowImpl = new Mock<IWindowImpl>();
windowImpl.SetupProperty(x => x.ClientSize);
@ -151,7 +150,6 @@ namespace Avalonia.Layout.UnitTests
.Bind<ILayoutManager>().ToConstant(new LayoutManager())
.Bind<IRuntimePlatform>().ToConstant(new AppBuilder().RuntimePlatform)
.Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
.Bind<IRenderQueueManager>().ToConstant(renderManager)
.Bind<IStyler>().ToConstant(new Styler())
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock(() => windowImpl.Object));

13
tests/Avalonia.SceneGraph.UnitTests/TestRoot.cs

@ -9,12 +9,19 @@ namespace Avalonia.SceneGraph.UnitTests
{
public class TestRoot : TestVisual, IRenderRoot
{
public IRenderTarget RenderTarget
public Size ClientSize { get; }
public IRenderTarget CreateRenderTarget()
{
get { throw new NotImplementedException(); }
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public IRenderQueueManager RenderQueueManager
public IRenderer Renderer
{
get { throw new NotImplementedException(); }
}

12
tests/Avalonia.UnitTests/TestRoot.cs

@ -41,7 +41,17 @@ namespace Avalonia.UnitTests
public IRenderTarget RenderTarget => null;
public IRenderQueueManager RenderQueueManager => null;
public IRenderer Renderer => null;
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public Point PointToClient(Point p) => p;

16
tests/Avalonia.UnitTests/TestServices.cs

@ -11,6 +11,7 @@ using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using Avalonia.Styling;
using Avalonia.Themes.Default;
using Avalonia.Rendering;
namespace Avalonia.UnitTests
{
@ -20,7 +21,9 @@ namespace Avalonia.UnitTests
assetLoader: new AssetLoader(),
layoutManager: new LayoutManager(),
platform: new AppBuilder().RuntimePlatform,
renderer: Mock.Of<IRenderer>(),
renderInterface: CreateRenderInterfaceMock(),
renderLoop: Mock.Of<IRenderLoop>(),
standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
styler: new Styler(),
theme: () => CreateDefaultTheme(),
@ -57,7 +60,9 @@ namespace Avalonia.UnitTests
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null,
IRuntimePlatform platform = null,
IRenderer renderer = null,
IPlatformRenderInterface renderInterface = null,
IRenderLoop renderLoop = null,
IStandardCursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<Styles> theme = null,
@ -71,7 +76,9 @@ namespace Avalonia.UnitTests
KeyboardDevice = keyboardDevice;
LayoutManager = layoutManager;
Platform = platform;
Renderer = renderer;
RenderInterface = renderInterface;
RenderLoop = renderLoop;
StandardCursorFactory = standardCursorFactory;
Styler = styler;
Theme = theme;
@ -86,7 +93,9 @@ namespace Avalonia.UnitTests
public Func<IKeyboardDevice> KeyboardDevice { get; }
public ILayoutManager LayoutManager { get; }
public IRuntimePlatform Platform { get; }
public IRenderer Renderer { get; }
public IPlatformRenderInterface RenderInterface { get; }
public IRenderLoop RenderLoop { get; }
public IStandardCursorFactory StandardCursorFactory { get; }
public IStyler Styler { get; }
public Func<Styles> Theme { get; }
@ -101,7 +110,9 @@ namespace Avalonia.UnitTests
Func<IKeyboardDevice> keyboardDevice = null,
ILayoutManager layoutManager = null,
IRuntimePlatform platform = null,
IRenderer renderer = null,
IPlatformRenderInterface renderInterface = null,
IRenderLoop renderLoop = null,
IStandardCursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<Styles> theme = null,
@ -116,7 +127,9 @@ namespace Avalonia.UnitTests
keyboardDevice: keyboardDevice ?? KeyboardDevice,
layoutManager: layoutManager ?? LayoutManager,
platform: platform ?? Platform,
renderer: renderer ?? Renderer,
renderInterface: renderInterface ?? RenderInterface,
renderLoop: renderLoop ?? RenderLoop,
standardCursorFactory: standardCursorFactory ?? StandardCursorFactory,
styler: styler ?? Styler,
theme: theme ?? Theme,
@ -152,8 +165,7 @@ namespace Avalonia.UnitTests
It.IsAny<FontWeight>(),
It.IsAny<TextWrapping>()) == Mock.Of<IFormattedTextImpl>() &&
x.CreateStreamGeometry() == Mock.Of<IStreamGeometryImpl>(
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()) &&
x.CreateRenderer(It.IsAny<IPlatformHandle>()) == Mock.Of<IRenderTarget>());
y => y.Open() == Mock.Of<IStreamGeometryContextImpl>()));
}
}
}

12
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@ -43,7 +43,17 @@ namespace Avalonia.UnitTests
public IRenderTarget RenderTarget => null;
public IRenderQueueManager RenderQueueManager => null;
public IRenderer Renderer => null;
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
}
public void Invalidate(Rect rect)
{
throw new NotImplementedException();
}
public Point PointToClient(Point p) => p;

3
tests/Avalonia.UnitTests/UnitTestApplication.cs

@ -7,6 +7,7 @@ using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Controls;
using Avalonia.Rendering;
namespace Avalonia.UnitTests
{
@ -42,7 +43,9 @@ namespace Avalonia.UnitTests
.Bind<IKeyboardDevice>().ToConstant(Services.KeyboardDevice?.Invoke())
.Bind<ILayoutManager>().ToConstant(Services.LayoutManager)
.Bind<IRuntimePlatform>().ToConstant(Services.Platform)
.Bind<IRenderer>().ToConstant(Services.Renderer)
.Bind<IPlatformRenderInterface>().ToConstant(Services.RenderInterface)
.Bind<IRenderLoop>().ToConstant(Services.RenderLoop)
.Bind<IPlatformThreadingInterface>().ToConstant(Services.ThreadingInterface)
.Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory)
.Bind<IStyler>().ToConstant(Services.Styler)

Loading…
Cancel
Save