Browse Source

Merge branch 'fixes/fix-getvisualroot' into scenegraph

Conflicts:
	src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
	src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs
	src/Skia/Avalonia.Skia/BitmapImpl.cs
	src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
	src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
	tests/Avalonia.UnitTests/TestRoot.cs
	tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
	tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
	tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensionsTests_GetVisualsAt.cs
scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
cb3090e931
  1. 4
      .travis.yml
  2. 43
      Avalonia.sln
  3. 6
      appveyor.yml
  4. 2
      docs/guidelines/build.md
  5. 6
      samples/interop/Direct3DInteropSample/App.paml
  6. 13
      samples/interop/Direct3DInteropSample/App.paml.cs
  7. 34
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  8. 264
      samples/interop/Direct3DInteropSample/MainWindow.cs
  9. 14
      samples/interop/Direct3DInteropSample/MainWindow.paml
  10. 45
      samples/interop/Direct3DInteropSample/MainWindowViewModel.cs
  11. 47
      samples/interop/Direct3DInteropSample/MiniCube.fx
  12. 17
      samples/interop/Direct3DInteropSample/Program.cs
  13. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  14. 6
      src/Avalonia.Base/AvaloniaObject.cs
  15. 2
      src/Avalonia.Base/AvaloniaProperty.cs
  16. 2
      src/Avalonia.Base/PriorityValue.cs
  17. 237
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  18. 4
      src/Avalonia.Controls/TopLevel.cs
  19. 10
      src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs
  20. 49
      src/Avalonia.Diagnostics/ViewModels/TreeNode.cs
  21. 4
      src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs
  22. 7
      src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs
  23. 4
      src/Avalonia.Diagnostics/Views/TreePage.xaml.cs
  24. 7
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  25. 2
      src/Avalonia.Visuals/Visual.cs
  26. 1
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  27. 1
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  28. 1
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  29. 64
      src/Markup/Avalonia.Markup/DefaultValueConverter.cs
  30. 2
      src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
  31. 132
      src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
  32. 23
      src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs
  33. 6
      src/Skia/Avalonia.Skia/BitmapImpl.cs
  34. 2
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  35. 29
      src/Windows/Avalonia.Direct2D1/Direct2DChecker.cs
  36. 1
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  37. 2
      src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
  38. 6
      src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs
  39. 15
      src/Windows/Avalonia.Direct2D1/WindowsVersionChecker.cs
  40. 1
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  41. 53
      tests/Avalonia.LeakTests/ControlTests.cs
  42. 17
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs
  43. 2
      tests/Avalonia.RenderTests/app.config
  44. 5
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

4
.travis.yml

@ -3,6 +3,10 @@ os:
- linux
- osx
dist: trusty
env:
global:
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
- DOTNET_CLI_TELEMETRY_OPTOUT=1
mono:
- latest
dotnet: 1.0.1

43
Avalonia.sln

@ -189,6 +189,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
@ -2547,6 +2549,46 @@ Global
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Mono.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.ActiveCfg = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.Build.0 = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Mono.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Mono.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|x86.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|x86.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Mono.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Mono.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|x86.ActiveCfg = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|x86.Build.0 = Debug|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.ActiveCfg = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.Build.0 = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.ActiveCfg = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.Build.0 = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.ActiveCfg = Release|Any CPU
{638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2606,5 +2648,6 @@ Global
{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A} = {74487168-7D91-487E-BF93-055F2251461E}
{4D6FAF79-58B4-482F-9122-0668C346364C} = {74487168-7D91-487E-BF93-055F2251461E}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
EndGlobalSection
EndGlobal

6
appveyor.yml

@ -1,16 +1,20 @@
os: Previous Visual Studio 2017
os: Visual Studio 2017
platform:
- Any CPU
skip_branch_with_pr: true
configuration:
- Release
environment:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
NUGET_API_KEY:
secure: Xv89dlP2MSBZKhl1nrWSxqcDgCXB0HRhOd4SWQ+jRJ7QoLxQel5mLTipXM++J3G5
NUGET_API_URL: https://www.nuget.org/api/v2/package
MYGET_API_KEY:
secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK
MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package
init:
- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/appveyor/ci/master/scripts/xamarin-vs2017-151-fixed.targets', "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Microsoft.Common.Targets\ImportAfter\Xamarin.Common.targets")
install:
- if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi
- if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe"

2
docs/guidelines/build.md

@ -2,7 +2,7 @@
## Windows
Avalonia requires at least Visual Studio 2015 to build on Windows.
Avalonia requires at least Visual Studio 2017 to build on Windows.
### Install GTK Sharp

6
samples/interop/Direct3DInteropSample/App.paml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

13
samples/interop/Direct3DInteropSample/App.paml.cs

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Markup.Xaml;
namespace Direct3DInteropSample
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
}
}

34
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpDX.Mathematics" Version="3.1.1" />
<PackageReference Include="SharpDX.D3DCompiler" Version="3.1.1" />
<Compile Update="**\*.paml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.paml">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Remove="MiniCube.fx" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MiniCube.fx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\..\build\Splat.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project>

