31 changed files with 520 additions and 467 deletions
@ -1,5 +0,0 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="Splat" Version="2.0.0" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="StyleResourceBinding"/> class.
|
|||
/// </summary>
|
|||
/// <param name="name">The resource name.</param>
|
|||
public StyleResourceBinding(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public BindingMode Mode => BindingMode.OneTime; |
|||
|
|||
/// <summary>
|
|||
/// Gets the resource name.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public BindingPriority Priority => BindingPriority.LocalValue; |
|||
|
|||
/// <inheritdoc/>
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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<IProvideValueTarget>(); |
|||
|
|||
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<IControl>(); |
|||
|
|||
// 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<IRootObjectProvider>()?.RootObject as IStyle ?? |
|||
context.GetLastOrDefaultAmbientValue<IStyle>(); |
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -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<IRuntimePlatform>().StartSystemTimer(new TimeSpan(0, 0, 0, 0, 1000 / 60), |
|||
() => |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
using (new NSAutoreleasePool()) |
|||
{ |
|||
Tick?.Invoke(this, EventArgs.Empty); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public event EventHandler<EventArgs> Tick; |
|||
} |
|||
} |
|||
@ -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<IPlatformRenderInterface>().ToSingleton<MockPlatformRenderInterface>(); |
|||
} |
|||
|
|||
[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<Point> |
|||
{ |
|||
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<Point> |
|||
{ |
|||
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<Point> |
|||
{ |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Avalonia.UnitTests |
|||
{ |
|||
public class TestWithServicesBase : IDisposable |
|||
{ |
|||
private IDisposable _scope; |
|||
|
|||
public TestWithServicesBase() |
|||
{ |
|||
_scope = AvaloniaLocator.EnterScope(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_scope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue