diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index b46ac16a79..4f0b1f0a5b 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -2,8 +2,8 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Avalonia
- 0.7.1
- Copyright 2018 © The AvaloniaUI Project
+ 0.8.1
+ Copyright 2019 © The AvaloniaUI Project
https://github.com/AvaloniaUI/Avalonia/blob/master/licence.md
https://github.com/AvaloniaUI/Avalonia/
https://github.com/AvaloniaUI/Avalonia/
@@ -11,4 +11,4 @@
CS1591
latest
-
\ No newline at end of file
+
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index bb31034299..84092d52eb 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -122,6 +122,14 @@ partial class Build : NukeBuild
foreach(var fw in frameworks)
{
+ if (fw.StartsWith("net4")
+ && RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
+ && Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
+ {
+ Information($"Skipping {fw} tests on Linux - https://github.com/mono/mono/issues/13969");
+ continue;
+ }
+
Information("Running for " + fw);
DotNetTest(c =>
{
diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs
index 01c52a2a49..f2f44cd502 100644
--- a/samples/BindingDemo/App.xaml.cs
+++ b/samples/BindingDemo/App.xaml.cs
@@ -3,6 +3,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
using Serilog;
namespace BindingDemo
diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs
index dd5644dd6b..b7aa34f5ba 100644
--- a/samples/ControlCatalog.Desktop/Program.cs
+++ b/samples/ControlCatalog.Desktop/Program.cs
@@ -4,6 +4,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.Platform;
+using Avalonia.ReactiveUI;
using Serilog;
namespace ControlCatalog
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index d13a5b5ef3..c8f3fb9921 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using Avalonia;
using Avalonia.Skia;
+using Avalonia.ReactiveUI;
namespace ControlCatalog.NetCore
{
diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs
index 0f627961e6..d95018520a 100644
--- a/samples/RenderDemo/App.xaml.cs
+++ b/samples/RenderDemo/App.xaml.cs
@@ -4,6 +4,7 @@
using Avalonia;
using Avalonia.Logging.Serilog;
using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
namespace RenderDemo
{
diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml
index 41164c7780..c15abad188 100644
--- a/samples/RenderDemo/MainWindow.xaml
+++ b/samples/RenderDemo/MainWindow.xaml
@@ -33,6 +33,9 @@
+
+
+
diff --git a/samples/RenderDemo/Pages/CustomSkiaPage.cs b/samples/RenderDemo/Pages/CustomSkiaPage.cs
new file mode 100644
index 0000000000..2e59d934a1
--- /dev/null
+++ b/samples/RenderDemo/Pages/CustomSkiaPage.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Diagnostics;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.Rendering.SceneGraph;
+using Avalonia.Skia;
+using Avalonia.Threading;
+using SkiaSharp;
+
+namespace RenderDemo.Pages
+{
+ public class CustomSkiaPage : Control
+ {
+ public CustomSkiaPage()
+ {
+ ClipToBounds = true;
+ }
+
+ class CustomDrawOp : ICustomDrawOperation
+ {
+ private readonly FormattedText _noSkia;
+
+ public CustomDrawOp(Rect bounds, FormattedText noSkia)
+ {
+ _noSkia = noSkia;
+ Bounds = bounds;
+ }
+
+ public void Dispose()
+ {
+ // No-op
+ }
+
+ public Rect Bounds { get; }
+ public bool HitTest(Point p) => false;
+ public bool Equals(ICustomDrawOperation other) => false;
+ static Stopwatch St = Stopwatch.StartNew();
+ public void Render(IDrawingContextImpl context)
+ {
+ var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
+ if (canvas == null)
+ context.DrawText(Brushes.Black, new Point(), _noSkia.PlatformImpl);
+ else
+ {
+ canvas.Save();
+ // create the first shader
+ var colors = new SKColor[] {
+ new SKColor(0, 255, 255),
+ new SKColor(255, 0, 255),
+ new SKColor(255, 255, 0),
+ new SKColor(0, 255, 255)
+ };
+
+ var sx = Animate(100, 2, 10);
+ var sy = Animate(1000, 5, 15);
+ var lightPosition = new SKPoint(
+ (float)(Bounds.Width / 2 + Math.Cos(St.Elapsed.TotalSeconds) * Bounds.Width / 4),
+ (float)(Bounds.Height / 2 + Math.Sin(St.Elapsed.TotalSeconds) * Bounds.Height / 4));
+ using (var sweep =
+ SKShader.CreateSweepGradient(new SKPoint((int)Bounds.Width / 2, (int)Bounds.Height / 2), colors,
+ null))
+ using(var turbulence = SKShader.CreatePerlinNoiseFractalNoise(0.05f, 0.05f, 4, 0))
+ using(var shader = SKShader.CreateCompose(sweep, turbulence, SKBlendMode.SrcATop))
+ using(var blur = SKImageFilter.CreateBlur(Animate(100, 2, 10), Animate(100, 5, 15)))
+ using (var paint = new SKPaint
+ {
+ Shader = shader,
+ ImageFilter = blur
+ })
+ canvas.DrawPaint(paint);
+
+ using (var pseudoLight = SKShader.CreateRadialGradient(
+ lightPosition,
+ (float) (Bounds.Width/3),
+ new [] {
+ new SKColor(255, 200, 200, 100),
+ SKColors.Transparent,
+ new SKColor(40,40,40, 220),
+ new SKColor(20,20,20, (byte)Animate(100, 200,220)) },
+ new float[] { 0.3f, 0.3f, 0.8f, 1 },
+ SKShaderTileMode.Clamp))
+ using (var paint = new SKPaint
+ {
+ Shader = pseudoLight
+ })
+ canvas.DrawPaint(paint);
+ canvas.Restore();
+ }
+ }
+ static int Animate(int d, int from, int to)
+ {
+ var ms = (int)(St.ElapsedMilliseconds / d);
+ var diff = to - from;
+ var range = diff * 2;
+ var v = ms % range;
+ if (v > diff)
+ v = range - v;
+ var rv = v + from;
+ if (rv < from || rv > to)
+ throw new Exception("WTF");
+ return rv;
+ }
+ }
+
+
+
+ public override void Render(DrawingContext context)
+ {
+ var noSkia = new FormattedText()
+ {
+ Text = "Current rendering API is not Skia"
+ };
+ context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia));
+ Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
+ }
+ }
+}
diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs
index 98f1f08d6c..9d8f7c1a3d 100644
--- a/samples/VirtualizationDemo/Program.cs
+++ b/samples/VirtualizationDemo/Program.cs
@@ -5,6 +5,7 @@ using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
+using Avalonia.ReactiveUI;
using Serilog;
namespace VirtualizationDemo
diff --git a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
index 0ade1af249..b59ed166bc 100644
--- a/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
+++ b/src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
@@ -19,6 +19,7 @@ namespace Avalonia.Utilities
///
/// The type of the target.
/// The type of the event arguments.
+ /// The type of the subscriber.
/// The event source.
/// The name of the event.
/// The subscriber.
@@ -40,6 +41,7 @@ namespace Avalonia.Utilities
/// Unsubscribes from an event.
///
/// The type of the event arguments.
+ /// The type of the subscriber.
/// The event source.
/// The name of the event.
/// The subscriber.
diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs
index fce568e56d..041b81155a 100644
--- a/src/Avalonia.Controls/ListBox.cs
+++ b/src/Avalonia.Controls/ListBox.cs
@@ -30,13 +30,13 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly new AvaloniaProperty SelectedItemsProperty =
+ public static readonly new DirectProperty SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty;
///
/// Defines the property.
///
- public static readonly new AvaloniaProperty SelectionModeProperty =
+ public static readonly new StyledProperty SelectionModeProperty =
SelectingItemsControl.SelectionModeProperty;
///
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index 94989254dc..c3fbce1d83 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -40,17 +40,15 @@ namespace Avalonia.Controls
/// Defines the property.
///
public static readonly DirectProperty SelectedItemsProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(SelectedItems),
+ ListBox.SelectedItemsProperty.AddOwner(
o => o.SelectedItems,
(o, v) => o.SelectedItems = v);
///
/// Defines the property.
///
- protected static readonly StyledProperty SelectionModeProperty =
- AvaloniaProperty.Register(
- nameof(SelectionMode));
+ public static readonly StyledProperty SelectionModeProperty =
+ ListBox.SelectionModeProperty.AddOwner();
private static readonly IList Empty = new object[0];
private object _selectedItem;
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index f5af6774b5..e40e114769 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -291,7 +291,8 @@ namespace Avalonia.Controls
///
/// The dialog result.
///
- /// When the window is shown with the method, the
+ /// When the window is shown with the
+ /// or method, the
/// resulting task will produce the value when the window
/// is closed.
///
@@ -370,8 +371,16 @@ namespace Avalonia.Controls
///
/// Shows the window.
///
+ ///
+ /// The window has already been closed.
+ ///
public override void Show()
{
+ if (PlatformImpl == null)
+ {
+ throw new InvalidOperationException("Cannot re-show a closed window.");
+ }
+
if (IsVisible)
{
return;
@@ -396,6 +405,9 @@ namespace Avalonia.Controls
/// Shows the window as a dialog.
///
/// The dialog's owner window.
+ ///
+ /// The window has already been closed.
+ ///
///
/// A task that can be used to track the lifetime of the dialog.
///
diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
index d763febdf3..f67cb7f40a 100644
--- a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
+++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
@@ -6,10 +6,15 @@ using Avalonia.Threading;
using ReactiveUI;
using Splat;
-namespace Avalonia
+namespace Avalonia.ReactiveUI
{
public static class AppBuilderExtensions
{
+ ///
+ /// Initializes ReactiveUI framework to use with Avalonia. Registers Avalonia
+ /// scheduler and Avalonia activation for view fetcher. Always remember to
+ /// call this method if you are using ReactiveUI in your application.
+ ///
public static TAppBuilder UseReactiveUI(this TAppBuilder builder)
where TAppBuilder : AppBuilderBase, new()
{
diff --git a/src/Avalonia.ReactiveUI/Attributes.cs b/src/Avalonia.ReactiveUI/Attributes.cs
new file mode 100644
index 0000000000..e908d1de80
--- /dev/null
+++ b/src/Avalonia.ReactiveUI/Attributes.cs
@@ -0,0 +1,8 @@
+// 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.Reflection;
+using System.Runtime.CompilerServices;
+using Avalonia.Metadata;
+
+[assembly: XmlnsDefinition("http://reactiveui.net", "Avalonia.ReactiveUI")]
\ No newline at end of file
diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
index e1db604e95..cfa7a270be 100644
--- a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
+++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
@@ -9,15 +9,24 @@ using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
-namespace Avalonia
+namespace Avalonia.ReactiveUI
{
+ ///
+ /// Determines when Avalonia IVisuals get activated.
+ ///
public class AvaloniaActivationForViewFetcher : IActivationForViewFetcher
{
+ ///
+ /// Returns affinity for view.
+ ///
public int GetAffinityForView(Type view)
{
return typeof(IVisual).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0;
}
+ ///
+ /// Returns activation observable for activatable Avalonia view.
+ ///
public IObservable GetActivationForView(IActivatable view)
{
if (!(view is IVisual visual)) return Observable.Return(false);
@@ -25,6 +34,9 @@ namespace Avalonia
return GetActivationForVisual(visual);
}
+ ///
+ /// Listens to Opened and Closed events for Avalonia windows.
+ ///
private IObservable GetActivationForWindowBase(WindowBase window)
{
var windowLoaded = Observable
@@ -42,6 +54,10 @@ namespace Avalonia
.DistinctUntilChanged();
}
+ ///
+ /// Listens to AttachedToVisualTree and DetachedFromVisualTree
+ /// events for Avalonia IVisuals.
+ ///
private IObservable GetActivationForVisual(IVisual visual)
{
var visualLoaded = Observable
diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
index 43e2ef93b6..010acc3ae0 100644
--- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
@@ -6,7 +6,7 @@ using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
-namespace Avalonia
+namespace Avalonia.ReactiveUI
{
///
/// A ReactiveUI UserControl that implements
diff --git a/src/Avalonia.ReactiveUI/ReactiveWindow.cs b/src/Avalonia.ReactiveUI/ReactiveWindow.cs
index bb50a37764..f0f115afbc 100644
--- a/src/Avalonia.ReactiveUI/ReactiveWindow.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveWindow.cs
@@ -6,7 +6,7 @@ using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
-namespace Avalonia
+namespace Avalonia.ReactiveUI
{
///
/// A ReactiveUI Window that implements
diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
index e364d5de0b..4bd86a67c0 100644
--- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs
+++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
@@ -1,13 +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 System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Styling;
+using Avalonia;
using ReactiveUI;
using Splat;
-namespace Avalonia
+namespace Avalonia.ReactiveUI
{
///
/// This control hosts the View associated with ReactiveUI RoutingState,
@@ -157,7 +161,7 @@ namespace Avalonia
return;
}
- var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current;
+ var viewLocator = ViewLocator ?? global::ReactiveUI.ViewLocator.Current;
var view = viewLocator.ResolveView(viewModel);
if (view == null) throw new Exception($"Couldn't find view for '{viewModel}'. Is it registered?");
diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs
index e52a1961ba..d314a8d44e 100644
--- a/src/Avalonia.Styling/StyledElement.cs
+++ b/src/Avalonia.Styling/StyledElement.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs
index fd593db991..d3af71ffcb 100644
--- a/src/Avalonia.Visuals/Media/DrawingContext.cs
+++ b/src/Avalonia.Visuals/Media/DrawingContext.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
+using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Visuals.Media.Imaging;
@@ -131,6 +132,12 @@ namespace Avalonia.Media
}
}
+ ///
+ /// Draws a custom drawing operation
+ ///
+ /// custom operation
+ public void Custom(ICustomDrawOperation custom) => PlatformImpl.Custom(custom);
+
///
/// Draws text.
///
diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
index ca84d4cc7b..c2df9db635 100644
--- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs
+++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
@@ -1,7 +1,6 @@
// 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.Media
@@ -57,36 +56,8 @@ namespace Avalonia.Media
protected override IGeometryImpl CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetService();
- var geometry = factory.CreateStreamGeometry();
- using (var ctx = geometry.Open())
- {
- var rect = Rect;
- double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3;
- var center = rect.Center;
- var radius = new Vector(rect.Width / 2, rect.Height / 2);
-
- var x0 = center.X - radius.X;
- var x1 = center.X - (radius.X * controlPointRatio);
- var x2 = center.X;
- var x3 = center.X + (radius.X * controlPointRatio);
- var x4 = center.X + radius.X;
-
- var y0 = center.Y - radius.Y;
- var y1 = center.Y - (radius.Y * controlPointRatio);
- var y2 = center.Y;
- var y3 = center.Y + (radius.Y * controlPointRatio);
- var y4 = center.Y + radius.Y;
-
- ctx.BeginFigure(new Point(x2, y0), true);
- ctx.CubicBezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2));
- ctx.CubicBezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4));
- ctx.CubicBezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2));
- ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0));
- ctx.EndFigure(true);
- }
-
- return geometry;
+ return factory.CreateEllipseGeometry(Rect);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Visuals/Media/LineGeometry.cs
index f7ba4ccb0e..90577dabd8 100644
--- a/src/Avalonia.Visuals/Media/LineGeometry.cs
+++ b/src/Avalonia.Visuals/Media/LineGeometry.cs
@@ -73,16 +73,8 @@ namespace Avalonia.Media
protected override IGeometryImpl CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetService();
- var geometry = factory.CreateStreamGeometry();
- using (var context = geometry.Open())
- {
- context.BeginFigure(StartPoint, false);
- context.LineTo(EndPoint);
- context.EndFigure(false);
- }
-
- return geometry;
+ return factory.CreateLineGeometry(StartPoint, EndPoint);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
index 3ccfd80f93..9250500644 100644
--- a/src/Avalonia.Visuals/Media/RectangleGeometry.cs
+++ b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
@@ -16,12 +16,6 @@ namespace Avalonia.Media
public static readonly StyledProperty RectProperty =
AvaloniaProperty.Register(nameof(Rect));
- public Rect Rect
- {
- get => GetValue(RectProperty);
- set => SetValue(RectProperty, value);
- }
-
static RectangleGeometry()
{
AffectsGeometry(RectProperty);
@@ -43,25 +37,23 @@ namespace Avalonia.Media
Rect = rect;
}
+ ///
+ /// Gets or sets the bounds of the rectangle.
+ ///
+ public Rect Rect
+ {
+ get => GetValue(RectProperty);
+ set => SetValue(RectProperty, value);
+ }
+
///
public override Geometry Clone() => new RectangleGeometry(Rect);
protected override IGeometryImpl CreateDefiningGeometry()
{
var factory = AvaloniaLocator.Current.GetService();
- var geometry = factory.CreateStreamGeometry();
-
- using (var context = geometry.Open())
- {
- var rect = Rect;
- context.BeginFigure(rect.TopLeft, true);
- context.LineTo(rect.TopRight);
- context.LineTo(rect.BottomRight);
- context.LineTo(rect.BottomLeft);
- context.EndFigure(true);
- }
- return geometry;
+ return factory.CreateRectangleGeometry(Rect);
}
}
}
diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
index 57b974f900..e5be04ebf9 100644
--- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
@@ -3,6 +3,7 @@
using System;
using Avalonia.Media;
+using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
@@ -139,5 +140,11 @@ namespace Avalonia.Platform
/// Pops the latest pushed geometry clip.
///
void PopGeometryClip();
+
+ ///
+ /// Adds a custom draw operation
+ ///
+ /// Custom draw operation
+ void Custom(ICustomDrawOperation custom);
}
}
diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
index 3a1f79e32a..87db9251e1 100644
--- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
+++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
@@ -36,6 +36,28 @@ namespace Avalonia.Platform
Size constraint,
IReadOnlyList spans);
+ ///
+ /// Creates an ellipse geometry implementation.
+ ///
+ /// The bounds of the ellipse.
+ /// An ellipse geometry..
+ IGeometryImpl CreateEllipseGeometry(Rect rect);
+
+ ///
+ /// Creates a line geometry implementation.
+ ///
+ /// The start of the line.
+ /// The end of the line.
+ /// A line geometry.
+ IGeometryImpl CreateLineGeometry(Point p1, Point p2);
+
+ ///
+ /// Creates a rectangle geometry implementation.
+ ///
+ /// The bounds of the rectangle.
+ /// A rectangle.
+ IGeometryImpl CreateRectangleGeometry(Rect rect);
+
///
/// Creates a stream geometry implementation.
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs
new file mode 100644
index 0000000000..68e2237430
--- /dev/null
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs
@@ -0,0 +1,39 @@
+using System;
+using Avalonia.Media;
+using Avalonia.Platform;
+
+namespace Avalonia.Rendering.SceneGraph
+{
+ internal sealed class CustomDrawOperation : DrawOperation
+ {
+ public Matrix Transform { get; }
+ public ICustomDrawOperation Custom { get; }
+ public CustomDrawOperation(ICustomDrawOperation custom, Matrix transform)
+ : base(custom.Bounds, transform, null)
+ {
+ Transform = transform;
+ Custom = custom;
+ }
+
+ public override bool HitTest(Point p)
+ {
+ return Custom.HitTest(p * Transform);
+ }
+
+ public override void Render(IDrawingContextImpl context)
+ {
+ context.Transform = Transform;
+ Custom.Render(context);
+ }
+
+ public override void Dispose() => Custom.Dispose();
+
+ public bool Equals(Matrix transform, ICustomDrawOperation custom) =>
+ Transform == transform && Custom?.Equals(custom) == true;
+ }
+
+ public interface ICustomDrawOperation : IDrawOperation, IEquatable
+ {
+
+ }
+}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
index dfed0d911c..0b33851911 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
@@ -165,6 +165,15 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
+
+ public void Custom(ICustomDrawOperation custom)
+ {
+ var next = NextDrawAs();
+ if (next == null || !next.Item.Equals(Transform, custom))
+ Add(new CustomDrawOperation(custom, Transform));
+ else
+ ++_drawOperationindex;
+ }
///
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
index a1f8bf6cf6..4c352f199f 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
@@ -162,7 +162,8 @@ namespace Avalonia.Markup.Xaml
var readerSettings = new XamlXmlReaderSettings()
{
BaseUri = uri,
- LocalAssembly = localAssembly
+ LocalAssembly = localAssembly,
+ ProvideLineInfo = true,
};
var context = IsDesignMode ? AvaloniaXamlSchemaContext.DesignInstance : AvaloniaXamlSchemaContext.Instance;
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
index ab55261737..7452b23169 160000
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
@@ -1 +1 @@
-Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975
+Subproject commit 7452b23169e4948907fa10e2c115b672897d0e04
diff --git a/src/Skia/Avalonia.Skia/CustomRenderTarget.cs b/src/Skia/Avalonia.Skia/CustomRenderTarget.cs
new file mode 100644
index 0000000000..23a509a2a4
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/CustomRenderTarget.cs
@@ -0,0 +1,42 @@
+// 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 Avalonia.Platform;
+using Avalonia.Rendering;
+
+namespace Avalonia.Skia
+{
+ ///
+ /// Adapts to be used within Skia rendering pipeline.
+ ///
+ internal class CustomRenderTarget : IRenderTarget
+ {
+ private readonly ICustomSkiaRenderTarget _renderTarget;
+
+ public CustomRenderTarget(ICustomSkiaRenderTarget renderTarget)
+ {
+ _renderTarget = renderTarget;
+ }
+
+ public void Dispose()
+ {
+ _renderTarget.Dispose();
+ }
+
+ public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
+ {
+ ICustomSkiaRenderSession session = _renderTarget.BeginRendering();
+
+ var nfo = new DrawingContextImpl.CreateInfo
+ {
+ GrContext = session.GrContext,
+ Canvas = session.Canvas,
+ Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
+ VisualBrushRenderer = visualBrushRenderer,
+ DisableTextLcdRendering = true
+ };
+
+ return new DrawingContextImpl(nfo, session);
+ }
+ }
+}
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index e69d155305..5272f4b22d 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -9,6 +9,7 @@ using System.Threading;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
+using Avalonia.Rendering.SceneGraph;
using Avalonia.Rendering.Utilities;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
@@ -19,7 +20,7 @@ namespace Avalonia.Skia
///
/// Skia based drawing context.
///
- public class DrawingContextImpl : IDrawingContextImpl
+ internal class DrawingContextImpl : IDrawingContextImpl, ISkiaDrawingContextImpl
{
private IDisposable[] _disposables;
private readonly Vector _dpi;
@@ -99,6 +100,8 @@ namespace Avalonia.Skia
///
public SKCanvas Canvas { get; }
+ SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas;
+
///
public void Clear(Color color)
{
@@ -296,6 +299,8 @@ namespace Avalonia.Skia
Canvas.Restore();
}
+ public void Custom(ICustomDrawOperation custom) => custom.Render(this);
+
///
public void PushOpacityMask(IBrush mask, Rect bounds)
{
diff --git a/src/Skia/Avalonia.Skia/EllipseGeometryImpl.cs b/src/Skia/Avalonia.Skia/EllipseGeometryImpl.cs
new file mode 100644
index 0000000000..aae1dd8cef
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/EllipseGeometryImpl.cs
@@ -0,0 +1,25 @@
+// 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 SkiaSharp;
+
+namespace Avalonia.Skia
+{
+ ///
+ /// A Skia implementation of a .
+ ///
+ internal class EllipseGeometryImpl : GeometryImpl
+ {
+ public override Rect Bounds { get; }
+ public override SKPath EffectivePath { get; }
+
+ public EllipseGeometryImpl(Rect rect)
+ {
+ var path = new SKPath();
+ path.AddOval(rect.ToSKRect());
+
+ EffectivePath = path;
+ Bounds = rect;
+ }
+ }
+}
diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
index c83d5f26fb..b701e60660 100644
--- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
+++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Skia
///
/// Skia formatted text implementation.
///
- public class FormattedTextImpl : IFormattedTextImpl
+ internal class FormattedTextImpl : IFormattedTextImpl
{
public FormattedTextImpl(
string text,
diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
index 0cb4c3db67..1af3d2968c 100644
--- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Skia
///
/// Skia render target that renders to a framebuffer surface. No gpu acceleration available.
///
- public class FramebufferRenderTarget : IRenderTarget
+ internal class FramebufferRenderTarget : IRenderTarget
{
private readonly IFramebufferPlatformSurface _platformSurface;
private SKImageInfo _currentImageInfo;
diff --git a/src/Skia/Avalonia.Skia/GeometryImpl.cs b/src/Skia/Avalonia.Skia/GeometryImpl.cs
index fbbd6eb58c..5940de418e 100644
--- a/src/Skia/Avalonia.Skia/GeometryImpl.cs
+++ b/src/Skia/Avalonia.Skia/GeometryImpl.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Skia
///
/// A Skia implementation of .
///
- public abstract class GeometryImpl : IGeometryImpl
+ internal abstract class GeometryImpl : IGeometryImpl
{
private PathCache _pathCache;
diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs
index cd8c334b53..7c0c42ca37 100644
--- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs
@@ -8,7 +8,7 @@ using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
- public class GlRenderTarget : IRenderTarget
+ internal class GlRenderTarget : IRenderTarget
{
private readonly GRContext _grContext;
private IGlPlatformSurfaceRenderTarget _surface;
diff --git a/src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs b/src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs
new file mode 100644
index 0000000000..751dd3c1e7
--- /dev/null
+++ b/src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs
@@ -0,0 +1,26 @@
+// 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.Collections.Generic;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+ ///
+ /// Custom Skia gpu instance.
+ ///
+ public interface ICustomSkiaGpu
+ {
+ ///
+ /// Skia GrContext used.
+ ///
+ GRContext GrContext { get; }
+
+ ///
+ /// Attempts to create custom render target from given surfaces.
+ ///
+ /// Surfaces.
+ /// Created render target or if it fails.
+ ICustomSkiaRenderTarget TryCreateRenderTarget(IEnumerable
public static void Initialize()
{
- var renderInterface = new PlatformRenderInterface();
-
+ Initialize(new SkiaOptions());
+ }
+
+ public static void Initialize(SkiaOptions options)
+ {
+ var customGpu = options.CustomGpuFactory?.Invoke();
+ var renderInterface = new PlatformRenderInterface(customGpu);
+
AvaloniaLocator.CurrentMutable
.Bind().ToConstant(renderInterface);
}
diff --git a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
index c19ff79d87..2764c65c6f 100644
--- a/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
+++ b/src/Skia/Avalonia.Skia/StreamGeometryImpl.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Skia
///
/// A Skia implementation of a .
///
- public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
+ internal class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl
{
private Rect _bounds;
private readonly SKPath _effectivePath;
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index 4b7eae1af4..9340c9add4 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Skia
///
/// Skia render target that writes to a surface.
///
- public class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
+ internal class SurfaceRenderTarget : IRenderTargetBitmapImpl, IDrawableBitmapImpl
{
private readonly SKSurface _surface;
private readonly SKCanvas _canvas;
diff --git a/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs b/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
index e95069eef3..9826bc2ce3 100644
--- a/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
+++ b/src/Skia/Avalonia.Skia/TransformedGeometryImpl.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Skia
///
/// A Skia implementation of a .
///
- public class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl
+ internal class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl
{
///
/// Initializes a new instance of the class.
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index c9d6fa6c11..fea21cde58 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Skia
///
/// Skia based writeable bitmap.
///
- public class WriteableBitmapImpl : IWriteableBitmapImpl, IDrawableBitmapImpl
+ internal class WriteableBitmapImpl : IWriteableBitmapImpl, IDrawableBitmapImpl
{
private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc;
private readonly SKBitmap _bitmap;
diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
index 8412a65e23..5ab9a8f74d 100644
--- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
+++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
@@ -182,10 +182,10 @@ namespace Avalonia.Direct2D1
return new WriteableWicBitmapImpl(size, dpi, format);
}
- public IStreamGeometryImpl CreateStreamGeometry()
- {
- return new StreamGeometryImpl();
- }
+ public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
+ public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
+ public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
+ public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
public IBitmapImpl LoadBitmap(string fileName)
{
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index 6123088d7e..e90d444c44 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
+using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using SharpDX;
using SharpDX.Direct2D1;
@@ -508,5 +509,7 @@ namespace Avalonia.Direct2D1.Media
{
PopLayer();
}
+
+ public void Custom(ICustomDrawOperation custom) => custom.Render(this);
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs
new file mode 100644
index 0000000000..9440966406
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs
@@ -0,0 +1,27 @@
+// 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 SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+ ///
+ /// A Direct2D implementation of a .
+ ///
+ internal class EllipseGeometryImpl : GeometryImpl
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EllipseGeometryImpl(Rect rect)
+ : base(CreateGeometry(rect))
+ {
+ }
+
+ private static Geometry CreateGeometry(Rect rect)
+ {
+ var ellipse = new Ellipse(rect.Center.ToSharpDX(), (float)rect.Width / 2, (float)rect.Height / 2);
+ return new EllipseGeometry(Direct2D1Platform.Direct2D1Factory, ellipse);
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
index b3cc4c8e0d..b73deb1f0a 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
@@ -9,7 +9,7 @@ using DWrite = SharpDX.DirectWrite;
namespace Avalonia.Direct2D1.Media
{
- public class FormattedTextImpl : IFormattedTextImpl
+ internal class FormattedTextImpl : IFormattedTextImpl
{
public FormattedTextImpl(
string text,
diff --git a/src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs
new file mode 100644
index 0000000000..6b73fce309
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs
@@ -0,0 +1,27 @@
+// 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 SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+ ///
+ /// A Direct2D implementation of a .
+ ///
+ internal class LineGeometryImpl : StreamGeometryImpl
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LineGeometryImpl(Point p1, Point p2)
+ {
+ using (var sink = ((PathGeometry)Geometry).Open())
+ {
+ sink.BeginFigure(p1.ToSharpDX(), FigureBegin.Hollow);
+ sink.AddLine(p2.ToSharpDX());
+ sink.EndFigure(FigureEnd.Open);
+ sink.Close();
+ }
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs
new file mode 100644
index 0000000000..194de4dd14
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs
@@ -0,0 +1,26 @@
+// 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 SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+ ///
+ /// A Direct2D implementation of a .
+ ///
+ internal class RectangleGeometryImpl : GeometryImpl
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RectangleGeometryImpl(Rect rect)
+ : base(CreateGeometry(rect))
+ {
+ }
+
+ private static Geometry CreateGeometry(Rect rect)
+ {
+ return new RectangleGeometry(Direct2D1Platform.Direct2D1Factory, rect.ToDirect2D());
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
index 400dd59ea9..5b04c5d7ff 100644
--- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
+++ b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
@@ -50,7 +50,7 @@ namespace Avalonia.Win32.Interop.Wpf
{
_resource = texture.QueryInterface();
- Target = new RenderTarget(AvaloniaLocator.Current.GetService(), surface,
+ Target = new RenderTarget(Direct2D1Platform.Direct2D1Factory, surface,
new RenderTargetProperties
{
DpiX = (float) dpi.X,
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index 8221dadc86..ee416c4cb0 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -292,6 +292,51 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void Calling_Show_On_Closed_Window_Should_Throw()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var windowImpl = Mock.Of(x => x.Scaling == 1);
+ var target = new Window(windowImpl);
+
+ target.Show();
+ target.Close();
+
+ var openedRaised = false;
+ target.Opened += (s, e) => openedRaised = true;
+
+ var ex = Assert.Throws(() => target.Show());
+ Assert.Equal("Cannot re-show a closed window.", ex.Message);
+ Assert.False(openedRaised);
+ }
+ }
+
+ [Fact]
+ public async Task Calling_ShowDialog_On_Closed_Window_Should_Throw()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var parent = new Mock();
+ var windowImpl = new Mock();
+ windowImpl.SetupProperty(x => x.Closed);
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ var target = new Window(windowImpl.Object);
+ var task = target.ShowDialog(parent.Object);
+
+ windowImpl.Object.Closed();
+ await task;
+
+ var openedRaised = false;
+ target.Opened += (s, e) => openedRaised = true;
+
+ var ex = await Assert.ThrowsAsync(() => target.ShowDialog(parent.Object));
+ Assert.Equal("Cannot re-show a closed window.", ex.Message);
+ Assert.False(openedRaised);
+ }
+ }
+
[Fact]
public void Window_Should_Be_Centered_When_WindowStartupLocation_Is_CenterScreen()
{
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
index 70a5504a7d..d9f1ce47dd 100644
--- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
+++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
@@ -11,6 +11,7 @@ using DynamicData;
using Xunit;
using Splat;
using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
namespace Avalonia
{
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs
index de09a1ea89..401d169896 100644
--- a/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs
+++ b/tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs
@@ -14,6 +14,7 @@ using Avalonia.Markup.Xaml;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Reactive;
+using Avalonia.ReactiveUI;
namespace Avalonia
{
diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
index 0e2abb314d..a3cc3dec17 100644
--- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
+++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
@@ -22,6 +22,21 @@ namespace Avalonia.UnitTests
return Mock.Of();
}
+ public IGeometryImpl CreateEllipseGeometry(Rect rect)
+ {
+ return Mock.Of();
+ }
+
+ public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
+ {
+ return Mock.Of();
+ }
+
+ public IGeometryImpl CreateRectangleGeometry(Rect rect)
+ {
+ return Mock.Of();
+ }
+
public IRenderTarget CreateRenderTarget(IEnumerable surfaces)
{
return Mock.Of();
diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs
index fec0f0831a..03470670d2 100644
--- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs
+++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs
@@ -56,6 +56,21 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
throw new NotImplementedException();
}
+ public IGeometryImpl CreateEllipseGeometry(Rect rect)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IGeometryImpl CreateRectangleGeometry(Rect rect)
+ {
+ throw new NotImplementedException();
+ }
+
class MockStreamGeometry : IStreamGeometryImpl
{
private MockStreamGeometryContext _impl = new MockStreamGeometryContext();