264
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Direct2D1.Media;
using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using Avalonia.Rendering;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.WIC;
using SharpDX.Mathematics;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using Buffer = SharpDX.Direct3D11.Buffer;
using DeviceContext = SharpDX.Direct3D11.DeviceContext;
using Factory1 = SharpDX.DXGI.Factory1;
using InputElement = SharpDX.Direct3D11.InputElement;
using Matrix = SharpDX.Matrix;
using PixelFormat = SharpDX.Direct2D1.PixelFormat;
namespace Direct3DInteropSample
{
class MainWindow : Window
{
private SharpDX.Direct3D11.Device _d3dDevice;
private SharpDX.DXGI.Device _dxgiDevice;
Texture2D backBuffer = null;
RenderTargetView renderView = null;
Texture2D depthBuffer = null;
DepthStencilView depthView = null;
private readonly SwapChain _swapChain;
private SwapChainDescription _desc;
private Matrix _proj = Matrix.Identity;
private Matrix _view;
private Buffer _contantBuffer;
private SharpDX.Direct2D1.Device _d2dDevice;
private SharpDX.Direct2D1.DeviceContext _d2dContext;
private RenderTarget _d2dRenderTarget;
private MainWindowViewModel _model;
public MainWindow()
{
_dxgiDevice = AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>();
_d3dDevice = _dxgiDevice.QueryInterface<SharpDX.Direct3D11.Device>();
_d2dDevice = AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Device>();
DataContext = _model = new MainWindowViewModel();
_desc = new SwapChainDescription()
{
BufferCount = 1,
ModeDescription =
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm),
IsWindowed = true,
OutputHandle = PlatformImpl.Handle.Handle,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
};
_swapChain = new SwapChain(new Factory1(), _d3dDevice, _desc);
_d2dContext = new SharpDX.Direct2D1.DeviceContext(_d2dDevice, DeviceContextOptions.None)
{
DotsPerInch = new Size2F(96, 96)
};
CreateMesh();
_view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY);
this.GetObservable(ClientSizeProperty).Subscribe(Resize);
Resize(ClientSize);
AvaloniaXamlLoader.Load(this);
Background = Avalonia.Media.Brushes.Transparent;
}
protected override void HandlePaint(Rect rect)
{
var viewProj = Matrix.Multiply(_view, _proj);
var context = _d3dDevice.ImmediateContext;
// Clear views
context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0);
context.ClearRenderTargetView(renderView, Color.White);
var time = 50;
// Update WorldViewProj Matrix
var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) *
Matrix.RotationZ((float) _model.RotationZ)
* Matrix.Scaling((float) _model.Zoom) * viewProj;
worldViewProj.Transpose();
context.UpdateSubresource(ref worldViewProj, _contantBuffer);
// Draw the cube
context.Draw(36, 0);
base.HandlePaint(rect);
// Present!
_swapChain.Present(0, PresentFlags.None);
}
void CreateMesh()
{
var device = _d3dDevice;
// Compile Vertex and Pixel shaders
var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0");
var vertexShader = new VertexShader(device, vertexShaderByteCode);
var pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "PS", "ps_4_0");
var pixelShader = new PixelShader(device, pixelShaderByteCode);
var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
// Layout from VertexShader input signature
var layout = new InputLayout(device, signature, new[]
{
new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0)
});
// Instantiate Vertex buiffer from vertex data
var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[]
{
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left
new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),
});
// Create Constant Buffer
_contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
var context = _d3dDevice.ImmediateContext;
// Prepare All the stages
context.InputAssembler.InputLayout = layout;
context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf<Vector4>() * 2, 0));
context.VertexShader.SetConstantBuffer(0, _contantBuffer);
context.VertexShader.Set(vertexShader);
context.PixelShader.Set(pixelShader);
}
void Resize(Size size)
{
Utilities.Dispose(ref _d2dRenderTarget);
Utilities.Dispose(ref backBuffer);
Utilities.Dispose(ref renderView);
Utilities.Dispose(ref depthBuffer);
Utilities.Dispose(ref depthView);
var context = _d3dDevice.ImmediateContext;
// Resize the backbuffer
_swapChain.ResizeBuffers(_desc.BufferCount, (int)size.Width, (int)size.Height, Format.Unknown, SwapChainFlags.None);
// Get the backbuffer from the swapchain
backBuffer = Texture2D.FromSwapChain<Texture2D>(_swapChain, 0);
// Renderview on the backbuffer
renderView = new RenderTargetView(_d3dDevice, backBuffer);
// Create the depth buffer
depthBuffer = new Texture2D(_d3dDevice, new Texture2DDescription()
{
Format = Format.D32_Float_S8X24_UInt,
ArraySize = 1,
MipLevels = 1,
Width = (int)size.Width,
Height = (int)size.Height,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.DepthStencil,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
});
// Create the depth buffer view
depthView = new DepthStencilView(_d3dDevice, depthBuffer);
// Setup targets and viewport for rendering
context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f));
context.OutputMerger.SetTargets(depthView, renderView);
// Setup new projection matrix with correct aspect ratio
_proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f);
using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0))
{
_d2dRenderTarget = new RenderTarget(AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Factory>()
, dxgiBackBuffer, new RenderTargetProperties
{
DpiX = 96,
DpiY = 96,
Type = RenderTargetType.Default,
PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied)
});
}
}
class D3DRenderTarget: IRenderTarget
{
private readonly MainWindow _window;
public D3DRenderTarget(MainWindow window)
{
_window = window;
}
public void Dispose()
{
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return new DrawingContextImpl(visualBrushRenderer, _window._d2dRenderTarget,
AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>());
}
}
protected override IRenderTarget CreateRenderTarget() => new D3DRenderTarget(this);
}
}

