Browse Source
Conflicts: src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs src/Windows/Avalonia.Direct2D1/Direct2D1Platform.csscenegraph-after-breakage
91 changed files with 1135 additions and 338 deletions
@ -1,5 +1,5 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="Moq" Version="4.7.1" /> |
|||
<PackageReference Include="Moq" Version="4.7.25" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Runtime.CompilerServices; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Avalonia.Diagnostics.ViewModels |
|||
{ |
|||
public class ViewModelBase : INotifyPropertyChanged |
|||
{ |
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
[NotifyPropertyChangedInvocator] |
|||
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null) |
|||
{ |
|||
if (!EqualityComparer<T>.Default.Equals(field, value)) |
|||
{ |
|||
field = value; |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
[NotifyPropertyChangedInvocator] |
|||
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Reactive.Linq; |
|||
using System.Reflection; |
|||
|
|||
namespace Avalonia.Diagnostics.Views |
|||
{ |
|||
internal static class PropertyChangedExtenions |
|||
{ |
|||
public static IObservable<T> GetObservable<T>(this INotifyPropertyChanged source, string propertyName) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(source != null); |
|||
Contract.Requires<ArgumentNullException>(propertyName != null); |
|||
|
|||
var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); |
|||
|
|||
if (property == null) |
|||
{ |
|||
throw new ArgumentException($"Property '{propertyName}' not found on '{source}."); |
|||
} |
|||
|
|||
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>( |
|||
e => source.PropertyChanged += e, |
|||
e => source.PropertyChanged -= e) |
|||
.Where(e => e.EventArgs.PropertyName == propertyName) |
|||
.Select(_ => (T)property.GetValue(source)) |
|||
.StartWith((T)property.GetValue(source)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +1,16 @@ |
|||
// 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.Input.Raw; |
|||
|
|||
namespace Avalonia.Input |
|||
{ |
|||
public interface IInputDevice |
|||
{ |
|||
/// <summary>
|
|||
/// Processes raw event. Is called after preprocessing by InputManager
|
|||
/// </summary>
|
|||
/// <param name="ev"></param>
|
|||
void ProcessRawEvent(RawInputEventArgs ev); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,50 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Direct2D1.Media; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Rendering; |
|||
using SharpDX; |
|||
using DirectWriteFactory = SharpDX.DirectWrite.Factory; |
|||
|
|||
namespace Avalonia.Direct2D1 |
|||
{ |
|||
class ExternalRenderTarget : IRenderTarget |
|||
{ |
|||
private readonly IExternalDirect2DRenderTargetSurface _externalRenderTargetProvider; |
|||
private readonly DirectWriteFactory _dwFactory; |
|||
private SharpDX.Direct2D1.RenderTarget _target; |
|||
public ExternalRenderTarget(IExternalDirect2DRenderTargetSurface externalRenderTargetProvider, |
|||
DirectWriteFactory dwFactory) |
|||
{ |
|||
_externalRenderTargetProvider = externalRenderTargetProvider; |
|||
_dwFactory = dwFactory; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_target?.Dispose(); |
|||
_target = null; |
|||
} |
|||
|
|||
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) |
|||
{ |
|||
_target = _target ?? _externalRenderTargetProvider.CreateRenderTarget(); |
|||
_externalRenderTargetProvider.BeforeDrawing(); |
|||
return new DrawingContextImpl(visualBrushRenderer, _target, _dwFactory, null, () => |
|||
{ |
|||
try |
|||
{ |
|||
_externalRenderTargetProvider.AfterDrawing(); |
|||
} |
|||
catch (SharpDXException ex) when ((uint) ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
|
|||
{ |
|||
_target?.Dispose(); |
|||
_target = null; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Avalonia.Direct2D1 |
|||
{ |
|||
public interface IExternalDirect2DRenderTargetSurface |
|||
{ |
|||
SharpDX.Direct2D1.RenderTarget CreateRenderTarget(); |
|||
void BeforeDrawing(); |
|||
void AfterDrawing(); |
|||
} |
|||
} |
|||
@ -0,0 +1,134 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Avalonia.Collections; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Base.UnitTests.Collections |
|||
{ |
|||
public class AvaloniaListExtenionsTests |
|||
{ |
|||
[Fact] |
|||
public void CreateDerivedList_Creates_Initial_Items() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Add() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.Add(4); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Insert() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.Insert(1, 4); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Remove() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.Remove(2); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_RemoveRange() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.RemoveRange(1, 2); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Move() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.Move(2, 0); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_MoveRange() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.MoveRange(1, 2, 0); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Replace() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source[1] = 4; |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateDerivedList_Handles_Clear() |
|||
{ |
|||
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
|||
var target = source.CreateDerivedList(x => new Wrapper(x)); |
|||
|
|||
source.Clear(); |
|||
|
|||
var result = target.Select(x => x.Value).ToList(); |
|||
|
|||
Assert.Equal(source, result); |
|||
} |
|||
|
|||
private class Wrapper |
|||
{ |
|||
public Wrapper(int value) |
|||
{ |
|||
Value = value; |
|||
} |
|||
|
|||
public int Value { get; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Layout; |
|||
using Avalonia.UnitTests; |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
namespace Avalonia.Benchmarks.Layout |
|||
{ |
|||
[MemoryDiagnoser] |
|||
public class Measure : IDisposable |
|||
{ |
|||
private IDisposable _app; |
|||
private TestRoot root; |
|||
private List<Control> controls = new List<Control>(); |
|||
|
|||
public Measure() |
|||
{ |
|||
_app = UnitTestApplication.Start(TestServices.RealLayoutManager); |
|||
|
|||
var panel = new StackPanel(); |
|||
root = new TestRoot { Child = panel }; |
|||
controls.Add(panel); |
|||
CreateChildren(panel, 3, 5); |
|||
LayoutManager.Instance.ExecuteInitialLayoutPass(root); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_app.Dispose(); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void Remeasure_Half() |
|||
{ |
|||
var random = new Random(1); |
|||
|
|||
foreach (var control in controls) |
|||
{ |
|||
if (random.Next(2) == 0) |
|||
{ |
|||
control.InvalidateMeasure(); |
|||
} |
|||
} |
|||
|
|||
LayoutManager.Instance.ExecuteLayoutPass(); |
|||
} |
|||
|
|||
private void CreateChildren(IPanel parent, int childCount, int iterations) |
|||
{ |
|||
for (var i = 0; i < childCount; ++i) |
|||
{ |
|||
var control = new StackPanel(); |
|||
parent.Children.Add(control); |
|||
|
|||
if (iterations > 0) |
|||
{ |
|||
CreateChildren(control, childCount, iterations - 1); |
|||
} |
|||
|
|||
controls.Add(control); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Layout.UnitTests |
|||
{ |
|||
internal class LayoutTestControl : Decorator |
|||
{ |
|||
public bool Measured { get; set; } |
|||
public bool Arranged { get; set; } |
|||
public Func<ILayoutable, Size, Size> DoMeasureOverride { get; set; } |
|||
public Func<ILayoutable, Size, Size> DoArrangeOverride { get; set; } |
|||
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
Measured = true; |
|||
return DoMeasureOverride != null ? |
|||
DoMeasureOverride(this, availableSize) : |
|||
base.MeasureOverride(availableSize); |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
Arranged = true; |
|||
return DoArrangeOverride != null ? |
|||
DoArrangeOverride(this, finalSize) : |
|||
base.ArrangeOverride(finalSize); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// 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.UnitTests; |
|||
|
|||
namespace Avalonia.Layout.UnitTests |
|||
{ |
|||
internal class LayoutTestRoot : TestRoot, ILayoutable |
|||
{ |
|||
public bool Measured { get; set; } |
|||
public bool Arranged { get; set; } |
|||
public Func<ILayoutable, Size, Size> DoMeasureOverride { get; set; } |
|||
public Func<ILayoutable, Size, Size> DoArrangeOverride { get; set; } |
|||
|
|||
void ILayoutable.Measure(Size availableSize) |
|||
{ |
|||
Measured = true; |
|||
Measure(availableSize); |
|||
} |
|||
|
|||
void ILayoutable.Arrange(Rect rect) |
|||
{ |
|||
Arranged = true; |
|||
Arrange(rect); |
|||
} |
|||
|
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
return DoMeasureOverride != null ? |
|||
DoMeasureOverride(this, availableSize) : |
|||
base.MeasureOverride(availableSize); |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
Arranged = true; |
|||
return DoArrangeOverride != null ? |
|||
DoArrangeOverride(this, finalSize) : |
|||
base.ArrangeOverride(finalSize); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Moq; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Layout.UnitTests |
|||
{ |
|||
public class LayoutableTests |
|||
{ |
|||
[Fact] |
|||
public void Only_Calls_LayoutManager_InvalidateMeasure_Once() |
|||
{ |
|||
var target = new Mock<ILayoutManager>(); |
|||
|
|||
using (Start(target.Object)) |
|||
{ |
|||
var control = new Decorator(); |
|||
var root = new LayoutTestRoot { Child = control }; |
|||
|
|||
root.Measure(Size.Infinity); |
|||
root.Arrange(new Rect(root.DesiredSize)); |
|||
target.ResetCalls(); |
|||
|
|||
control.InvalidateMeasure(); |
|||
control.InvalidateMeasure(); |
|||
|
|||
target.Verify(x => x.InvalidateMeasure(control), Times.Once()); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Only_Calls_LayoutManager_InvalidateArrange_Once() |
|||
{ |
|||
var target = new Mock<ILayoutManager>(); |
|||
|
|||
using (Start(target.Object)) |
|||
{ |
|||
var control = new Decorator(); |
|||
var root = new LayoutTestRoot { Child = control }; |
|||
|
|||
root.Measure(Size.Infinity); |
|||
root.Arrange(new Rect(root.DesiredSize)); |
|||
target.ResetCalls(); |
|||
|
|||
control.InvalidateArrange(); |
|||
control.InvalidateArrange(); |
|||
|
|||
target.Verify(x => x.InvalidateArrange(control), Times.Once()); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Attaching_Control_To_Tree_Invalidates_Parent_Measure() |
|||
{ |
|||
var target = new Mock<ILayoutManager>(); |
|||
|
|||
using (Start(target.Object)) |
|||
{ |
|||
var control = new Decorator(); |
|||
var root = new LayoutTestRoot { Child = control }; |
|||
|
|||
root.Measure(Size.Infinity); |
|||
root.Arrange(new Rect(root.DesiredSize)); |
|||
Assert.True(control.IsMeasureValid); |
|||
|
|||
root.Child = null; |
|||
root.Measure(Size.Infinity); |
|||
root.Arrange(new Rect(root.DesiredSize)); |
|||
|
|||
Assert.False(control.IsMeasureValid); |
|||
Assert.True(root.IsMeasureValid); |
|||
|
|||
target.ResetCalls(); |
|||
|
|||
root.Child = control; |
|||
|
|||
Assert.False(root.IsMeasureValid); |
|||
Assert.False(control.IsMeasureValid); |
|||
target.Verify(x => x.InvalidateMeasure(root), Times.Once()); |
|||
} |
|||
} |
|||
|
|||
private IDisposable Start(ILayoutManager layoutManager) |
|||
{ |
|||
var result = AvaloniaLocator.EnterScope(); |
|||
AvaloniaLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(layoutManager); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +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.Controls; |
|||
|
|||
namespace Avalonia.Layout.UnitTests |
|||
{ |
|||
internal class TestLayoutRoot : Decorator, ILayoutRoot |
|||
{ |
|||
public TestLayoutRoot() |
|||
{ |
|||
ClientSize = new Size(500, 500); |
|||
} |
|||
|
|||
public Size ClientSize |
|||
{ |
|||
get; |
|||
set; |
|||
} |
|||
|
|||
public Size MaxClientSize => Size.Infinity; |
|||
public double LayoutScaling => 1; |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="Cake" version="0.18.0" /> |
|||
</packages> |
|||
Loading…
Reference in new issue