diff --git a/build/Splat.props b/build/Splat.props
deleted file mode 100644
index bc3367be01..0000000000
--- a/build/Splat.props
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/packages.cake b/packages.cake
index 98075ef36b..9e78ee75d5 100644
--- a/packages.cake
+++ b/packages.cake
@@ -107,7 +107,6 @@ public class Packages
context.Information("Setting NuGet package dependencies versions:");
var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1;
- var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
@@ -121,7 +120,6 @@ public class Packages
var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1;
context.Information("Package: Serilog, version: {0}", SerilogVersion);
- context.Information("Package: Splat, version: {0}", SplatVersion);
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
@@ -245,7 +243,6 @@ public class Packages
Dependencies = new DependencyBuilder(this)
{
new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion },
- new NuSpecDependency() { Id = "Splat", Version = SplatVersion },
new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version },
@@ -253,7 +250,6 @@ public class Packages
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
- new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp2.0", Version = SplatVersion },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
diff --git a/samples/BindingTest/BindingTest.csproj b/samples/BindingTest/BindingTest.csproj
index d7d349fbdf..4f82303c3a 100644
--- a/samples/BindingTest/BindingTest.csproj
+++ b/samples/BindingTest/BindingTest.csproj
@@ -152,7 +152,6 @@
-
\ No newline at end of file
diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj
index 0141ad29c6..a71f17ab7a 100644
--- a/samples/RenderTest/RenderTest.csproj
+++ b/samples/RenderTest/RenderTest.csproj
@@ -181,7 +181,6 @@
-
\ No newline at end of file
diff --git a/samples/VirtualizationTest/VirtualizationTest.csproj b/samples/VirtualizationTest/VirtualizationTest.csproj
index 58775926cc..6d3803b4cf 100644
--- a/samples/VirtualizationTest/VirtualizationTest.csproj
+++ b/samples/VirtualizationTest/VirtualizationTest.csproj
@@ -148,7 +148,6 @@
-
\ No newline at end of file
diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
index da230d34c6..e0fc79faea 100644
--- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
+++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
@@ -29,6 +29,5 @@
-
\ No newline at end of file
diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
index 5b621b9e08..9977d77978 100644
--- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
+++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
@@ -154,7 +154,6 @@
-
\ No newline at end of file
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index a4b1aaafbc..4a096fc326 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -72,6 +72,12 @@ namespace Avalonia.Threading
_jobRunner?.RunJobs(null);
}
+ ///
+ /// Use this method to ensure that more prioritized tasks are executed
+ ///
+ ///
+ public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
+
///
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs
index a7aafbf6e8..d4777b2f8a 100644
--- a/src/Avalonia.Controls/Control.cs
+++ b/src/Avalonia.Controls/Control.cs
@@ -774,7 +774,9 @@ namespace Avalonia.Controls
foreach (var child in control.LogicalChildren)
{
- if (child is Control c && !c.IsSet(DataContextProperty))
+ if (child is Control c &&
+ c.InheritanceParent == control &&
+ !c.IsSet(DataContextProperty))
{
DataContextNotifying(c, notifying);
}
diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs
index c03f4dc563..73b89ca4b7 100644
--- a/src/Avalonia.Controls/Shapes/Shape.cs
+++ b/src/Avalonia.Controls/Shapes/Shape.cs
@@ -29,6 +29,8 @@ namespace Avalonia.Controls.Shapes
private Matrix _transform = Matrix.Identity;
private Geometry _definingGeometry;
private Geometry _renderedGeometry;
+ bool _calculateTransformOnArrange = false;
+
static Shape()
{
@@ -150,13 +152,53 @@ namespace Avalonia.Controls.Shapes
this._definingGeometry = null;
InvalidateMeasure();
}
-
+
protected override Size MeasureOverride(Size availableSize)
+ {
+ bool deferCalculateTransform;
+ switch (Stretch)
+ {
+ case Stretch.Fill:
+ case Stretch.UniformToFill:
+ deferCalculateTransform = double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height);
+ break;
+ case Stretch.Uniform:
+ deferCalculateTransform = double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height);
+ break;
+ case Stretch.None:
+ default:
+ deferCalculateTransform = false;
+ break;
+ }
+
+ if (deferCalculateTransform)
+ {
+ _calculateTransformOnArrange = true;
+ return DefiningGeometry.Bounds.Size;
+ }
+ else
+ {
+ _calculateTransformOnArrange = false;
+ return CalculateShapeSizeAndSetTransform(availableSize);
+ }
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ if(_calculateTransformOnArrange)
+ {
+ _calculateTransformOnArrange = false;
+ CalculateShapeSizeAndSetTransform(finalSize);
+ }
+
+ return finalSize;
+ }
+ private Size CalculateShapeSizeAndSetTransform(Size availableSize)
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
-
+
if (_transform != transform)
{
_transform = transform;
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index 5cb56e0be1..abbdd15bed 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -50,5 +50,4 @@
-
\ No newline at end of file
diff --git a/src/Avalonia.Diagnostics/LogManager.cs b/src/Avalonia.Diagnostics/LogManager.cs
deleted file mode 100644
index d08d61836e..0000000000
--- a/src/Avalonia.Diagnostics/LogManager.cs
+++ /dev/null
@@ -1,53 +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 Avalonia.Layout;
-using Splat;
-
-namespace Avalonia.Diagnostics
-{
- public class LogManager : ILogManager
- {
- private static LogManager s_instance;
-
- public static LogManager Instance => s_instance ?? (s_instance = new LogManager());
-
- public ILogger Logger
- {
- get;
- set;
- }
-
- public bool LogPropertyMessages
- {
- get;
- set;
- }
-
- public bool LogLayoutMessages
- {
- get;
- set;
- }
-
- public static void Enable(ILogger logger)
- {
- Instance.Logger = logger;
- Locator.CurrentMutable.Register(() => Instance, typeof(ILogManager));
- }
-
- public IFullLogger GetLogger(Type type)
- {
- if ((type == typeof(AvaloniaObject) && LogPropertyMessages) ||
- (type == typeof(Layoutable) && LogLayoutMessages))
- {
- return new WrappingFullLogger(Logger, type);
- }
- else
- {
- return new WrappingFullLogger(new NullLogger(), type);
- }
- }
- }
-}
diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
index c29dbcd381..b7ad36f8b2 100644
--- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
+++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
@@ -16,5 +16,4 @@
-
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index e07c22d750..a1db5c2915 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -39,7 +39,6 @@
-
@@ -73,8 +72,6 @@
-
-
diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
index 3f12593197..eb5f87ea2a 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
@@ -83,6 +83,8 @@ namespace Avalonia.Markup.Xaml.Data
///
public object Source { get; set; }
+ internal WeakReference DefaultAnchor { get; set; }
+
///
public InstancedBinding Initiate(
IAvaloniaObject target,
@@ -92,6 +94,8 @@ namespace Avalonia.Markup.Xaml.Data
{
Contract.Requires(target != null);
+ anchor = anchor ?? DefaultAnchor?.Target;
+
var pathInfo = ParsePath(Path);
ValidateState(pathInfo);
enableDataValidation = enableDataValidation && Priority == BindingPriority.LocalValue;
diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs
deleted file mode 100644
index 7df2aef07b..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs
+++ /dev/null
@@ -1,69 +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.Disposables;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Styling;
-
-namespace Avalonia.Markup.Xaml.Data
-{
- public class StyleResourceBinding : IBinding
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The resource name.
- public StyleResourceBinding(string name)
- {
- Name = name;
- }
-
- ///
- public BindingMode Mode => BindingMode.OneTime;
-
- ///
- /// Gets the resource name.
- ///
- public string Name { get; }
-
- ///
- public BindingPriority Priority => BindingPriority.LocalValue;
-
- ///
- public InstancedBinding Initiate(
- IAvaloniaObject target,
- AvaloniaProperty targetProperty,
- object anchor = null,
- bool enableDataValidation = false)
- {
- var host = (target as IControl) ?? (anchor as IControl);
- var style = anchor as IStyle;
- var resource = AvaloniaProperty.UnsetValue;
-
- if (host != null)
- {
- resource = host.FindResource(Name);
- }
- else if (style != null)
- {
- if (!style.TryGetResource(Name, out resource))
- {
- resource = AvaloniaProperty.UnsetValue;
- }
- }
-
- if (resource != AvaloniaProperty.UnsetValue)
- {
- return InstancedBinding.OneTime(resource, Priority);
- }
- else
- {
- return null;
- }
- }
- }
-}
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
index 9d1e36ee78..8984498393 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
@@ -7,8 +7,13 @@ using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
+ using Avalonia.Controls;
+ using Avalonia.Styling;
+ using Portable.Xaml;
+ using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
using PortableXaml;
+ using System.ComponentModel;
[MarkupExtensionReturnType(typeof(IBinding))]
public class BindingExtension : MarkupExtension
@@ -24,7 +29,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider)
{
- var b = new Binding
+ return new Binding
{
Converter = Converter,
ConverterParameter = ConverterParameter,
@@ -33,10 +38,26 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Mode = Mode,
Path = Path,
Priority = Priority,
- RelativeSource = RelativeSource
+ RelativeSource = RelativeSource,
+ DefaultAnchor = new WeakReference(GetDefaultAnchor((ITypeDescriptorContext)serviceProvider))
};
+ }
+
+
+ private static object GetDefaultAnchor(ITypeDescriptorContext context)
+ {
+ object anchor = null;
+
+ // The target is not a control, so we need to find an anchor that will let us look
+ // up named controls and style resources. First look for the closest IControl in
+ // the context.
+ anchor = context.GetFirstAmbientValue();
- return XamlBinding.FromMarkupExtensionContext(b, serviceProvider);
+ // If a control was not found, then try to find the highest-level style as the XAML
+ // file could be a XAML file containing only styles.
+ return anchor ??
+ context.GetService()?.RootObject as IStyle ??
+ context.GetLastOrDefaultAmbientValue();
}
public IValueConverter Converter { get; set; }
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs
deleted file mode 100644
index 55a1b72125..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs
+++ /dev/null
@@ -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 Avalonia.Data;
-using Avalonia.Markup.Xaml.Data;
-using System;
-
-namespace Avalonia.Markup.Xaml.MarkupExtensions
-{
- using Portable.Xaml.Markup;
- using PortableXaml;
-
- [MarkupExtensionReturnType(typeof(IBinding))]
- public class StyleResourceExtension : MarkupExtension
- {
- public StyleResourceExtension(string name)
- {
- Name = name;
- }
-
- public override object ProvideValue(IServiceProvider serviceProvider)
- {
- return XamlBinding.FromMarkupExtensionContext(
- new StyleResourceBinding(Name),
- serviceProvider);
- }
-
- [ConstructorArgument("name")]
- public string Name { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
index a38029964f..7de96ea220 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
@@ -212,11 +212,6 @@ namespace Avalonia.Markup.Xaml.PortableXaml
value);
}
- if (value is XamlBinding)
- {
- value = (value as XamlBinding).Value;
- }
-
if (UpdateListInsteadSet &&
value != null &&
UpdateListInsteadSetValue(instance, value))
@@ -317,9 +312,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
if (!Member.AssignBinding)
ApplyBinding(obj, (IBinding)value);
else
- obj.SetValue(Property, value is XamlBinding ?
- (value as XamlBinding).Value :
- value);
+ obj.SetValue(Property, value);
}
else
{
@@ -348,12 +341,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
var control = obj as IControl;
var property = Property;
- var xamlBinding = binding as XamlBinding;
if (control != null && property != Control.DataContextProperty)
DelayedBinding.Add(control, property, binding);
- else if (xamlBinding != null)
- obj.Bind(property, xamlBinding.Value, xamlBinding.Anchor?.Target);
else
obj.Bind(property, binding);
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs
deleted file mode 100644
index 1f4e66471c..0000000000
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Data;
-using Avalonia.Styling;
-using Portable.Xaml;
-using Portable.Xaml.ComponentModel;
-using System.ComponentModel;
-using Portable.Xaml.Markup;
-using System;
-
-namespace Avalonia.Markup.Xaml.PortableXaml
-{
- internal class XamlBinding : IBinding
- {
- public static IBinding FromMarkupExtensionContext(
- IBinding binding,
- IServiceProvider serviceProvider)
- {
- var context = (ITypeDescriptorContext)serviceProvider;
- var pvt = context.GetService();
-
- if (pvt.TargetObject is IControl) return binding;
-
- object anchor = GetDefaultAnchor(context);
-
- if (anchor == null) return binding;
-
- return new XamlBinding(binding, anchor);
- }
-
- private static object GetDefaultAnchor(ITypeDescriptorContext context)
- {
- object anchor = null;
-
- // The target is not a control, so we need to find an anchor that will let us look
- // up named controls and style resources. First look for the closest IControl in
- // the context.
- anchor = context.GetFirstAmbientValue();
-
- // If a control was not found, then try to find the highest-level style as the XAML
- // file could be a XAML file containing only styles.
- return anchor ??
- context.GetService()?.RootObject as IStyle ??
- context.GetLastOrDefaultAmbientValue();
- }
-
- private XamlBinding(IBinding binding, object anchor)
- {
- Value = binding;
-
- Anchor = new WeakReference(anchor);
- }
-
- public WeakReference Anchor { get; }
-
- public IBinding Value { get; }
-
- public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null, bool enableDataValidation = false)
- {
- return Value.Initiate(target, targetProperty,
- anchor ?? Anchor.Target, enableDataValidation);
- }
- }
-}
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs
index 1ecfa0eb7d..935ab53432 100644
--- a/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs
+++ b/src/OSX/Avalonia.MonoMac/EmulatedFramebuffer.cs
@@ -2,44 +2,80 @@
using Avalonia.Platform;
using MonoMac.AppKit;
using System.Runtime.InteropServices;
+using Avalonia.Threading;
using MonoMac.CoreGraphics;
namespace Avalonia.MonoMac
{
class EmulatedFramebuffer : ILockedFramebuffer
{
+ private readonly TopLevelImpl.TopLevelView _view;
private readonly CGSize _logicalSize;
- public EmulatedFramebuffer(NSView view)
+ private readonly bool _isDeferred;
+
+ [DllImport("libc")]
+ static extern void memset(IntPtr p, int c, IntPtr size);
+
+ public EmulatedFramebuffer(TopLevelImpl.TopLevelView view)
{
- _logicalSize = view.Frame.Size;
- var pixelSize = view.ConvertSizeToBacking(_logicalSize);
+ _view = view;
+
+ _isDeferred = !Dispatcher.UIThread.CheckAccess();
+ _logicalSize = _view.LogicalSize;
+ var pixelSize = _view.PixelSize;
Width = (int)pixelSize.Width;
Height = (int)pixelSize.Height;
RowBytes = Width * 4;
Dpi = new Vector(96 * pixelSize.Width / _logicalSize.Width, 96 * pixelSize.Height / _logicalSize.Height);
Format = PixelFormat.Rgba8888;
- Address = Marshal.AllocHGlobal(Height * RowBytes);
+ var size = Height * RowBytes;
+ Address = Marshal.AllocHGlobal(size);
+ memset(Address, 0, new IntPtr(size));
}
-
+
public void Dispose()
{
if (Address == IntPtr.Zero)
return;
var nfo = (int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast;
-
- using (var colorSpace = CGColorSpace.CreateDeviceRGB())
- using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4,
- colorSpace, (CGImageAlphaInfo) nfo))
- using (var image = bContext.ToImage())
- using (var nscontext = NSGraphicsContext.CurrentContext)
- using (var context = nscontext.GraphicsPort)
+ CGImage image = null;
+ try
{
- context.SetFillColor(255, 255, 255, 255);
- context.FillRect(new CGRect(default(CGPoint), _logicalSize));
- context.DrawImage(new CGRect(default(CGPoint), _logicalSize), image);
+ using (var colorSpace = CGColorSpace.CreateDeviceRGB())
+ using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4,
+ colorSpace, (CGImageAlphaInfo)nfo))
+ image = bContext.ToImage();
+ lock (_view.SyncRoot)
+ {
+ if(!_isDeferred)
+ {
+ using (var nscontext = NSGraphicsContext.CurrentContext)
+ using (var context = nscontext.GraphicsPort)
+ {
+ context.SetFillColor(255, 255, 255, 255);
+ context.FillRect(new CGRect(default(CGPoint), _view.LogicalSize));
+ context.TranslateCTM(0, _view.LogicalSize.Height - _logicalSize.Height);
+ context.DrawImage(new CGRect(default(CGPoint), _logicalSize), image);
+ context.Flush();
+ nscontext.FlushGraphics();
+ }
+ }
+ }
}
- Marshal.FreeHGlobal(Address);
- Address = IntPtr.Zero;
+ finally
+ {
+ if (image != null)
+ {
+ if (!_isDeferred)
+ image.Dispose();
+ else
+ _view.SetBackBufferImage(new SavedImage(image, _logicalSize));
+ }
+ Marshal.FreeHGlobal(Address);
+ Address = IntPtr.Zero;
+ }
+
+
}
public IntPtr Address { get; private set; }
@@ -49,4 +85,22 @@ namespace Avalonia.MonoMac
public Vector Dpi { get; }
public PixelFormat Format { get; }
}
+
+ class SavedImage : IDisposable
+ {
+ public CGImage Image { get; private set; }
+ public CGSize LogicalSize { get; }
+
+ public SavedImage(CGImage image, CGSize logicalSize)
+ {
+ Image = image;
+ LogicalSize = logicalSize;
+ }
+
+ public void Dispose()
+ {
+ Image?.Dispose();
+ Image = null;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
index 0deea7fb44..a6b1f1d5b4 100644
--- a/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
+++ b/src/OSX/Avalonia.MonoMac/MonoMacPlatform.cs
@@ -1,10 +1,13 @@
using System;
+using System.Threading;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Rendering;
using MonoMac.AppKit;
+using MonoMac.Foundation;
+using MonoMac.ObjCRuntime;
namespace Avalonia.MonoMac
{
@@ -16,9 +19,11 @@ namespace Avalonia.MonoMac
internal static NSApplication App;
private static bool s_monoMacInitialized;
private static bool s_showInDock = true;
+ private static IRenderLoop s_renderLoop;
void DoInitialize()
{
+ InitializeMonoMac();
AvaloniaLocator.CurrentMutable
.Bind().ToTransient()
.Bind().ToSingleton()
@@ -27,9 +32,8 @@ namespace Avalonia.MonoMac
.Bind().ToConstant(this)
.Bind().ToConstant(this)
.Bind().ToSingleton()
+ .Bind().ToConstant(s_renderLoop)
.Bind().ToConstant(PlatformThreadingInterface.Instance);
-
- InitializeMonoMac();
}
public static void Initialize()
@@ -39,13 +43,44 @@ namespace Avalonia.MonoMac
}
+
+ ///
+ /// See "Using POSIX Threads in a Cocoa Application" section here:
+ /// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
+ ///
+ class ThreadHelper : NSObject
+ {
+ private readonly AutoResetEvent _event = new AutoResetEvent(false);
+ private const string InitThreadingName = "initThreading";
+ [Export(InitThreadingName)]
+ public void DoNothing()
+ {
+ _event.Set();
+ }
+
+ public static void InitializeCocoaThreadingLocks()
+ {
+ var helper = new ThreadHelper();
+ var thread = new NSThread(helper, Selector.FromHandle(Selector.GetHandle(InitThreadingName)), new NSObject());
+ thread.Start();
+ helper._event.WaitOne();
+ helper._event.Dispose();
+ if (!NSThread.IsMultiThreaded)
+ {
+ throw new Exception("Unable to initialize Cocoa threading");
+ }
+ }
+ }
+
void InitializeMonoMac()
{
if(s_monoMacInitialized)
return;
NSApplication.Init();
+ ThreadHelper.InitializeCocoaThreadingLocks();
App = NSApplication.SharedApplication;
UpdateActivationPolicy();
+ s_renderLoop = new RenderLoop(); //TODO: use CVDisplayLink
s_monoMacInitialized = true;
}
@@ -64,6 +99,7 @@ namespace Avalonia.MonoMac
}
}
+ public static bool UseDeferredRendering { get; set; } = true;
public Size DoubleClickSize => new Size(4, 4);
public TimeSpan DoubleClickTime => TimeSpan.FromSeconds(NSEvent.DoubleClickInterval);
@@ -87,9 +123,11 @@ namespace Avalonia
{
public static class MonoMacPlatformExtensions
{
- public static T UseMonoMac(this T builder) where T : AppBuilderBase, new()
+ public static T UseMonoMac(this T builder, bool? useDeferredRendering = null) where T : AppBuilderBase, new()
{
- return builder.UseWindowingSubsystem(MonoMac.MonoMacPlatform.Initialize);
+ if (useDeferredRendering.HasValue)
+ MonoMac.MonoMacPlatform.UseDeferredRendering = useDeferredRendering.Value;
+ return builder.UseWindowingSubsystem(MonoMac.MonoMacPlatform.Initialize, "MonoMac");
}
}
}
\ No newline at end of file
diff --git a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs
index 184416e77a..88189ba12c 100644
--- a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs
+++ b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs
@@ -3,14 +3,18 @@ using System.Threading;
using Avalonia.Platform;
using Avalonia.Threading;
using MonoMac.AppKit;
+using MonoMac.CoreFoundation;
using MonoMac.CoreGraphics;
using MonoMac.Foundation;
+using MonoMac.ObjCRuntime;
namespace Avalonia.MonoMac
{
- class PlatformThreadingInterface : IPlatformThreadingInterface
+ class PlatformThreadingInterface : NSObject, IPlatformThreadingInterface
{
private bool _signaled;
+ private const string SignaledSelectorName = "avaloniauiSignaled";
+ private readonly Selector _signaledSelector = new Selector(SignaledSelectorName);
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread;
@@ -27,18 +31,25 @@ namespace Avalonia.MonoMac
return;
_signaled = true;
}
- NSApplication.SharedApplication.BeginInvokeOnMainThread(() =>
- {
- lock (this)
+ PerformSelector(_signaledSelector, NSThread.MainThread, this, false,
+ new[]
{
- if (!_signaled)
- return;
- _signaled = false;
- }
- Signaled?.Invoke(null);
- });
+ NSRunLoop.NSDefaultRunLoopMode, NSRunLoop.NSRunLoopEventTracking, NSRunLoop.NSRunLoopModalPanelMode,
+ NSRunLoop.NSRunLoopCommonModes, NSRunLoop.NSRunLoopConnectionReplyMode
+ });
}
+ [Export(SignaledSelectorName)]
+ public void CallSignaled()
+ {
+ lock (this)
+ {
+ if (!_signaled)
+ return;
+ _signaled = false;
+ }
+ Signaled?.Invoke(null);
+ }
public void RunLoop(CancellationToken cancellationToken)
@@ -55,6 +66,7 @@ namespace Avalonia.MonoMac
var ev = app.NextEvent(NSEventMask.AnyEvent, NSDate.DistantFuture, NSRunLoop.NSDefaultRunLoopMode, true);
if (ev != null)
{
+ Console.WriteLine("NSEVENT");
app.SendEvent(ev);
ev.Dispose();
}
diff --git a/src/OSX/Avalonia.MonoMac/RenderLoop.cs b/src/OSX/Avalonia.MonoMac/RenderLoop.cs
new file mode 100644
index 0000000000..a6f3e9f493
--- /dev/null
+++ b/src/OSX/Avalonia.MonoMac/RenderLoop.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+using MonoMac.Foundation;
+
+namespace Avalonia.MonoMac
+{
+ //TODO: Switch to using CVDisplayLink
+ public class RenderLoop : IRenderLoop
+ {
+ private readonly object _lock = new object();
+ private readonly IDisposable _timer;
+
+ public RenderLoop()
+ {
+ _timer = AvaloniaLocator.Current.GetService().StartSystemTimer(new TimeSpan(0, 0, 0, 0, 1000 / 60),
+ () =>
+ {
+ lock (_lock)
+ {
+ using (new NSAutoreleasePool())
+ {
+ Tick?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ });
+ }
+
+ public event EventHandler Tick;
+ }
+}
diff --git a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
index 2744474214..5ea7972871 100644
--- a/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
+++ b/src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
@@ -5,8 +5,9 @@ using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Rendering;
+using Avalonia.Threading;
using MonoMac.AppKit;
-
+using MonoMac.CoreFoundation;
using MonoMac.CoreGraphics;
using MonoMac.Foundation;
using MonoMac.ObjCRuntime;
@@ -24,6 +25,7 @@ namespace Avalonia.MonoMac
protected virtual void OnInput(RawInputEventArgs args)
{
+ Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Input?.Invoke(args);
}
@@ -36,6 +38,14 @@ namespace Avalonia.MonoMac
private readonly IKeyboardDevice _keyboard;
private NSTrackingArea _area;
private NSCursor _cursor;
+ private bool _nonUiRedrawQueued;
+
+ public CGSize PixelSize { get; set; }
+
+ public CGSize LogicalSize { get; set; }
+
+ private SavedImage _backBuffer;
+ public object SyncRoot { get; } = new object();
public TopLevelView(TopLevelImpl tl)
{
@@ -44,17 +54,75 @@ namespace Avalonia.MonoMac
_keyboard = AvaloniaLocator.Current.GetService();
}
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _backBuffer?.Dispose();
+ _backBuffer = null;
+ }
+ base.Dispose(disposing);
+ }
+
public override bool ConformsToProtocol(IntPtr protocol)
{
var rv = base.ConformsToProtocol(protocol);
return rv;
}
+ public override bool IsOpaque => false;
+
public override void DrawRect(CGRect dirtyRect)
{
+ lock (SyncRoot)
+ _nonUiRedrawQueued = false;
+ Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
+ lock (SyncRoot)
+ {
+ if (_backBuffer != null)
+ {
+ using (var context = NSGraphicsContext.CurrentContext.GraphicsPort)
+ {
+ context.SetFillColor(255, 255, 255, 255);
+ context.FillRect(new CGRect(default(CGPoint), LogicalSize));
+ context.TranslateCTM(0, LogicalSize.Height - _backBuffer.LogicalSize.Height);
+ context.DrawImage(new CGRect(default(CGPoint), _backBuffer.LogicalSize), _backBuffer.Image);
+ context.Flush();
+ NSGraphicsContext.CurrentContext.FlushGraphics();
+ }
+ }
+ }
_tl.Paint?.Invoke(dirtyRect.ToAvaloniaRect());
}
+ public void SetBackBufferImage(SavedImage image)
+ {
+ lock (SyncRoot)
+ {
+ _backBuffer?.Dispose();
+ _backBuffer = image;
+ if (image == null)
+ return;
+
+ if (_nonUiRedrawQueued)
+ return;
+ _nonUiRedrawQueued = true;
+ Dispatcher.UIThread.InvokeAsync(
+ () =>
+ {
+ lock (SyncRoot)
+ {
+ if (!_nonUiRedrawQueued)
+ return;
+ _nonUiRedrawQueued = false;
+ }
+ SetNeedsDisplayInRect(Frame);
+ Display();
+ }, DispatcherPriority.Render);
+
+ }
+ }
+
[Export("viewDidChangeBackingProperties:")]
public void ViewDidChangeBackingProperties()
{
@@ -78,7 +146,12 @@ namespace Avalonia.MonoMac
public override void SetFrameSize(CGSize newSize)
{
- base.SetFrameSize(newSize);
+ lock (SyncRoot)
+ {
+ base.SetFrameSize(newSize);
+ LogicalSize = Frame.Size;
+ PixelSize = ConvertSizeToBacking(LogicalSize);
+ }
if (_area != null)
{
@@ -92,6 +165,7 @@ namespace Avalonia.MonoMac
AddTrackingArea(_area);
UpdateCursor();
_tl?.Resized?.Invoke(_tl.ClientSize);
+ Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout);
}
InputModifiers GetModifiers(NSEventModifierMask mod)
@@ -348,9 +422,16 @@ namespace Avalonia.MonoMac
View.Dispose();
}
- public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);
+ public IRenderer CreateRenderer(IRenderRoot root) =>
+ MonoMacPlatform.UseDeferredRendering
+ ? new DeferredRenderer(root, AvaloniaLocator.Current.GetService())
+ : (IRenderer) new ImmediateRenderer(root);
- public void Invalidate(Rect rect) => View.SetNeedsDisplayInRect(View.Frame);
+ public void Invalidate(Rect rect)
+ {
+ if (!MonoMacPlatform.UseDeferredRendering)
+ View.SetNeedsDisplayInRect(View.Frame);
+ }
public abstract Point PointToClient(Point point);
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index ccbf04533d..85a450e1dc 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -249,6 +249,37 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
+ [Fact]
+ public void DataContextBeginUpdate_Should_Not_Be_Called_For_Controls_That_Dont_Inherit()
+ {
+ using (CreateServices())
+ {
+ TestControl child;
+ var popup = new Popup
+ {
+ Child = child = new TestControl(),
+ DataContext = "foo",
+ };
+
+ var beginCalled = false;
+ child.DataContextBeginUpdate += (s, e) => beginCalled = true;
+
+ // Test for #1245. Here, the child's logical parent is the popup but it's not yet
+ // attached to a visual tree because the popup hasn't been opened.
+ Assert.Same(popup, ((ILogical)child).LogicalParent);
+ Assert.Same(popup, child.InheritanceParent);
+ Assert.Null(child.GetVisualRoot());
+
+ popup.Open();
+
+ // #1245 was caused by the fact that DataContextBeginUpdate was called on `target`
+ // when the PopupRoot was created, even though PopupRoot isn't the
+ // InheritanceParent of child.
+ Assert.False(beginCalled);
+ }
+ }
+
+
private static IDisposable CreateServices()
{
var result = AvaloniaLocator.EnterScope();
@@ -304,5 +335,18 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class PopupContentControl : ContentControl
{
}
+
+ private class TestControl : Decorator
+ {
+ public event EventHandler DataContextBeginUpdate;
+
+ public new IAvaloniaObject InheritanceParent => base.InheritanceParent;
+
+ protected override void OnDataContextBeginUpdate()
+ {
+ DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
+ base.OnDataContextBeginUpdate();
+ }
+ }
}
}
diff --git a/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs
new file mode 100644
index 0000000000..2ccd6fb04c
--- /dev/null
+++ b/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs
@@ -0,0 +1,113 @@
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Avalonia.UnitTests;
+using Xunit;
+
+namespace Avalonia.Layout.UnitTests
+
+{
+ public class ShapeLayoutTests : TestWithServicesBase
+ {
+
+ public ShapeLayoutTests()
+ {
+ AvaloniaLocator.CurrentMutable
+ .Bind().ToSingleton();
+ }
+
+ [Fact]
+ public void Shape_Transformation_Calculation_Should_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Infinite()
+ {
+ var shape = new Polygon()
+ {
+ Points = new List
+ {
+ new Point(0, 0),
+ new Point(10, 5),
+ new Point(0, 10)
+ },
+ Stretch = Stretch.Fill
+ };
+
+ var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
+ shape.Measure(availableSize);
+ Geometry postMeasureGeometry = shape.RenderedGeometry;
+ Transform postMeasureTransform = postMeasureGeometry.Transform;
+
+ var finalSize = new Size(100, 50);
+ var finalRect = new Rect(finalSize);
+ shape.Arrange(finalRect);
+
+ Geometry postArrangeGeometry = shape.RenderedGeometry;
+ Transform postArrangeTransform = postArrangeGeometry.Transform;
+
+ Assert.NotEqual(postMeasureGeometry, postArrangeGeometry);
+ Assert.NotEqual(postMeasureTransform, postArrangeTransform);
+ Assert.Equal(finalSize, shape.Bounds.Size);
+ }
+
+ [Fact]
+ public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Finite()
+ {
+ var shape = new Polygon()
+ {
+ Points = new List
+ {
+ new Point(0, 0),
+ new Point(10, 5),
+ new Point(0, 10)
+ },
+ Stretch = Stretch.Fill
+ };
+
+ var availableSize = new Size(100, 50);
+ shape.Measure(availableSize);
+ Geometry postMeasureGeometry = shape.RenderedGeometry;
+ Transform postMeasureTransform = postMeasureGeometry.Transform;
+
+ var finalRect = new Rect(availableSize);
+ shape.Arrange(finalRect);
+
+ Geometry postArrangeGeometry = shape.RenderedGeometry;
+ Transform postArrangeTransform = postArrangeGeometry.Transform;
+
+ Assert.Equal(postMeasureGeometry, postArrangeGeometry);
+ Assert.Equal(postMeasureTransform, postArrangeTransform);
+ Assert.Equal(availableSize, shape.Bounds.Size);
+ }
+
+ [Fact]
+ public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_None()
+ {
+ var shape = new Polygon()
+ {
+ Points = new List
+ {
+ new Point(0, 0),
+ new Point(10, 5),
+ new Point(0, 10)
+ },
+ Stretch = Stretch.None
+ };
+
+ var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
+ shape.Measure(availableSize);
+ Geometry postMeasureGeometry = shape.RenderedGeometry;
+ Transform postMeasureTransform = postMeasureGeometry.Transform;
+
+ var finalSize = new Size(100, 50);
+ var finalRect = new Rect(finalSize);
+ shape.Arrange(finalRect);
+
+ Geometry postArrangeGeometry = shape.RenderedGeometry;
+ Transform postArrangeTransform = postArrangeGeometry.Transform;
+
+ Assert.Equal(postMeasureGeometry, postArrangeGeometry);
+ Assert.Equal(postMeasureTransform, postArrangeTransform);
+ Assert.Equal(finalSize, shape.Bounds.Size);
+ }
+ }
+}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
index 03bb71a383..eb6dc8b5e5 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
@@ -8,7 +8,6 @@
-
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
index 8c7dad1bce..03dc5374e0 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
@@ -411,8 +411,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var xaml = @"
-
-
+
";
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
index 65fc9eaddd..b339972029 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs
@@ -61,171 +61,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}
}
- [Fact]
- public void StyleResource_Can_Be_Assigned_To_Property()
- {
- var xaml = @"
-
-
-
-
-
-
-";
-
- var loader = new AvaloniaXamlLoader();
- var userControl = (UserControl)loader.Load(xaml);
- var border = userControl.FindControl("border");
-
- DelayedBinding.ApplyBindings(border);
-
- var brush = (SolidColorBrush)border.Background;
- Assert.Equal(0xff506070, brush.Color.ToUint32());
- }
-
- [Fact]
- public void StyleResource_Can_Be_Assigned_To_Setter()
- {
- //skip default theme and styles, they are not needed
- using (UnitTestApplication.Start(TestServices.StyledWindow
- .With(theme: () => new Styles())))
- {
- var xaml = @"
-
-
-
-
-
-
-";
-
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
- var button = window.FindControl