14
samples/interop/Direct3DInteropSample/MainWindow.paml

@ -0,0 +1,14 @@
<Window xmlns="https://github.com/avaloniaui" Background="White" Title="Avalonia Direct3D Demo">
<Grid ColumnDefinitions="*,Auto" Margin="20">
<StackPanel Grid.Column="1" MinWidth="200">
<TextBlock>Rotation X</TextBlock>
<Slider Value="{Binding RotationX, Mode=TwoWay}" Maximum="10"/>
<TextBlock>Rotation Y</TextBlock>
<Slider Value="{Binding RotationY, Mode=TwoWay}" Maximum="10"/>
<TextBlock>Rotation Z</TextBlock>
<Slider Value="{Binding RotationZ, Mode=TwoWay}" Maximum="10"/>
<TextBlock>Zoom</TextBlock>
<Slider Value="{Binding Zoom, Mode=TwoWay}" Maximum="3" Minimum="0.5"/>
</StackPanel>
</Grid>
</Window>

45
samples/interop/Direct3DInteropSample/MainWindowViewModel.cs

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ReactiveUI;
namespace Direct3DInteropSample
{
public class MainWindowViewModel : ReactiveObject
{
private double _rotationX;
public double RotationX
{
get { return _rotationX; }
set { this.RaiseAndSetIfChanged(ref _rotationX, value); }
}
private double _rotationY = 1;
public double RotationY
{
get { return _rotationY; }
set { this.RaiseAndSetIfChanged(ref _rotationY, value); }
}
private double _rotationZ = 2;
public double RotationZ
{
get { return _rotationZ; }
set { this.RaiseAndSetIfChanged(ref _rotationZ, value); }
}
private double _zoom = 1;
public double Zoom
{
get { return _zoom; }
set { this.RaiseAndSetIfChanged(ref _zoom, value); }
}
}
}

47
samples/interop/Direct3DInteropSample/MiniCube.fx

@ -0,0 +1,47 @@
// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
struct VS_IN
{
float4 pos : POSITION;
float4 col : COLOR;
};
struct PS_IN
{
float4 pos : SV_POSITION;
float4 col : COLOR;
};
float4x4 worldViewProj;
PS_IN VS( VS_IN input )
{
PS_IN output = (PS_IN)0;
output.pos = mul(input.pos, worldViewProj);
output.col = input.col;
return output;
}
float4 PS( PS_IN input ) : SV_Target
{
return input.col;
}

17
samples/interop/Direct3DInteropSample/Program.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
namespace Direct3DInteropSample
{
class Program
{
static void Main(string[] args)
{
AppBuilder.Configure<App>().UseWin32().UseDirect2D1().Start<MainWindow>();
}
}
}

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -53,6 +53,7 @@ namespace Avalonia.Android
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance)

6
src/Avalonia.Base/AvaloniaObject.cs

@ -578,13 +578,13 @@ namespace Avalonia
if (notification == null)
{
return TypeUtilities.CastOrDefault(value, type);
return TypeUtilities.ConvertImplicitOrDefault(value, type);
}
else
{
if (notification.HasValue)
{
notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type));
notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type));
}
return notification;
@ -735,7 +735,7 @@ namespace Avalonia
ThrowNotRegistered(property);
}
if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})",

2
src/Avalonia.Base/AvaloniaProperty.cs

@ -476,7 +476,7 @@ namespace Avalonia
/// <returns>True if the value is valid, otherwise false.</returns>
public bool IsValidValue(object value)
{
return TypeUtilities.TryCast(PropertyType, value, out value);
return TypeUtilities.TryConvertImplicit(PropertyType, value, out value);
}
/// <summary>

2
src/Avalonia.Base/PriorityValue.cs

@ -249,7 +249,7 @@ namespace Avalonia
value = (notification.HasValue) ? notification.Value : null;
}
if (TypeUtilities.TryCast(_valueType, value, out castValue))
if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue))
{
var old = _value;

237
src/Avalonia.Base/Utilities/TypeUtilities.cs

@ -14,17 +14,61 @@ namespace Avalonia.Utilities
/// </summary>
public static class TypeUtilities
{
private static readonly Dictionary<Type, List<Type>> Conversions = new Dictionary<Type, List<Type>>()
private static int[] Conversions =
{
{ typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
{ typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
{ typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
{ typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
{ typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
{ typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
{ typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
{ typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
{ typeof(short), new List<Type> { typeof(byte) } }
0b101111111111101, // Boolean
0b100001111111110, // Char
0b101111111111111, // SByte
0b101111111111111, // Byte
0b101111111111111, // Int16
0b101111111111111, // UInt16
0b101111111111111, // Int32
0b101111111111111, // UInt32
0b101111111111111, // Int64
0b101111111111111, // UInt64
0b101111111111101, // Single
0b101111111111101, // Double
0b101111111111101, // Decimal
0b110000000000000, // DateTime
0b111111111111111, // String
};
private static int[] ImplicitConversions =
{
0b000000000000001, // Boolean
0b001110111100010, // Char
0b001110101010100, // SByte
0b001111111111000, // Byte
0b001110101010000, // Int16
0b001111111100000, // UInt16
0b001110101000000, // Int32
0b001111110000000, // UInt32
0b001110100000000, // Int64
0b001111000000000, // UInt64
0b000110000000000, // Single
0b000100000000000, // Double
0b001000000000000, // Decimal
0b010000000000000, // DateTime
0b100000000000000, // String
};
private static Type[] InbuiltTypes =
{
typeof(Boolean),
typeof(Char),
typeof(SByte),
typeof(Byte),
typeof(Int16),
typeof(UInt16),
typeof(Int32),
typeof(UInt32),
typeof(Int64),
typeof(UInt64),
typeof(Single),
typeof(Double),
typeof(Decimal),
typeof(DateTime),
typeof(String),
};
private static readonly Type[] NumericTypes = new[]
@ -54,49 +98,104 @@ namespace Avalonia.Utilities
}
/// <summary>
/// Try to cast a value to a type, using implicit conversions if possible.
/// Try to convert a value to a type by any means possible.
/// </summary>
/// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param>
/// <param name="culture">The culture to use.</param>
/// <param name="result">If sucessful, contains the cast value.</param>
/// <returns>True if the cast was sucessful, otherwise false.</returns>
public static bool TryCast(Type to, object value, out object result)
public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
{
Contract.Requires<ArgumentNullException>(to != null);
if (value == null)
{
result = null;
return AcceptsNull(to);
}
var from = value.GetType();
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
else if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
var from = value.GetType();
var fromTypeInfo = from.GetTypeInfo();
var toTypeInfo = to.GetTypeInfo();
if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
result = value;
return true;
}
else if (Conversions.ContainsKey(to) && Conversions[to].Contains(from))
if (to == typeof(string))
{
result = Convert.ChangeType(value, to);
result = Convert.ToString(value);
return true;
}
else
if (toTypeInfo.IsEnum && from == typeof(string))
{
if (Enum.IsDefined(to, (string)value))
{
result = Enum.Parse(to, (string)value);
return true;
}
}
if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum)
{
var cast = from.GetRuntimeMethods()
.FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
result = null;
if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue))
{
result = Enum.ToObject(to, enumValue);
return true;
}
}
if (cast != null)
if (fromTypeInfo.IsEnum && IsNumeric(to))
{
try
{
result = cast.Invoke(null, new[] { value });
result = Convert.ChangeType((int)value, to, culture);
return true;
}
catch
{
result = null;
return false;
}
}
var convertableFrom = Array.IndexOf(InbuiltTypes, from);
var convertableTo = Array.IndexOf(InbuiltTypes, to);
if (convertableFrom != -1 && convertableTo != -1)
{
if ((Conversions[convertableFrom] & 1 << convertableTo) != 0)
{
try
{
result = Convert.ChangeType(value, to, culture);
return true;
}
catch
{
result = null;
return false;
}
}
}
var cast = from.GetRuntimeMethods()
.FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
if (cast != null)
{
result = cast.Invoke(null, new[] { value });
return true;
}
result = null;
@ -104,15 +203,14 @@ namespace Avalonia.Utilities
}
/// <summary>
/// Try to convert a value to a type, using <see cref="System.Convert"/> if possible,
/// otherwise using <see cref="TryCast(Type, object, out object)"/>.
/// Try to convert a value to a type using the implicit conversions allowed by the C#
/// language.
/// </summary>
/// <param name="to">The type to cast to.</param>
/// <param name="value">The value to cast.</param>
/// <param name="culture">The culture to use.</param>
/// <param name="result">If sucessful, contains the cast value.</param>
/// <returns>True if the cast was sucessful, otherwise false.</returns>
public static bool TryConvert(Type to, object value, CultureInfo culture, out object result)
public static bool TryConvertImplicit(Type to, object value, out object result)
{
if (value == null)
{
@ -120,54 +218,44 @@ namespace Avalonia.Utilities
return AcceptsNull(to);
}
var from = value.GetType();
if (value == AvaloniaProperty.UnsetValue)
{
result = value;
return true;
}
if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo()))
{
result = value;
return true;
}
var from = value.GetType();
var fromTypeInfo = from.GetTypeInfo();
var toTypeInfo = to.GetTypeInfo();
if (to == typeof(string))
if (toTypeInfo.IsAssignableFrom(fromTypeInfo))
{
result = Convert.ToString(value);
result = value;
return true;
}
if (to.GetTypeInfo().IsEnum && from == typeof(string))
{
if (Enum.IsDefined(to, (string)value))
{
result = Enum.Parse(to, (string)value);
return true;
}
}
bool containsFrom = Conversions.ContainsKey(from);
bool containsTo = Conversions.ContainsKey(to);
var convertableFrom = Array.IndexOf(InbuiltTypes, from);
var convertableTo = Array.IndexOf(InbuiltTypes, to);
if ((containsFrom && containsTo) || (from == typeof(string) && containsTo))
if (convertableFrom != -1 && convertableTo != -1)
{
try
{
result = Convert.ChangeType(value, to, culture);
return true;
}
catch
if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0)
{
result = null;
return false;
try
{
result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture);
return true;
}
catch
{
result = null;
return false;
}
}
}
var cast = from.GetRuntimeMethods()
.FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to);
.FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to);
if (cast != null)
{
@ -180,29 +268,28 @@ namespace Avalonia.Utilities
}
/// <summary>
/// Casts a value to a type, returning the default for that type if the value could not be
/// cast.
/// Convert a value to a type by any means possible, returning the default for that type
/// if the value could not be converted.
/// </summary>
/// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param>
/// <param name="culture">The culture to use.</param>
/// <returns>A value of <paramref name="type"/>.</returns>
public static object CastOrDefault(object value, Type type)
public static object ConvertOrDefault(object value, Type type, CultureInfo culture)
{
var typeInfo = type.GetTypeInfo();
object result;
return TryConvert(type, value, culture, out object result) ? result : Default(type);
}
if (TypeUtilities.TryCast(type, value, out result))
{
return result;
}
else if (typeInfo.IsValueType)
{
return Activator.CreateInstance(type);
}
else
{
return null;
}
/// <summary>
/// Convert a value to a type using the implicit conversions allowed by the C# language or
/// return the default for the type if the value could not be converted.
/// </summary>
/// <param name="value">The value to cast.</param>
/// <param name="type">The type to cast to..</param>
/// <returns>A value of <paramref name="type"/>.</returns>
public static object ConvertImplicitOrDefault(object value, Type type)
{
return TryConvertImplicit(type, value, out object result) ? result : Default(type);
}
/// <summary>

4
src/Avalonia.Controls/TopLevel.cs

@ -188,8 +188,10 @@ namespace Avalonia.Controls
get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }
}
IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget();
/// <inheritdoc/>
IRenderTarget IRenderRoot.CreateRenderTarget()
protected virtual IRenderTarget CreateRenderTarget()
{
return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
}

10
src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs

@ -3,19 +3,19 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
{
internal class ControlDetailsViewModel : ReactiveObject
{
public ControlDetailsViewModel(Control control)
public ControlDetailsViewModel(IVisual control)
{
if (control != null)
if (control is AvaloniaObject avaloniaObject)
{
Properties = AvaloniaPropertyRegistry.Instance.GetRegistered(control)
.Select(x => new PropertyDetails(control, x))
Properties = AvaloniaPropertyRegistry.Instance.GetRegistered(avaloniaObject)
.Select(x => new PropertyDetails(avaloniaObject, x))
.OrderBy(x => x.IsAttached)
.ThenBy(x => x.Name);
}

49
src/Avalonia.Diagnostics/ViewModels/TreeNode.cs

@ -5,8 +5,8 @@ using System;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.VisualTree;
using ReactiveUI;
namespace Avalonia.Diagnostics.ViewModels
@ -16,32 +16,35 @@ namespace Avalonia.Diagnostics.ViewModels
private string _classes;
private bool _isExpanded;
public TreeNode(Control control, TreeNode parent)
public TreeNode(IVisual visual, TreeNode parent)
{
Control = control;
Parent = parent;
Type = control.GetType().Name;
Type = visual.GetType().Name;
Visual = visual;
var classesChanged = Observable.FromEventPattern<
NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
x => control.Classes.CollectionChanged += x,
x => control.Classes.CollectionChanged -= x)
.TakeUntil(((IStyleable)control).StyleDetach);
if (visual is IStyleable styleable)
{
var classesChanged = Observable.FromEventPattern<
NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
x => styleable.Classes.CollectionChanged += x,
x => styleable.Classes.CollectionChanged -= x)
.TakeUntil(((IStyleable)styleable).StyleDetach);
classesChanged.Select(_ => Unit.Default)
.StartWith(Unit.Default)
.Subscribe(_ =>
{
if (control.Classes.Count > 0)
classesChanged.Select(_ => Unit.Default)
.StartWith(Unit.Default)
.Subscribe(_ =>
{
Classes = "(" + string.Join(" ", control.Classes) + ")";
}
else
{
Classes = string.Empty;
}
});
if (styleable.Classes.Count > 0)
{
Classes = "(" + string.Join(" ", styleable.Classes) + ")";
}
else
{
Classes = string.Empty;
}
});
}
}
public IReadOnlyReactiveList<TreeNode> Children
@ -56,7 +59,7 @@ namespace Avalonia.Diagnostics.ViewModels
private set { this.RaiseAndSetIfChanged(ref _classes, value); }
}
public Control Control
public IVisual Visual
{
get;
}

4
src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs

@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels
{
Nodes = nodes;
_details = this.WhenAnyValue(x => x.SelectedNode)
.Select(x => x != null ? new ControlDetailsViewModel(x.Control) : null)
.Select(x => x != null ? new ControlDetailsViewModel(x.Visual) : null)
.ToProperty(this, x => x.Details);
}
@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.ViewModels
private TreeNode FindNode(TreeNode node, IControl control)
{
if (node.Control == control)
if (node.Visual == control)
{
return node;
}

7
src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.VisualTree;
using ReactiveUI;
@ -10,7 +11,7 @@ namespace Avalonia.Diagnostics.ViewModels
internal class VisualTreeNode : TreeNode
{
public VisualTreeNode(IVisual visual, TreeNode parent)
: base((Control)visual, parent)
: base(visual, parent)
{
var host = visual as IVisualTreeHost;
@ -23,9 +24,9 @@ namespace Avalonia.Diagnostics.ViewModels
Children = new ReactiveList<VisualTreeNode>(new[] { new VisualTreeNode(host.Root, this) });
}
if (Control != null)
if ((Visual is IStyleable styleable))
{
IsInTemplate = Control.TemplatedParent != null;
IsInTemplate = styleable.TemplatedParent != null;
}
}

4
src/Avalonia.Diagnostics/Views/TreePage.xaml.cs

@ -23,14 +23,14 @@ namespace Avalonia.Diagnostics.Views
protected void AddAdorner(object sender, PointerEventArgs e)
{
var node = (TreeNode)((Control)sender).DataContext;
var layer = AdornerLayer.GetAdornerLayer(node.Control);
var layer = AdornerLayer.GetAdornerLayer(node.Visual);
if (layer != null)
{
_adorner = new Rectangle
{
Fill = new SolidColorBrush(0x80a0c5e8),
[AdornerLayer.AdornedElementProperty] = node.Control,
[AdornerLayer.AdornedElementProperty] = node.Visual,
};
layer.Children.Add(_adorner);

7
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -20,6 +20,11 @@ namespace Avalonia.Rendering
/// </remarks>
public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer
{
class ImmediateRendererFactory : IRendererFactory
{
public IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop) => new ImmediateRenderer(root);
}
private readonly IVisual _root;
private readonly IRenderRoot _renderRoot;
private IRenderTarget _renderTarget;
@ -36,6 +41,8 @@ namespace Avalonia.Rendering
_renderRoot = root as IRenderRoot;
}
public static IRendererFactory Factory { get; } = new ImmediateRendererFactory();
/// <inheritdoc/>
public bool DrawFps { get; set; }

2
src/Avalonia.Visuals/Visual.cs

@ -494,7 +494,7 @@ namespace Avalonia
{
return;
}
var old = _visualParent;
_visualParent = value;

1
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -5,6 +5,7 @@ using Avalonia.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Rendering;
namespace Avalonia.VisualTree
{

1
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -40,6 +40,7 @@ namespace Avalonia.Gtk3
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
.Bind<IRendererFactory>().ToConstant(Instance)
.Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60))
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
}

1
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -35,6 +35,7 @@ namespace Avalonia.LinuxFramebuffer
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IRenderLoop>().ToConstant(PlatformThreadingInterface.Instance);
}

64
src/Markup/Avalonia.Markup/DefaultValueConverter.cs

@ -3,10 +3,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Utilities;
namespace Avalonia.Markup
@ -32,32 +29,28 @@ namespace Avalonia.Markup
/// <returns>The converted value.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
object result;
if (value != null &&
(TypeUtilities.TryConvert(targetType, value, culture, out result) ||
TryConvertEnum(value, targetType, culture, out result)))
if (value == null)
{
return result;
return AvaloniaProperty.UnsetValue;
}
if (value != null)
if (TypeUtilities.TryConvert(targetType, value, culture, out object result))
{
string message;
return result;
}
if (TypeUtilities.IsNumeric(targetType))
{
message = $"'{value}' is not a valid number.";
}
else
{
message = $"Could not convert '{value}' to '{targetType.Name}'.";
}
string message;
return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
if (TypeUtilities.IsNumeric(targetType))
{
message = $"'{value}' is not a valid number.";
}
else
{
message = $"Could not convert '{value}' to '{targetType.Name}'.";
}
return AvaloniaProperty.UnsetValue;
return new BindingNotification(new InvalidCastException(message), BindingErrorType.Error);
}
/// <summary>
@ -72,34 +65,5 @@ namespace Avalonia.Markup
{
return Convert(value, targetType, parameter, culture);
}
private bool TryConvertEnum(object value, Type targetType, CultureInfo cultur, out object result)
{
var valueTypeInfo = value.GetType().GetTypeInfo();
var targetTypeInfo = targetType.GetTypeInfo();
if (valueTypeInfo.IsEnum && !targetTypeInfo.IsEnum)
{
var enumValue = (int)value;
if (TypeUtilities.TryCast(targetType, enumValue, out result))
{
return true;
}
}
else if (!valueTypeInfo.IsEnum && targetTypeInfo.IsEnum)
{
object intValue;
if (TypeUtilities.TryCast(typeof(int), value, out intValue))
{
result = Enum.ToObject(targetType, intValue);
return true;
}
}
result = null;
return false;
}
}
}

2
src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj

@ -37,10 +37,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="RenderTarget.cs" />
<Compile Include="SkiaView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowDrawingContextImpl.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />

132
src/Skia/Avalonia.Skia.iOS/RenderTarget.cs

@ -1,132 +0,0 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using SkiaSharp;
using CoreGraphics;
using UIKit;
using Avalonia.Rendering;
namespace Avalonia.Skia
{
internal partial class RenderTarget : IRenderTarget
{
public SKSurface Surface { get; protected set; }
public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return new DrawingContextImpl(Surface.Canvas, visualBrushRenderer);
}
public void Dispose()
{
// Nothing to do here.
}
}
internal class WindowRenderTarget : RenderTarget
{
SKBitmap _bitmap;
int Width { get; set; }
int Height { get; set; }
public WindowRenderTarget()
{
FixSize();
}
private CGRect GetApplicationFrame()
{
// if we are excluding Status Bar then we use ApplicationFrame
// otherwise we use full screen bounds. Note that this must also match
// the Skia/AvaloniaView!!!
//
bool excludeStatusArea = false; // TODO: make this configurable later
if (excludeStatusArea)
{
return UIScreen.MainScreen.ApplicationFrame;
}
else
{
return UIScreen.MainScreen.Bounds;
}
}
private void FixSize()
{
int width, height;
GetPlatformWindowSize(out width, out height);
if (Width == width && Height == height)
return;
Width = width;
Height = height;
if (Surface != null)
{
Surface.Dispose();
}
if (_bitmap != null)
{
_bitmap.Dispose();
}
_bitmap = new SKBitmap(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
// Wrap the bitmap in a Surface and keep it cached
Surface = SKSurface.Create(_bitmap.Info, pixels, _bitmap.RowBytes);
}
private void GetPlatformWindowSize(out int w, out int h)
{
var bounds = GetApplicationFrame();
w = (int)bounds.Width;
h = (int)bounds.Height;
}
public override IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
FixSize();
var canvas = Surface.Canvas;
canvas.RestoreToCount(0);
canvas.Save();
var screenScale = UIScreen.MainScreen.Scale;
canvas.Scale((float)screenScale, (float)screenScale);
canvas.Clear(SKColors.Red);
canvas.ResetMatrix();
return new WindowDrawingContextImpl(this);
}
public void Present()
{
_bitmap.LockPixels();
IntPtr length;
var pixels = _bitmap.GetPixels(out length);
const int bitmapInfo = ((int)CGBitmapFlags.ByteOrder32Big) | ((int)CGImageAlphaInfo.PremultipliedLast);
var bounds = GetApplicationFrame();
var statusBarOffset = UIScreen.MainScreen.Bounds.Height - bounds.Height;
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
using (var bContext = new CGBitmapContext(pixels, _bitmap.Width, _bitmap.Height, 8, _bitmap.Width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
using (var image = bContext.ToImage())
using (var context = UIGraphics.GetCurrentContext())
{
// flip the image for CGContext.DrawImage
context.TranslateCTM(0, bounds.Height + statusBarOffset);
context.ScaleCTM(1, -1);
context.DrawImage(bounds, image);
}
_bitmap.UnlockPixels();
}
}
}

23
src/Skia/Avalonia.Skia.iOS/WindowDrawingContextImpl.cs

@ -1,23 +0,0 @@
namespace Avalonia.Skia
{
#if !DESKTOP
// not sure we need this yet
internal class WindowDrawingContextImpl : DrawingContextImpl
{
WindowRenderTarget _target;
public WindowDrawingContextImpl(WindowRenderTarget target)
: base(target.Surface.Canvas, null)
{
_target = target;
}
public override void Dispose()
{
base.Dispose();
_target.Present();
}
}
#endif
}

6
src/Skia/Avalonia.Skia/BitmapImpl.cs

@ -30,11 +30,7 @@ namespace Avalonia.Skia
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
using (var context = new BitmapDrawingContext(Bitmap, null))
{
context.Clear(Colors.Transparent);
}
Bitmap.Erase(SKColor.Empty);
}
public void Dispose()

2
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -98,7 +98,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RenderTarget.cs" />
<Compile Include="SwapChainRenderTarget.cs" />
<Compile Include="WindowsVersionChecker.cs" />
<Compile Include="Direct2DChecker.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

29
src/Windows/Avalonia.Direct2D1/Direct2DChecker.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
namespace Avalonia.Direct2D1
{
class Direct2DChecker : IModuleEnvironmentChecker
{
//Direct2D backend doesn't work on some machines anymore
public bool IsCompatible
{
get
{
try
{
Direct2D1Platform.InitializeDirect2D();
return true;
}
catch
{
return false;
}
}
}
}
}

1
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -17,6 +17,7 @@ namespace Avalonia.Direct2D1.Media
public class WicBitmapImpl : BitmapImpl
{
private readonly ImagingFactory _factory;
/// <summary>
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.

2
src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs

@ -7,5 +7,5 @@ using Avalonia.Direct2D1;
[assembly: AssemblyTitle("Avalonia.Direct2D1")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize),
typeof(WindowsVersionChecker))]
typeof(Direct2DChecker))]

6
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@ -99,9 +99,9 @@ namespace Avalonia.Direct2D1
Quality = 0,
},
Usage = Usage.RenderTargetOutput,
BufferCount = 2,
Scaling = Scaling.None,
SwapEffect = SwapEffect.FlipSequential,
BufferCount = 1,
Scaling = Scaling.Stretch,
SwapEffect = SwapEffect.Discard,
Flags = 0,
};

15
src/Windows/Avalonia.Direct2D1/WindowsVersionChecker.cs

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
namespace Avalonia.Direct2D1
{
class WindowsVersionChecker : IModuleEnvironmentChecker
{
//Direct2D backend doesn't work with Win7 anymore
public bool IsCompatible => Environment.OSVersion.Version >= new Version(6, 2);
}
}

1
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -58,6 +58,7 @@ namespace Avalonia.iOS
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()

53
tests/Avalonia.LeakTests/ControlTests.cs

@ -4,18 +4,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.dotMemoryUnit;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Diagnostics;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using JetBrains.dotMemoryUnit;
using Moq;
using Xunit;
using Xunit.Abstractions;
@ -33,7 +30,7 @@ namespace Avalonia.LeakTests
[Fact]
public void Canvas_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -66,7 +63,7 @@ namespace Avalonia.LeakTests
[Fact]
public void Named_Canvas_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -103,7 +100,7 @@ namespace Avalonia.LeakTests
[Fact]
public void ScrollViewer_With_Content_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -143,7 +140,7 @@ namespace Avalonia.LeakTests
[Fact]
public void TextBox_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -178,7 +175,7 @@ namespace Avalonia.LeakTests
[Fact]
public void TextBox_With_Xaml_Binding_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -225,7 +222,7 @@ namespace Avalonia.LeakTests
[Fact]
public void TextBox_Class_Listeners_Are_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
TextBox textBox;
@ -261,7 +258,7 @@ namespace Avalonia.LeakTests
[Fact]
public void TreeView_Is_Freed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
Func<Window> run = () =>
{
@ -313,7 +310,7 @@ namespace Avalonia.LeakTests
[Fact]
public void RendererIsDisposed()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using (Start())
{
var renderer = new Mock<IRenderer>();
renderer.Setup(x => x.Dispose());
@ -336,12 +333,10 @@ namespace Avalonia.LeakTests
}
}
private static void PurgeMoqReferences()
private IDisposable Start()
{
// Moq holds onto references in its mock of IRenderer in case we want to check if a method has been called;
// clear these.
var renderer = Mock.Get(AvaloniaLocator.Current.GetService<IRenderer>());
renderer.ResetCalls();
var services = TestServices.StyledWindow.With(renderer: (root, loop) => new NullRenderer());
return UnitTestApplication.Start(services);
}
private class Node
@ -349,5 +344,29 @@ namespace Avalonia.LeakTests
public string Name { get; set; }
public IEnumerable<Node> Children { get; set; }
}
private class NullRenderer : IRenderer
{
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
public void AddDirty(IVisual visual)
{
}
public void Dispose()
{
}
public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter) => null;
public void Paint(Rect rect)
{
}
public void Resized(Size size)
{
}
}
}
}

17
tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Parsers;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Parsers
{
public class SelectorParserTests
{
[Fact]
public void Parses_Boolean_Property_Selector()
{
var target = new SelectorParser((type, ns) => typeof(TextBlock));
var result = target.Parse("TextBlock[IsPointerOver=True]");
}
}
}

2
tests/Avalonia.RenderTests/app.config

@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpDX.Direct2D1" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />

5
tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs

@ -1,19 +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 System;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
using System;
using Avalonia.Controls.Shapes;
namespace Avalonia.Visuals.UnitTests.Rendering
{

Loading…
Cancel
Save