Browse Source

Merge latest from master.

pull/1044/head
José Pedro 9 years ago
parent
commit
592dbccf2d
  1. 9
      .gitmodules
  2. 5
      .ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject
  3. 5
      .ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject
  4. 5
      .ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject
  5. 3
      appveyor.yml
  6. 52
      build.cake
  7. 5
      build/Markup.props
  8. 9
      build/SharpDX.props
  9. 1
      build/UnitTests.NetCore.targets
  10. 12
      build/XUnit.props
  11. 2
      docs/tutorial/from-wpf.md
  12. 75
      packages.cake
  13. 16
      samples/BindingTest/App.config
  14. 3
      samples/BindingTest/BindingTest.csproj
  15. 2
      samples/BindingTest/MainWindow.xaml
  16. 4
      samples/BindingTest/ViewModels/MainWindowViewModel.cs
  17. 1
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  18. 16
      samples/ControlCatalog.Desktop/App.config
  19. 3
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  20. 1
      samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
  21. 2
      samples/ControlCatalog/ControlCatalog.csproj
  22. 1
      samples/ControlCatalog/MainWindow.xaml.cs
  23. 16
      samples/RenderTest/App.config
  24. 3
      samples/RenderTest/RenderTest.csproj
  25. 16
      samples/VirtualizationTest/App.config
  26. 3
      samples/VirtualizationTest/VirtualizationTest.csproj
  27. 5
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  28. 2
      samples/interop/Direct3DInteropSample/Program.cs
  29. 3
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  30. 2
      samples/interop/WindowsInteropTest/Program.cs
  31. 3
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  32. 2
      src/Android/Avalonia.Android/AndroidPlatform.cs
  33. 4
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  34. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  35. 1
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  36. 2
      src/Avalonia.Animation/Avalonia.Animation.csproj
  37. 2
      src/Avalonia.Base/Avalonia.Base.csproj
  38. 2
      src/Avalonia.Base/Collections/AvaloniaList.cs
  39. 15
      src/Avalonia.Base/Metadata/AmbientAttribute.cs
  40. 15
      src/Avalonia.Base/Metadata/TemplateContent.cs
  41. 5
      src/Avalonia.Base/Utilities/WeakObservable.cs
  42. 24
      src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs
  43. 2
      src/Avalonia.Controls/Avalonia.Controls.csproj
  44. 14
      src/Avalonia.Controls/Classes.cs
  45. 45
      src/Avalonia.Controls/Control.cs
  46. 9
      src/Avalonia.Controls/Panel.cs
  47. 7
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  48. 9
      src/Avalonia.Controls/TextBox.cs
  49. 30
      src/Avalonia.Controls/ToolTip.cs
  50. 6
      src/Avalonia.Controls/TopLevel.cs
  51. 9
      src/Avalonia.Controls/Window.cs
  52. 2
      src/Avalonia.Controls/WindowBase.cs
  53. 2
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  54. 2
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  55. 2
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  56. 3
      src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
  57. 2
      src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj
  58. 2
      src/Avalonia.Input/Avalonia.Input.csproj
  59. 2
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  60. 2
      src/Avalonia.Layout/Avalonia.Layout.csproj
  61. 2
      src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
  62. 2
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  63. 2
      src/Avalonia.Styling/Avalonia.Styling.csproj
  64. 21
      src/Avalonia.Styling/Styling/Style.cs
  65. 90
      src/Avalonia.Styling/Styling/StyleResources.cs
  66. 2
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  67. 3
      src/Avalonia.Themes.Default/CheckBox.xaml
  68. 1
      src/Avalonia.Themes.Default/DropDownItem.xaml
  69. 6
      src/Avalonia.Themes.Default/ListBox.xaml
  70. 1
      src/Avalonia.Themes.Default/ListBoxItem.xaml
  71. 1
      src/Avalonia.Themes.Default/MenuItem.xaml
  72. 3
      src/Avalonia.Themes.Default/RadioButton.xaml
  73. 3
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  74. 1
      src/Avalonia.Themes.Default/TabStripItem.xaml
  75. 6
      src/Avalonia.Themes.Default/TreeView.xaml
  76. 3
      src/Avalonia.Themes.Default/TreeViewItem.xaml
  77. 2
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  78. 51
      src/Avalonia.Visuals/Media/BrushExtensions.cs
  79. 0
      src/Avalonia.Visuals/Media/IImageBrush.cs
  80. 12
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  81. 8
      src/Avalonia.Visuals/Properties/AssemblyInfo.cs
  82. 34
      src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs
  83. 430
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  84. 88
      src/Avalonia.Visuals/Rendering/DirtyRects.cs
  85. 107
      src/Avalonia.Visuals/Rendering/DirtyVisuals.cs
  86. 51
      src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs
  87. 62
      src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs
  88. 11
      src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs
  89. 5
      src/Avalonia.Visuals/Rendering/IRenderRoot.cs
  90. 10
      src/Avalonia.Visuals/Rendering/IRenderer.cs
  91. 18
      src/Avalonia.Visuals/Rendering/IRendererFactory.cs
  92. 17
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  93. 47
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  94. 63
      src/Avalonia.Visuals/Rendering/RenderLayers.cs
  95. 31
      src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
  96. 64
      src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
  97. 392
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  98. 64
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs
  99. 101
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  100. 36
      src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs

9
.gitmodules

@ -5,9 +5,6 @@
path = src/Avalonia.HtmlRenderer/external
url = https://github.com/AvaloniaUI/HTML-Renderer.git
branch = perspex-pcl
[submodule "src/Markup/Avalonia.Markup.Xaml/OmniXAML"]
path = src/Markup/Avalonia.Markup.Xaml/OmniXAML
url = https://github.com/AvaloniaUI/OmniXAML.git
[submodule "src/Markup/Avalonia.Markup.Xaml/glass"]
path = src/Markup/Avalonia.Markup.Xaml/glass
url = https://github.com/SuperJMN/glass
[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"]
path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
url = https://github.com/AvaloniaUI/Portable.Xaml.git

5
.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

3
appveyor.yml

@ -14,7 +14,7 @@ environment:
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")
- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")}
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"
@ -26,7 +26,6 @@ before_build:
build_script:
- ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration"
after_build:
- tools\JetBrains.dotMemoryUnit\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll"
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
- pip install codecov
- codecov -f "./artifacts/coverage.xml"

52
build.cake

@ -4,8 +4,8 @@
#addin "nuget:?package=Polly&version=4.2.0"
#addin "nuget:?package=NuGet.Core&version=2.12.0"
#tool "nuget:?package=xunit.runner.console&version=2.2.0"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140"
#tool "JetBrains.ReSharper.CommandLineTools"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
@ -119,7 +119,6 @@ Task("Restore-NuGet-Packages")
}})
.Execute(()=> {
NuGetRestore(parameters.MSBuildSolution, new NuGetRestoreSettings {
ToolPath = "./tools/NuGet.CommandLine/tools/NuGet.exe",
ToolTimeout = TimeSpan.FromMinutes(toolTimeout)
});
});
@ -194,6 +193,7 @@ Task("Run-Net-Core-Unit-Tests")
Task("Run-Unit-Tests")
.IsDependentOn("Run-Net-Core-Unit-Tests")
.IsDependentOn("Build")
//.IsDependentOn("Run-Leak-Tests")
.WithCriteria(() => !parameters.SkipTests)
.Does(() =>
{
@ -206,13 +206,6 @@ Task("Run-Unit-Tests")
.Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + parameters.DirSuffix + "/" + name + ".dll")))
.ToList();
if (parameters.IsRunningOnWindows)
{
var leakTests = GetFiles("./tests/Avalonia.LeakTests/bin/" + parameters.DirSuffix + "/*.LeakTests.dll");
unitTests.AddRange(leakTests);
}
var toolPath = (parameters.IsPlatformAnyCPU || parameters.IsPlatformX86) ?
"./tools/xunit.runner.console/tools/xunit.console.x86.exe" :
"./tools/xunit.runner.console/tools/xunit.console.exe";
@ -365,6 +358,44 @@ Task("Publish-NuGet")
Information("Publish-NuGet Task failed, but continuing with next Task...");
});
Task("Run-Leak-Tests")
.WithCriteria(parameters.IsRunningOnWindows)
.IsDependentOn("Build")
.Does(() =>
{
DotNetCoreRestore("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj");
DotNetBuild("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj", settings => settings.SetConfiguration("Release"));
var report = "tests\\Avalonia.LeakTests\\bin\\Release\\report.xml";
if(System.IO.File.Exists(report))
System.IO.File.Delete(report);
var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName="tests\\Avalonia.LeakTests\\toolproject\\bin\\dotMemoryUnit.exe",
Arguments="-targetExecutable=\"tools\\xunit.runner.console\\tools\\xunit.console.x86.exe\" -returnTargetExitCode -- tests\\Avalonia.LeakTests\\bin\\Release\\Avalonia.LeakTests.dll -xml tests\\Avalonia.LeakTests\\bin\\Release\\report.xml ",
UseShellExecute = false,
});
var st = System.Diagnostics.Stopwatch.StartNew();
while(!proc.HasExited && !System.IO.File.Exists(report))
{
if(st.Elapsed.TotalSeconds>60)
{
Error("Timed out, probably a bug in dotMemoryUnit");
proc.Kill();
throw new Exception("dotMemory issue");
}
proc.WaitForExit(100);
}
try{
proc.Kill();
}catch{}
var doc = System.Xml.Linq.XDocument.Load(report);
if(doc.Root.Descendants("assembly").Any(x=>x.Attribute("failed").Value.ToString() != "0"))
{
throw new Exception("Tests failed");
}
});
Task("Inspect")
.WithCriteria(parameters.IsRunningOnWindows)
.IsDependentOn("Restore-NuGet-Packages")
@ -372,7 +403,8 @@ Task("Inspect")
{
var badIssues = new []{"PossibleNullReferenceException"};
var whitelist = new []{"tests", "src\\android", "src\\ios",
"src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"};
"src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external",
"src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"};
Information("Running code inspections");

5
build/Markup.props

@ -5,5 +5,10 @@
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime" Version="4.3.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
<PackageReference Include="System.ComponentModel.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.ReaderWriter" Version="4.3.0" />
</ItemGroup>
</Project>

9
build/SharpDX.props

@ -1,8 +1,9 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SharpDX" Version="3.1.1" />
<PackageReference Include="SharpDX.Direct2D1" Version="3.1.1" />
<PackageReference Include="SharpDX.Direct3D11" Version="3.1.1" />
<PackageReference Include="SharpDX.DXGI" Version="3.1.1" />
<PackageReference Include="SharpDX" Version="4.0.1" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.0.1" />
<PackageReference Include="SharpDX.Direct3D11" Version="4.0.1" />
<PackageReference Include="SharpDX.DXGI" Version="4.0.1" />
<PackageReference Include="SharpDX.Direct3D9" Version="4.0.1" Condition="'$(UseDirect3D9)' == 'true'" />
</ItemGroup>
</Project>

1
build/UnitTests.NetCore.targets

@ -25,5 +25,4 @@
<ItemGroup>
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
</ItemGroup>
<Import Condition="'$(TargetFramework)' == 'net461'" Project="$(MSBuildThisFileDirectory)..\src\Shared\nuget.workaround.targets" />
</Project>

12
build/XUnit.props

@ -12,4 +12,16 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.1'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
</ItemGroup>
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<Target Name="ForceGenerationOfBindingRedirects"
AfterTargets="ResolveAssemblyReferences"
BeforeTargets="GenerateBindingRedirects"
Condition="'$(AutoGenerateBindingRedirects)' == 'true'">
<PropertyGroup>
<!-- Needs to be set in a target because it has to be set after the initial evaluation in the common targets -->
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
</Target>
</Project>

2
docs/tutorial/from-wpf.md

@ -161,7 +161,7 @@ the same way that event class listeners are added](../spec/working-with-properti
## RenderTransforms and RenderTransformOrigin
RenderTransformOrigins are different in WPF and Avalonia: If you apply a `RenderTransform`, keep in mind that our default value for the RenderTransformOrigin is `RelativePoint.Middle`. In WPF the default value is `RelativePoint.TopLeft` (0, 0). In controls like Viewbox (currently being developed) the same code will lead to a different rendering behavior:
RenderTransformOrigins are different in WPF and Avalonia: If you apply a `RenderTransform`, keep in mind that our default value for the RenderTransformOrigin is `RelativePoint.Center`. In WPF the default value is `RelativePoint.TopLeft` (0, 0). In controls like Viewbox (currently being developed) the same code will lead to a different rendering behavior:
In WPF:
![WPF](https://files.gitter.im/AvaloniaUI/Avalonia/cDrM/image.png)

75
packages.cake

@ -8,6 +8,42 @@ public class Packages
public string NugetPackagesDir {get; private set;}
public string SkiaSharpVersion {get; private set; }
public string SkiaSharpLinuxVersion {get; private set; }
public Dictionary<string, IList<Tuple<string,string>>> PackageVersions{get; private set;}
class DependencyBuilder : List<NuSpecDependency>
{
Packages _parent;
public DependencyBuilder(Packages parent)
{
_parent = parent;
}
string GetVersion(string name)
{
return _parent.PackageVersions[name].First().Item1;
}
public DependencyBuilder Dep(string name, params string[] fws)
{
if(fws.Length == 0)
Add(new NuSpecDependency() { Id = name, Version = GetVersion(name) });
foreach(var fw in fws)
Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) });
return this;
}
public DependencyBuilder Deps(string[] fws, params string[] deps)
{
foreach(var fw in fws)
foreach(var name in deps)
Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) });
return this;
}
}
//new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
public Packages(ICakeContext context, Parameters parameters)
{
// NUGET NUSPECS
@ -26,7 +62,7 @@ public class Packages
// Key: Package Id
// Value is Tuple where Item1: Package Version, Item2: The *.csproj/*.props file path.
var packageVersions = new Dictionary<string, IList<Tuple<string,string>>>();
PackageVersions = packageVersions;
System.IO.Directory.EnumerateFiles(((DirectoryPath)context.Directory("./build")).FullPath,
"*.props", SearchOption.AllDirectories).ToList().ForEach(fileName =>
{
@ -81,6 +117,7 @@ public class Packages
var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1;
var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1;
var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1;
var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1;
var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1;
context.Information("Package: Serilog, version: {0}", SerilogVersion);
@ -93,6 +130,7 @@ public class Packages
context.Information("Package: SharpDX, version: {0}", SharpDXVersion);
context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version);
context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version);
context.Information("Package: SharpDX.Direct3D9, version: {0}", SharpDXDirect3D9Version);
context.Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion);
var nugetPackagesDir = System.Environment.GetEnvironmentVariable("NUGET_HOME")
@ -148,12 +186,12 @@ public class Packages
};
var coreLibrariesFiles = coreLibraries.Select((lib) => {
return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.1/" + lib[1] + lib[2]);
return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]);
}).ToList();
var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent {
Source = file.FullPath, Target = "lib/netstandard1.1"
Source = file.FullPath, Target = "lib/netstandard1.3"
};
});
@ -193,13 +231,12 @@ public class Packages
new NuGetPackSettings()
{
Id = "Avalonia",
Dependencies = new []
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 = "System.ValueTuple", Version = SystemValueTupleVersion },
//.NET Core
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" },
@ -208,8 +245,11 @@ public class Packages
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "System.ValueTuple", TargetFramework = "netcoreapp1.0", Version = SystemValueTupleVersion }
},
}
.Deps(new string[]{null, "netcoreapp1.0"},
"System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives",
"System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter")
.ToArray(),
Files = coreLibrariesNuSpecContent
.Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform)
.Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform)
@ -229,9 +269,9 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.1" }
new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" }
},
BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.1"),
BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"),
OutputDirectory = parameters.NugetRoot
}
};
@ -345,7 +385,7 @@ public class Packages
Files = new []
{
new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" },
new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.1/Avalonia.Win32.dll", Target = "lib/netstandard1.1" }
new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" }
},
BasePath = context.Directory("./src/Windows"),
OutputDirectory = parameters.NugetRoot
@ -366,9 +406,9 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/net45" }
new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard1.3" }
},
BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix),
BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard1.3"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
@ -400,9 +440,9 @@ public class Packages
},
Files = new []
{
new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.1" }
new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" }
},
BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.1"),
BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"),
OutputDirectory = parameters.NugetRoot
},
///////////////////////////////////////////////////////////////////////////////
@ -458,9 +498,9 @@ public class Packages
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
//.NET Core
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version }
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.0", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version }
},
Files = new NuSpecContent[]
{
@ -476,6 +516,7 @@ public class Packages
{
new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version },
new NuSpecDependency() { Id = "SharpDX.Direct3D9", Version = SharpDXDirect3D9Version },
},
Files = new []
{

16
samples/BindingTest/App.config

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

3
samples/BindingTest/BindingTest.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BindingTest</RootNamespace>
<AssemblyName>BindingTest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
@ -162,5 +162,4 @@
<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>

2
samples/BindingTest/MainWindow.xaml

@ -44,6 +44,8 @@
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Scheduler"/>
<TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/>
<TextBlock FontSize="16" Text="Stream Operator"/>
<TextBox Watermark="StreamOperator" Text="{Binding CurrentTimeObservable^, Mode=OneWay}"/>
</StackPanel>
</StackPanel>
</StackPanel>

4
samples/BindingTest/ViewModels/MainWindowViewModel.cs

@ -49,6 +49,9 @@ namespace BindingTest.ViewModels
Thread.Sleep(1000);
}
});
CurrentTimeObservable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
.Select(x => DateTimeOffset.Now.ToString());
}
public ObservableCollection<TestItem> Items { get; }
@ -85,6 +88,7 @@ namespace BindingTest.ViewModels
private set { this.RaiseAndSetIfChanged(ref _currentTime, value); }
}
public IObservable<string> CurrentTimeObservable { get; }
public ReactiveCommand<object> StringValueCommand { get; }
public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();

1
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -158,5 +158,4 @@
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
</Project>

16
samples/ControlCatalog.Desktop/App.config

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

3
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ControlCatalog.Desktop</RootNamespace>
<AssemblyName>ControlCatalog.Desktop</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
@ -144,5 +144,4 @@
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
</Project>

1
samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj

@ -181,5 +181,4 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
</Project>

2
samples/ControlCatalog/ControlCatalog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

1
samples/ControlCatalog/MainWindow.xaml.cs

@ -10,6 +10,7 @@ namespace ControlCatalog
{
this.InitializeComponent();
this.AttachDevTools();
Renderer.DrawDirtyRects = Renderer.DrawFps = true;
}
private void InitializeComponent()

16
samples/RenderTest/App.config

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

3
samples/RenderTest/RenderTest.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RenderTest</RootNamespace>
<AssemblyName>RenderTest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
@ -183,5 +183,4 @@
<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>

16
samples/VirtualizationTest/App.config

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
<assemblyIdentity name="Mono.Cairo" publicKeyToken="0738eb9f132ed756" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0" />
<assemblyIdentity name="SharpDX.DXGI" publicKeyToken="b4dcf0f35e5521f1" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.1.1.0" newVersion="3.1.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

3
samples/VirtualizationTest/VirtualizationTest.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>VirtualizationTest</RootNamespace>
<AssemblyName>VirtualizationTest</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
@ -158,5 +158,4 @@
<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>

5
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -4,8 +4,8 @@
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpDX.Mathematics" Version="3.1.1" />
<PackageReference Include="SharpDX.D3DCompiler" Version="3.1.1" />
<PackageReference Include="SharpDX.Mathematics" Version="4.0.1" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.0.1" />
<Compile Update="**\*.paml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
@ -31,5 +31,4 @@
<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>

2
samples/interop/Direct3DInteropSample/Program.cs

@ -11,7 +11,7 @@ namespace Direct3DInteropSample
{
static void Main(string[] args)
{
AppBuilder.Configure<App>().UseWin32().UseDirect2D1().Start<MainWindow>();
AppBuilder.Configure<App>().UseWin32(deferredRendering: false).UseDirect2D1().Start<MainWindow>();
}
}
}

3
samples/interop/GtkInteropDemo/GtkInteropDemo.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GtkInteropDemo</RootNamespace>
<AssemblyName>GtkInteropDemo</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
@ -151,5 +151,4 @@
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project>

2
samples/interop/WindowsInteropTest/Program.cs

@ -15,7 +15,7 @@ namespace WindowsInteropTest
{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
AppBuilder.Configure<App>().UseWin32().UseSkia().SetupWithoutStarting();
AppBuilder.Configure<App>().UseWin32().UseDirect2D1().SetupWithoutStarting();
System.Windows.Forms.Application.Run(new SelectorForm());
}
}

3
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WindowsInteropTest</RootNamespace>
<AssemblyName>WindowsInteropTest</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
@ -186,5 +186,4 @@
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\..\build\SkiaSharp.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project>

2
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -52,13 +52,11 @@ namespace Avalonia.Android
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
.Bind<IPlatformSettings>().ToConstant(Instance)
.Bind<IRendererFactory>().ToConstant(ImmediateRenderer.Factory)
.Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
.Bind<IWindowingPlatform>().ToConstant(Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderLoop>().ToConstant(new DefaultRenderLoop(60))
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize();

4
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -133,8 +133,4 @@
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\Shared\nuget.workaround.targets" />
<PropertyGroup Condition="'$(UseRoslynPathHack)' == ''">
<CscToolPath>$(MSBuildToolsPath)\Roslyn</CscToolPath>
</PropertyGroup>
</Project>

8
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -13,6 +13,7 @@ using System.Reactive.Disposables;
using Avalonia.Android.Platform.Input;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform
{
@ -85,7 +86,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public IPlatformHandle Handle => _view;
public IEnumerable<object> Surfaces => new object[] {this};
public IRenderer CreateRenderer(IRenderRoot root)
{
return new ImmediateRenderer(root);
}
public virtual void Hide()
{
_view.Visibility = ViewStates.Invisible;

1
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -157,5 +157,4 @@
<Import Project="..\..\..\build\Splat.props" />
<Import Project="..\..\..\build\Sprache.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\Shared\nuget.workaround.targets" />
</Project>

2
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Base/Avalonia.Base.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -220,7 +220,7 @@ namespace Avalonia.Collections
/// <summary>
/// Removes all items from the collection.
/// </summary>
public void Clear()
public virtual void Clear()
{
if (this.Count > 0)
{

15
src/Avalonia.Base/Metadata/AmbientAttribute.cs

@ -0,0 +1,15 @@
// 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;
namespace Avalonia.Metadata
{
/// <summary>
/// Defines the ambient class/property
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, Inherited = true)]
public class AmbientAttribute : Attribute
{
}
}

15
src/Avalonia.Base/Metadata/TemplateContent.cs

@ -0,0 +1,15 @@
// 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;
namespace Avalonia.Metadata
{
/// <summary>
/// Defines the property that contains the object's content in markup.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class TemplateContentAttribute : Attribute
{
}
}

5
src/Avalonia.Base/Utilities/WeakObservable.cs

@ -16,12 +16,13 @@ namespace Avalonia.Utilities
/// Converts a .NET event conforming to the standard .NET event pattern into an observable
/// sequence, subscribing weakly.
/// </summary>
/// <typeparam name="TTarget">The type of target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
/// <param name="target">Object instance that exposes the event to convert.</param>
/// <param name="eventName">Name of the event to convert.</param>
/// <returns></returns>
public static IObservable<EventPattern<object, TEventArgs>> FromEventPattern<TEventArgs>(
object target,
public static IObservable<EventPattern<object, TEventArgs>> FromEventPattern<TTarget, TEventArgs>(
TTarget target,
string eventName)
where TEventArgs : EventArgs
{

24
src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs

@ -17,22 +17,23 @@ namespace Avalonia.Utilities
/// <summary>
/// Subscribes to an event on an object using a weak subscription.
/// </summary>
/// <typeparam name="T">The type of the event arguments.</typeparam>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <typeparam name="TEventArgs">The type of the event arguments.</typeparam>
/// <param name="target">The event source.</param>
/// <param name="eventName">The name of the event.</param>
/// <param name="subscriber">The subscriber.</param>
public static void Subscribe<T>(object target, string eventName, IWeakSubscriber<T> subscriber)
where T : EventArgs
public static void Subscribe<TTarget, TEventArgs>(TTarget target, string eventName, IWeakSubscriber<TEventArgs> subscriber)
where TEventArgs : EventArgs
{
var dic = SubscriptionTypeStorage<T>.Subscribers.GetOrCreateValue(target);
Subscription<T> sub;
var dic = SubscriptionTypeStorage<TEventArgs>.Subscribers.GetOrCreateValue(target);
Subscription<TEventArgs> sub;
if (!dic.TryGetValue(eventName, out sub))
{
dic[eventName] = sub = new Subscription<T>(dic, target, eventName);
dic[eventName] = sub = new Subscription<TEventArgs>(dic, typeof(TTarget), target, eventName);
}
sub.Add(new WeakReference<IWeakSubscriber<T>>(subscriber));
sub.Add(new WeakReference<IWeakSubscriber<TEventArgs>>(subscriber));
}
/// <summary>
@ -84,19 +85,18 @@ namespace Avalonia.Utilities
private WeakReference<IWeakSubscriber<T>>[] _data = new WeakReference<IWeakSubscriber<T>>[16];
private int _count = 0;
public Subscription(SubscriptionDic<T> sdic, object target, string eventName)
public Subscription(SubscriptionDic<T> sdic, Type targetType, object target, string eventName)
{
_sdic = sdic;
_target = target;
_eventName = eventName;
var t = target.GetType();
Dictionary<string, EventInfo> evDic;
if (!Accessors.TryGetValue(t, out evDic))
Accessors[t] = evDic = new Dictionary<string, EventInfo>();
if (!Accessors.TryGetValue(targetType, out evDic))
Accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
if (!evDic.TryGetValue(eventName, out _info))
{
var ev = t.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName);
var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName);
if (ev == null)
{

2
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

14
src/Avalonia.Controls/Classes.cs

@ -86,6 +86,20 @@ namespace Avalonia.Controls
base.AddRange(c);
}
/// <summary>
/// Remvoes all non-pseudoclasses from the collection.
/// </summary>
public override void Clear()
{
for (var i = Count - 1; i >= 0; --i)
{
if (!this[i].StartsWith(":"))
{
RemoveAt(i);
}
}
}
/// <summary>
/// Inserts a style class into the collection.
/// </summary>

45
src/Avalonia.Controls/Control.cs

@ -355,18 +355,28 @@ namespace Avalonia.Controls
if (--_initCount == 0 && _isAttachedToLogicalTree)
{
if (!_styled)
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
InitializeStylesIfNeeded();
if (!IsInitialized)
{
IsInitialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
InitializeIfNeeded();
}
}
private void InitializeStylesIfNeeded(bool force = false)
{
if (_initCount == 0 && (!_styled || force))
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
}
private void InitializeIfNeeded()
{
if (_initCount == 0 && !IsInitialized)
{
IsInitialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
}
@ -580,11 +590,7 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTreeCore(e);
if (!IsInitialized)
{
IsInitialized = true;
Initialized?.Invoke(this, EventArgs.Empty);
}
InitializeIfNeeded();
}
/// <inheritdoc/>
@ -752,12 +758,7 @@ namespace Avalonia.Controls
{
_isAttachedToLogicalTree = true;
if (_initCount == 0)
{
RegisterWithNameScope();
ApplyStyling();
_styled = true;
}
InitializeStylesIfNeeded(true);
OnAttachedToLogicalTree(e);
}

9
src/Avalonia.Controls/Panel.cs

@ -64,9 +64,12 @@ namespace Avalonia.Controls
{
Contract.Requires<ArgumentNullException>(value != null);
VisualChildren.Clear();
_children.Clear();
_children.AddRange(value);
if (_children != value)
{
VisualChildren.Clear();
_children.Clear();
_children.AddRange(value);
}
}
}

7
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Rendering;
using JetBrains.Annotations;
namespace Avalonia.Platform
@ -60,6 +61,12 @@ namespace Avalonia.Platform
/// </summary>
Action<double> ScalingChanged { get; set; }
/// <summary>
/// Creates a new renderer for the toplevel.
/// </summary>
/// <param name="root">The toplevel.</param>
IRenderer CreateRenderer(IRenderRoot root);
/// <summary>
/// Invalidates a rect on the toplevel.
/// </summary>

9
src/Avalonia.Controls/TextBox.cs

@ -236,6 +236,11 @@ namespace Avalonia.Controls
{
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
_presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
if(IsFocused)
{
_presenter.ShowCaret();
}
}
protected override void OnGotFocus(GotFocusEventArgs e)
@ -254,7 +259,7 @@ namespace Avalonia.Controls
}
else
{
_presenter.ShowCaret();
_presenter?.ShowCaret();
}
}
@ -263,7 +268,7 @@ namespace Avalonia.Controls
base.OnLostFocus(e);
SelectionStart = 0;
SelectionEnd = 0;
_presenter.HideCaret();
_presenter?.HideCaret();
}
protected override void OnTextInput(TextInputEventArgs e)

30
src/Avalonia.Controls/ToolTip.cs

@ -106,24 +106,28 @@ namespace Avalonia.Controls
if (control != null && control.IsVisible && control.GetVisualRoot() != null)
{
var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22);
if (s_popup == null)
if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value))
{
s_popup = new PopupRoot();
s_popup.Content = new ToolTip();
}
else
{
((ISetLogicalParent)s_popup).SetParent(null);
}
var position = control.PointToScreen(cp.Value) + new Vector(0, 22);
if (s_popup == null)
{
s_popup = new PopupRoot();
s_popup.Content = new ToolTip();
}
else
{
((ISetLogicalParent)s_popup).SetParent(null);
}
((ISetLogicalParent)s_popup).SetParent(control);
((ToolTip)s_popup.Content).Content = GetTip(control);
s_popup.Position = position;
s_popup.Show();
((ToolTip)s_popup.Content).Content = GetTip(control);
s_popup.Position = position;
s_popup.Show();
s_current = control;
s_current = control;
}
}
}

6
src/Avalonia.Controls/TopLevel.cs

@ -90,8 +90,7 @@ namespace Avalonia.Controls
_renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
var renderLoop = TryGetService<IRenderLoop>(dependencyResolver);
var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver);
Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
Renderer = impl.CreateRenderer(this);
impl.SetInputRoot(this);
@ -181,6 +180,9 @@ namespace Avalonia.Controls
/// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
/// <inheritdoc/>
double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1;
IStyleHost IStyleHost.StylingParent
{
get { return AvaloniaLocator.Current.GetService<IGlobalStyles>(); }

9
src/Avalonia.Controls/Window.cs

@ -244,8 +244,14 @@ namespace Avalonia.Controls
/// </summary>
public override void Hide()
{
if (!IsVisible)
{
return;
}
using (BeginAutoSizing())
{
Renderer?.Stop();
PlatformImpl?.Hide();
}
@ -272,6 +278,7 @@ namespace Avalonia.Controls
using (BeginAutoSizing())
{
PlatformImpl?.Show();
Renderer?.Start();
}
}
@ -318,6 +325,8 @@ namespace Avalonia.Controls
var modal = PlatformImpl?.ShowDialog();
var result = new TaskCompletionSource<TResult>();
Renderer?.Start();
Observable.FromEventPattern<EventHandler, EventArgs>(
x => this.Closed += x,
x => this.Closed -= x)

2
src/Avalonia.Controls/WindowBase.cs

@ -136,6 +136,7 @@ namespace Avalonia.Controls
try
{
Renderer?.Stop();
PlatformImpl?.Hide();
IsVisible = false;
}
@ -164,6 +165,7 @@ namespace Avalonia.Controls
}
PlatformImpl?.Show();
Renderer?.Start();
}
finally
{

2
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -5,8 +5,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using OmniXaml;
using OmniXaml.ObjectAssembler;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Markup.Xaml;

2
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

3
src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.DotNetFrameworkRuntime</RootNamespace>
<AssemblyName>Avalonia.DotNetFrameworkRuntime</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
@ -69,5 +69,4 @@
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildThisFileDirectory)..\Shared\nuget.workaround.targets" />
</Project>

2
src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<EnableDefaultItems>False</EnableDefaultItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

2
src/Avalonia.Input/Avalonia.Input.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

2
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

2
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

21
src/Avalonia.Styling/Styling/Style.cs

@ -13,10 +13,10 @@ namespace Avalonia.Styling
/// </summary>
public class Style : IStyle
{
private static Dictionary<IStyleable, List<IDisposable>> _applied =
private static Dictionary<IStyleable, List<IDisposable>> _applied =
new Dictionary<IStyleable, List<IDisposable>>();
private Dictionary<string, object> _resources;
private StyleResources _resources;
/// <summary>
/// Initializes a new instance of the <see cref="Style"/> class.
@ -37,13 +37,13 @@ namespace Avalonia.Styling
/// <summary>
/// Gets or sets a dictionary of style resources.
/// </summary>
public IDictionary<string, object> Resources
public StyleResources Resources
{
get
{
if (_resources == null)
{
_resources = new Dictionary<string, object>();
_resources = new StyleResources();
}
return _resources;
@ -51,11 +51,16 @@ namespace Avalonia.Styling
set
{
var resources = Resources;
foreach (var i in value)
if (!Equals(resources, value))
{
resources.Add(i);
foreach (var i in value)
{
resources[i.Key] = i.Value;
//resources.Add(i.Key, i.Value);
//(resources as IDictionary<string,object>).Add(i);
}
}
}
}
@ -69,7 +74,7 @@ namespace Avalonia.Styling
/// Gets or sets the style's setters.
/// </summary>
[Content]
public IEnumerable<ISetter> Setters { get; set; } = new List<ISetter>();
public IList<ISetter> Setters { get; set; } = new List<ISetter>();
/// <summary>
/// Attaches the style to a control if the style's selector matches.

90
src/Avalonia.Styling/Styling/StyleResources.cs

@ -0,0 +1,90 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Avalonia.Styling
{
/// <summary>
/// Holds resources for a <see cref="Style"/>.
/// </summary>
public class StyleResources : IDictionary<string, object>, IDictionary
{
private Dictionary<string, object> _inner = new Dictionary<string, object>();
public object this[string key]
{
get { return _inner[key]; }
set { _inner[key] = value; }
}
public int Count => _inner.Count;
ICollection<string> IDictionary<string, object>.Keys => _inner.Keys;
ICollection<object> IDictionary<string, object>.Values => _inner.Values;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
object IDictionary.this[object key]
{
get { return ((IDictionary)_inner)[key]; }
set { ((IDictionary)_inner)[key] = value; }
}
ICollection IDictionary.Keys => _inner.Keys;
ICollection IDictionary.Values => _inner.Values;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot;
bool IDictionary.IsFixedSize => false;
bool IDictionary.IsReadOnly => false;
public void Add(string key, object value) => _inner.Add(key, value);
public void Clear() => _inner.Clear();
public bool ContainsKey(string key) => _inner.ContainsKey(key);
public bool Remove(string key) => _inner.Remove(key);
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => _inner.GetEnumerator();
public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value);
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
return ((IDictionary<string, object>)_inner).Contains(item);
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
((IDictionary<string, object>)_inner).Add(item);
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
((IDictionary<string, object>)_inner).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
return ((IDictionary<string, object>)_inner).Remove(item);
}
void ICollection.CopyTo(Array array, int index) => ((IDictionary)_inner).CopyTo(array, index);
IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator();
void IDictionary.Add(object key, object value) => ((IDictionary)_inner).Add(key, value);
bool IDictionary.Contains(object key) => ((IDictionary)_inner).Contains(key);
void IDictionary.Remove(object key) => ((IDictionary)_inner).Remove(key);
}
}

2
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

3
src/Avalonia.Themes.Default/CheckBox.xaml

@ -1,10 +1,11 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="CheckBox">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="Auto,*">
<Grid ColumnDefinitions="Auto,*" Background="{TemplateBinding Background}">
<Border Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"

1
src/Avalonia.Themes.Default/DropDownItem.xaml

@ -1,5 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="DropDownItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>

6
src/Avalonia.Themes.Default/ListBox.xaml

@ -1,13 +1,13 @@
<Style xmlns="https://github.com/avaloniaui" Selector="ListBox">
<Setter Property="Background" Value="{StyleResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Name="PART_ScrollViewer">
<ScrollViewer Name="PART_ScrollViewer" Background="{TemplateBinding Background}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

1
src/Avalonia.Themes.Default/ListBoxItem.xaml

@ -1,5 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="ListBoxItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"

1
src/Avalonia.Themes.Default/MenuItem.xaml

@ -2,6 +2,7 @@
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Style Selector="MenuItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="6,0"/>
<Setter Property="Template">

3
src/Avalonia.Themes.Default/RadioButton.xaml

@ -1,10 +1,11 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="RadioButton">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="Auto,*">
<Grid ColumnDefinitions="Auto,*" Background="{TemplateBinding Background}">
<Ellipse Name="border"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"

3
src/Avalonia.Themes.Default/ScrollViewer.xaml

@ -1,8 +1,9 @@
<Style xmlns="https://github.com/avaloniaui" Selector="ScrollViewer">
<Setter Property="Template">
<ControlTemplate>
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto" Background="{TemplateBinding Background}">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Path=Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"

1
src/Avalonia.Themes.Default/TabStripItem.xaml

@ -1,5 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="TabStripItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontSize" Value="{StyleResource FontSizeLarge}"/>
<Setter Property="Foreground" Value="{StyleResource ThemeForegroundLightBrush}"/>
<Setter Property="Template">

6
src/Avalonia.Themes.Default/TreeView.xaml

@ -1,13 +1,13 @@
<Style xmlns="https://github.com/avaloniaui" Selector="TreeView">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StyleResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{StyleResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanScrollHorizontally="True">
<ScrollViewer CanScrollHorizontally="True" Background="{TemplateBinding Background}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

3
src/Avalonia.Themes.Default/TreeViewItem.xaml

@ -31,7 +31,8 @@
<Style Selector="TreeViewItem /template/ ToggleButton#expander">
<Setter Property="Template">
<ControlTemplate>
<Border Width="14"
<Border Background="Transparent"
Width="14"
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center">

2
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>

51
src/Avalonia.Visuals/Media/BrushExtensions.cs

@ -0,0 +1,51 @@
using System;
namespace Avalonia.Media
{
/// <summary>
/// Extension methods for brush classes.
/// </summary>
public static class BrushExtensions
{
/// <summary>
/// Converts a brush to an immutable brush.
/// </summary>
/// <param name="brush">The brush.</param>
/// <returns>
/// The result of calling <see cref="IMutableBrush.ToImmutable"/> if the brush is mutable,
/// otherwise <paramref name="brush"/>.
/// </returns>
public static IBrush ToImmutable(this IBrush brush)
{
Contract.Requires<ArgumentNullException>(brush != null);
return (brush as IMutableBrush)?.ToImmutable() ?? brush;
}
/// <summary>
/// Converts a pen to a pen with an immutable brush
/// </summary>
/// <param name="pen">The pen.</param>
/// <returns>
/// A copy of the pen with an immutable brush, or <paramref name="pen"/> if the pen's brush
/// is already immutable or null.
/// </returns>
public static Pen ToImmutable(this Pen pen)
{
Contract.Requires<ArgumentNullException>(pen != null);
var brush = pen?.Brush?.ToImmutable();
return pen == null || ReferenceEquals(pen?.Brush, brush) ?
pen :
new Pen(
brush,
thickness: pen.Thickness,
dashStyle: pen.DashStyle,
dashCap: pen.DashCap,
startLineCap: pen.StartLineCap,
endLineCap: pen.EndLineCap,
lineJoin: pen.LineJoin,
miterLimit: pen.MiterLimit);
}
}
}

0
src/Avalonia.Visuals/Media/Imaging/IImageBrush.cs → src/Avalonia.Visuals/Media/IImageBrush.cs

12
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -3,7 +3,6 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Platform
{
@ -32,6 +31,15 @@ namespace Avalonia.Platform
/// <param name="destRect">The rect in the output to draw to.</param>
void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect);
/// <summary>
/// Draws a bitmap image.
/// </summary>
/// <param name="source">The bitmap image.</param>
/// <param name="opacityMask">The opacity mask to draw with.</param>
/// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
/// <param name="destRect">The rect in the output to draw to.</param>
void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect);
/// <summary>
/// Draws a line.
/// </summary>
@ -100,4 +108,4 @@ namespace Avalonia.Platform
void PopGeometryClip();
}
}
}

8
src/Avalonia.Visuals/Properties/AssemblyInfo.cs

@ -2,8 +2,14 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
using System.Runtime.CompilerServices;
using Avalonia.Metadata;
[assembly: AssemblyTitle("Avalonia.Visuals")]
[assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")]
[assembly: InternalsVisibleTo("Avalonia.Cairo.RenderTests")]
[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")]
[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")]

34
src/Avalonia.Visuals/Rendering/DefaultRenderLayerFactory.cs

@ -0,0 +1,34 @@
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class DefaultRenderLayerFactory : IRenderLayerFactory
{
private IPlatformRenderInterface _renderInterface;
public DefaultRenderLayerFactory()
: this(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>())
{
}
public DefaultRenderLayerFactory(IPlatformRenderInterface renderInterface)
{
_renderInterface = renderInterface;
}
public IRenderTargetBitmapImpl CreateLayer(
IVisual layerRoot,
Size size,
double dpiX,
double dpiY)
{
return _renderInterface.CreateRenderTargetBitmap(
(int)Math.Ceiling(size.Width),
(int)Math.Ceiling(size.Height),
dpiX,
dpiY);
}
}
}

430
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -0,0 +1,430 @@
// 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.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.VisualTree;
using System.Collections.Generic;
using System.IO;
using Avalonia.Media.Immutable;
using System.Threading;
namespace Avalonia.Rendering
{
/// <summary>
/// A renderer which renders the state of the visual tree to an intermediate scene graph
/// representation which is then rendered on a rendering thread.
/// </summary>
public class DeferredRenderer : RendererBase, IRenderer, IVisualBrushRenderer
{
private readonly IDispatcher _dispatcher;
private readonly IRenderLoop _renderLoop;
private readonly IVisual _root;
private readonly ISceneBuilder _sceneBuilder;
private readonly RenderLayers _layers;
private readonly IRenderLayerFactory _layerFactory;
private bool _running;
private Scene _scene;
private IRenderTarget _renderTarget;
private DirtyVisuals _dirty;
private IRenderTargetBitmapImpl _overlay;
private bool _updateQueued;
private object _rendering = new object();
private int _lastSceneId = -1;
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
private IDrawOperation _currentDraw;
/// <summary>
/// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
/// </summary>
/// <param name="root">The control to render.</param>
/// <param name="renderLoop">The render loop.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <param name="dispatcher">The dispatcher to use. Optional.</param>
public DeferredRenderer(
IRenderRoot root,
IRenderLoop renderLoop,
ISceneBuilder sceneBuilder = null,
IRenderLayerFactory layerFactory = null,
IDispatcher dispatcher = null)
{
Contract.Requires<ArgumentNullException>(root != null);
_dispatcher = dispatcher ?? Dispatcher.UIThread;
_root = root;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_scene = new Scene(root);
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
_renderLoop = renderLoop;
}
/// <summary>
/// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
/// </summary>
/// <param name="root">The control to render.</param>
/// <param name="renderTarget">The render target.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <remarks>
/// This constructor is intended to be used for unit testing.
/// </remarks>
public DeferredRenderer(
IVisual root,
IRenderTarget renderTarget,
ISceneBuilder sceneBuilder = null,
IRenderLayerFactory layerFactory = null)
{
Contract.Requires<ArgumentNullException>(root != null);
Contract.Requires<ArgumentNullException>(renderTarget != null);
_root = root;
_renderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
_scene = new Scene(root);
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
}
/// <inheritdoc/>
public bool DrawFps { get; set; }
/// <inheritdoc/>
public bool DrawDirtyRects { get; set; }
/// <summary>
/// Gets or sets a path to which rendered frame should be rendered for debugging.
/// </summary>
public string DebugFramesPath { get; set; }
/// <inheritdoc/>
public void AddDirty(IVisual visual)
{
_dirty?.Add(visual);
}
/// <summary>
/// Disposes of the renderer and detaches from the render loop.
/// </summary>
public void Dispose() => Stop();
/// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
{
if (_renderLoop == null && (_dirty == null || _dirty.Count > 0))
{
// When unit testing the renderLoop may be null, so update the scene manually.
UpdateScene();
}
return _scene.HitTest(p, filter);
}
/// <inheritdoc/>
public void Paint(Rect rect)
{
}
/// <inheritdoc/>
public void Resized(Size size)
{
}
/// <inheritdoc/>
public void Start()
{
if (!_running && _renderLoop != null)
{
_renderLoop.Tick += OnRenderLoopTick;
_running = true;
}
}
/// <inheritdoc/>
public void Stop()
{
if (_running && _renderLoop != null)
{
_renderLoop.Tick -= OnRenderLoopTick;
_running = false;
}
}
/// <inheritdoc/>
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{
return (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
}
/// <inheritdoc/>
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
var childScene = (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual];
if (childScene != null)
{
Render(context, (VisualNode)childScene.Root, null, new Rect(childScene.Size));
}
}
internal void UnitTestUpdateScene() => UpdateScene();
internal void UnitTestRender() => Render(_scene);
private void Render(Scene scene)
{
_dirtyRectsDisplay.Tick();
if (scene.Size != Size.Empty)
{
if (scene.Generation != _lastSceneId)
{
_layers.Update(scene);
RenderToLayers(scene);
if (DebugFramesPath != null)
{
SaveDebugFrames(scene.Generation);
}
_lastSceneId = scene.Generation;
}
RenderOverlay(scene);
RenderComposite(scene);
}
}
private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds)
{
if (layer == null || node.LayerRoot == layer)
{
clipBounds = node.ClipBounds.Intersect(clipBounds);
if (!clipBounds.IsEmpty)
{
node.BeginRender(context);
foreach (var operation in node.DrawOperations)
{
_currentDraw = operation;
operation.Render(context);
_currentDraw = null;
}
foreach (var child in node.Children)
{
Render(context, (VisualNode)child, layer, clipBounds);
}
node.EndRender(context);
}
}
}
private void RenderToLayers(Scene scene)
{
if (scene.Layers.HasDirty)
{
foreach (var layer in scene.Layers)
{
var renderTarget = _layers[layer.LayerRoot].Bitmap;
var node = (VisualNode)scene.FindNode(layer.LayerRoot);
if (node != null)
{
using (var context = renderTarget.CreateDrawingContext(this))
{
foreach (var rect in layer.Dirty)
{
context.Transform = Matrix.Identity;
context.PushClip(rect);
context.Clear(Colors.Transparent);
Render(context, node, layer.LayerRoot, rect);
context.PopClip();
if (DrawDirtyRects)
{
_dirtyRectsDisplay.Add(rect);
}
}
}
}
}
}
}
private void RenderOverlay(Scene scene)
{
if (DrawDirtyRects)
{
var overlay = GetOverlay(scene.Size, scene.Scaling);
using (var context = overlay.CreateDrawingContext(this))
{
context.Clear(Colors.Transparent);
RenderDirtyRects(context);
}
}
else
{
_overlay?.Dispose();
_overlay = null;
}
}
private void RenderDirtyRects(IDrawingContextImpl context)
{
foreach (var r in _dirtyRectsDisplay)
{
var brush = new ImmutableSolidColorBrush(Colors.Magenta, r.Opacity);
context.FillRectangle(brush, r.Rect);
}
}
private void RenderComposite(Scene scene)
{
try
{
if (_renderTarget == null)
{
_renderTarget = ((IRenderRoot)_root).CreateRenderTarget();
}
using (var context = _renderTarget.CreateDrawingContext(this))
{
var clientRect = new Rect(scene.Size);
foreach (var layer in scene.Layers)
{
var bitmap = _layers[layer.LayerRoot].Bitmap;
var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
if (layer.GeometryClip != null)
{
context.PushGeometryClip(layer.GeometryClip);
}
if (layer.OpacityMask == null)
{
context.DrawImage(bitmap, layer.Opacity, sourceRect, clientRect);
}
else
{
context.DrawImage(bitmap, layer.OpacityMask, layer.OpacityMaskRect, sourceRect);
}
if (layer.GeometryClip != null)
{
context.PopGeometryClip();
}
}
if (_overlay != null)
{
var sourceRect = new Rect(0, 0, _overlay.PixelWidth, _overlay.PixelHeight);
context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
}
if (DrawFps)
{
RenderFps(context, clientRect, true);
}
}
}
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget?.Dispose();
_renderTarget = null;
}
}
private void UpdateScene()
{
Dispatcher.UIThread.VerifyAccess();
try
{
var scene = _scene.Clone();
if (_dirty == null)
{
_dirty = new DirtyVisuals();
_sceneBuilder.UpdateAll(scene);
}
else if (_dirty.Count > 0)
{
foreach (var visual in _dirty)
{
_sceneBuilder.Update(scene, visual);
}
}
Interlocked.Exchange(ref _scene, scene);
_dirty.Clear();
(_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
}
finally
{
_updateQueued = false;
}
}
private void OnRenderLoopTick(object sender, EventArgs e)
{
if (Monitor.TryEnter(_rendering))
{
try
{
if (!_updateQueued && (_dirty == null || _dirty.Count > 0))
{
_updateQueued = true;
_dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
}
Scene scene = null;
Interlocked.Exchange(ref scene, _scene);
Render(scene);
}
catch { }
finally
{
Monitor.Exit(_rendering);
}
}
}
private IRenderTargetBitmapImpl GetOverlay(Size size, double scaling)
{
size = new Size(size.Width * scaling, size.Height * scaling);
if (_overlay == null ||
_overlay.PixelWidth != size.Width ||
_overlay.PixelHeight != size.Height)
{
_overlay?.Dispose();
_overlay = _layerFactory.CreateLayer(null, size, 96 * scaling, 96 * scaling);
}
return _overlay;
}
private void SaveDebugFrames(int id)
{
var index = 0;
foreach (var layer in _layers)
{
var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png");
layer.Bitmap.Save(fileName);
}
}
}
}

88
src/Avalonia.Visuals/Rendering/DirtyRects.cs

@ -0,0 +1,88 @@
// 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.Collections;
using System.Collections.Generic;
namespace Avalonia.Rendering
{
/// <summary>
/// Tracks dirty rectangles.
/// </summary>
internal class DirtyRects : IEnumerable<Rect>
{
private List<Rect> _rects = new List<Rect>();
public bool IsEmpty => _rects.Count == 0;
/// <summary>
/// Adds a dirty rectangle, extending an existing dirty rectangle if it intersects.
/// </summary>
/// <param name="rect">The dirt rectangle.</param>
/// <remarks>
/// We probably want to do this more intellegently because:
/// - Adding e.g. the top left quarter of a scene and the bottom left quarter of a scene
/// will cause the whole scene to be invalidated if they overlap by a single pixel
/// - Adding two adjacent rectangles that don't overlap will not cause them to be
/// coalesced
/// - It only coaleces the first intersecting rectangle found - one needs to
/// call <see cref="Coalesce"/> at the end of the draw cycle to coalesce the rest.
/// </remarks>
public void Add(Rect rect)
{
if (!rect.IsEmpty)
{
for (var i = 0; i < _rects.Count; ++i)
{
var r = _rects[i];
if (r.Intersects(rect))
{
_rects[i] = r.Union(rect);
return;
}
}
_rects.Add(rect);
}
}
/// <summary>
/// Works around our flimsy dirt-rect coalescing algorithm.
/// </summary>
/// <remarks>
/// See the comments in <see cref="Add(Rect)"/>.
/// </remarks>
public void Coalesce()
{
for (var i = _rects.Count - 1; i >= 0; --i)
{
var a = _rects[i];
for (var j = 0; j < i; ++j)
{
var b = _rects[j];
if (i < _rects.Count && a.Intersects(b))
{
_rects[i] = _rects[i].Union(b);
_rects.RemoveAt(i);
}
}
}
}
/// <summary>
/// Gets the dirty rectangles.
/// </summary>
/// <returns>A collection of dirty rectangles</returns>
public IEnumerator<Rect> GetEnumerator() => _rects.GetEnumerator();
/// <summary>
/// Gets the dirty rectangles.
/// </summary>
/// <returns>A collection of dirty rectangles</returns>
IEnumerator IEnumerable.GetEnumerator() => _rects.GetEnumerator();
}
}

107
src/Avalonia.Visuals/Rendering/DirtyVisuals.cs

@ -0,0 +1,107 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
/// <summary>
/// Stores a list of dirty visuals for an <see cref="IRenderer"/>.
/// </summary>
/// <remarks>
/// This class stores the dirty visuals for a scene, ordered by their distance to the root
/// visual. TODO: We probably want to put an upper limit on the number of visuals that can be
/// stored and if we reach that limit, assume all visuals are dirty.
/// </remarks>
internal class DirtyVisuals : IEnumerable<IVisual>
{
private SortedDictionary<int, List<IVisual>> _inner = new SortedDictionary<int, List<IVisual>>();
private Dictionary<IVisual, int> _index = new Dictionary<IVisual, int>();
/// <summary>
/// Gets the number of dirty visuals.
/// </summary>
public int Count => _index.Count;
/// <summary>
/// Adds a visual to the dirty list.
/// </summary>
/// <param name="visual">The dirty visual.</param>
public void Add(IVisual visual)
{
var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot);
int existingDistance;
if (_index.TryGetValue(visual, out existingDistance))
{
if (distance == existingDistance)
{
return;
}
_inner[existingDistance].Remove(visual);
_index.Remove(visual);
}
List<IVisual> list;
if (!_inner.TryGetValue(distance, out list))
{
list = new List<IVisual>();
_inner.Add(distance, list);
}
list.Add(visual);
_index.Add(visual, distance);
}
/// <summary>
/// Clears the list.
/// </summary>
public void Clear()
{
_inner.Clear();
_index.Clear();
}
/// <summary>
/// Removes a visual from the dirty list.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>True if the visual was present in the list; otherwise false.</returns>
public bool Remove(IVisual visual)
{
int distance;
if (_index.TryGetValue(visual, out distance))
{
_inner[distance].Remove(visual);
_index.Remove(visual);
return true;
}
return false;
}
/// <summary>
/// Gets the dirty visuals, in ascending order of distance to their root.
/// </summary>
/// <returns>A collection of visuals.</returns>
public IEnumerator<IVisual> GetEnumerator()
{
foreach (var i in _inner)
{
foreach (var j in i.Value)
{
yield return j;
}
}
}
/// <summary>
/// Gets the dirty visuals, in ascending order of distance to their root.
/// </summary>
/// <returns>A collection of visuals.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

51
src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs

@ -0,0 +1,51 @@
using System;
namespace Avalonia.Rendering
{
/// <summary>
/// Holds the state for a dirty rect rendered when <see cref="IRenderer.DrawDirtyRects"/> is set.
/// </summary>
internal class DisplayDirtyRect
{
public static readonly TimeSpan TimeToLive = TimeSpan.FromMilliseconds(250);
/// <summary>
/// Initializes a new instance of the <see cref="DisplayDirtyRect"/> class.
/// </summary>
/// <param name="rect">The dirt rect.</param>
public DisplayDirtyRect(Rect rect)
{
Rect = rect;
ResetLifetime();
}
/// <summary>
/// Gets the bounds of the dirty rectangle.
/// </summary>
public Rect Rect { get; }
/// <summary>
/// Gets the time at which the rectangle was made dirty.
/// </summary>
public DateTimeOffset Born { get; private set; }
/// <summary>
/// Gets the time at which the rectagle should no longer be displayed.
/// </summary>
public DateTimeOffset Dies { get; private set; }
/// <summary>
/// Gets the opacity at which to display the dirty rectangle.
/// </summary>
public double Opacity => (Dies - DateTimeOffset.UtcNow).TotalMilliseconds / TimeToLive.TotalMilliseconds;
/// <summary>
/// Resets the rectangle's lifetime.
/// </summary>
public void ResetLifetime()
{
Born = DateTimeOffset.UtcNow;
Dies = Born + TimeToLive;
}
}
}

62
src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs

@ -0,0 +1,62 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Avalonia.Rendering
{
/// <summary>
/// Holds a collection of <see cref="DisplayDirtyRect"/> objects and manages their aging.
/// </summary>
internal class DisplayDirtyRects : IEnumerable<DisplayDirtyRect>
{
private List<DisplayDirtyRect> _inner = new List<DisplayDirtyRect>();
/// <summary>
/// Adds new new dirty rect to the collection.
/// </summary>
/// <param name="rect"></param>
public void Add(Rect rect)
{
foreach (var r in _inner)
{
if (r.Rect == rect)
{
r.ResetLifetime();
return;
}
}
_inner.Add(new DisplayDirtyRect(rect));
}
/// <summary>
/// Removes dirty rects one they are no longer active.
/// </summary>
public void Tick()
{
var now = DateTimeOffset.UtcNow;
for (var i = _inner.Count - 1; i >= 0; --i)
{
var r = _inner[i];
if (now > r.Dies)
{
_inner.RemoveAt(i);
}
}
}
/// <summary>
/// Gets the dirty rects.
/// </summary>
/// <returns>A collection of <see cref="DisplayDirtyRect"/> objects.</returns>
public IEnumerator<DisplayDirtyRect> GetEnumerator() => _inner.GetEnumerator();
/// <summary>
/// Gets the dirty rects.
/// </summary>
/// <returns>A collection of <see cref="DisplayDirtyRect"/> objects.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

11
src/Avalonia.Visuals/Rendering/IRenderLayerFactory.cs

@ -0,0 +1,11 @@
using System;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public interface IRenderLayerFactory
{
IRenderTargetBitmapImpl CreateLayer(IVisual layerRoot, Size size, double dpiX, double dpiY);
}
}

5
src/Avalonia.Visuals/Rendering/IRenderRoot.cs

@ -22,6 +22,11 @@ namespace Avalonia.Rendering
/// </summary>
IRenderer Renderer { get; }
/// <summary>
/// The scaling factor to use in rendering.
/// </summary>
double RenderScaling { get; }
/// <summary>
/// Creates a render target for the window.
/// </summary>

10
src/Avalonia.Visuals/Rendering/IRenderer.cs

@ -48,5 +48,15 @@ namespace Avalonia.Rendering
/// </summary>
/// <param name="rect">The dirty rectangle.</param>
void Paint(Rect rect);
/// <summary>
/// Starts the renderer.
/// </summary>
void Start();
/// <summary>
/// Stops the renderer.
/// </summary>
void Stop();
}
}

18
src/Avalonia.Visuals/Rendering/IRendererFactory.cs

@ -1,18 +0,0 @@
using System;
namespace Avalonia.Rendering
{
/// <summary>
/// Defines a factory for creating <see cref="IRenderer"/> instances.
/// </summary>
public interface IRendererFactory
{
/// <summary>
/// Creates a new renderer for the specified render root.
/// </summary>
/// <param name="root">The render root.</param>
/// <param name="renderLoop">The render loop.</param>
/// <returns>An instance of an <see cref="IRenderer"/>.</returns>
IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop);
}
}

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

@ -20,11 +20,6 @@ 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;
@ -41,8 +36,6 @@ namespace Avalonia.Rendering
_renderRoot = root as IRenderRoot;
}
public static IRendererFactory Factory { get; } = new ImmediateRendererFactory();
/// <inheritdoc/>
public bool DrawFps { get; set; }
@ -148,6 +141,16 @@ namespace Avalonia.Rendering
return HitTest(_root, p, filter);
}
/// <inheritdoc/>
public void Start()
{
}
/// <inheritdoc/>
public void Stop()
{
}
/// <inheritdoc/>
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{

47
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@ -0,0 +1,47 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class RenderLayer
{
private readonly IRenderLayerFactory _factory;
public RenderLayer(
IRenderLayerFactory factory,
Size size,
double scaling,
IVisual layerRoot)
{
_factory = factory;
Bitmap = factory.CreateLayer(layerRoot, size * scaling, 96 * scaling, 96 * scaling);
Size = size;
Scaling = scaling;
LayerRoot = layerRoot;
}
public IRenderTargetBitmapImpl Bitmap { get; private set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }
public IVisual LayerRoot { get; }
public void ResizeBitmap(Size size, double scaling)
{
if (Size != size || Scaling != scaling)
{
var resized = _factory.CreateLayer(LayerRoot, size * scaling, 96 * scaling, 96 * scaling);
using (var context = resized.CreateDrawingContext(null))
{
context.Clear(Colors.Transparent);
context.DrawImage(Bitmap, 1, new Rect(Size), new Rect(Size));
Bitmap.Dispose();
Bitmap = resized;
Size = size;
}
}
}
}
}

63
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@ -0,0 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Avalonia.Rendering.SceneGraph;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class RenderLayers : IEnumerable<RenderLayer>
{
private readonly IRenderLayerFactory _factory;
private List<RenderLayer> _inner = new List<RenderLayer>();
private Dictionary<IVisual, RenderLayer> _index = new Dictionary<IVisual, RenderLayer>();
public RenderLayers(IRenderLayerFactory factory)
{
_factory = factory;
}
public int Count => _inner.Count;
public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
public void Update(Scene scene)
{
for (var i = scene.Layers.Count - 1; i >= 0; --i)
{
var src = scene.Layers[i];
RenderLayer layer;
if (!_index.TryGetValue(src.LayerRoot, out layer))
{
layer = new RenderLayer(_factory, scene.Size, scene.Scaling, src.LayerRoot);
_inner.Add(layer);
_index.Add(src.LayerRoot, layer);
}
else
{
layer.ResizeBitmap(scene.Size, scene.Scaling);
}
}
for (var i = _inner.Count - 1; i >= 0; --i)
{
var layer = _inner[i];
if (!scene.Layers.Exists(layer.LayerRoot))
{
layer.Bitmap.Dispose();
_inner.RemoveAt(i);
_index.Remove(layer.LayerRoot);
}
}
}
public bool TryGetValue(IVisual layerRoot, out RenderLayer value)
{
return _index.TryGetValue(layerRoot, out value);
}
public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

31
src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs

@ -0,0 +1,31 @@
// 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.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Base class for draw operations that can use a brush.
/// </summary>
internal abstract class BrushDrawOperation : IDrawOperation
{
/// <inheritdoc/>
public abstract Rect Bounds { get; }
/// <inheritdoc/>
public abstract bool HitTest(Point p);
/// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes.
/// </summary>
public abstract IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public abstract void Render(IDrawingContextImpl context);
}
}

64
src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs

@ -0,0 +1,64 @@
using System;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a clip push or pop.
/// </summary>
internal class ClipNode : IDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
/// clip push.
/// </summary>
/// <param name="clip">The clip to push.</param>
public ClipNode(Rect clip)
{
Clip = clip;
}
/// <summary>
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
/// clip pop.
/// </summary>
public ClipNode()
{
}
/// <inheritdoc/>
public Rect Bounds => Rect.Empty;
/// <summary>
/// Gets the clip to be pushed or null if the operation represents a pop.
/// </summary>
public Rect? Clip { get; }
/// <inheritdoc/>
public bool HitTest(Point p) => false;
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="clip">The clip of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Rect? clip) => Clip == clip;
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
{
if (Clip.HasValue)
{
context.PushClip(Clip.Value);
}
else
{
context.PopClip();
}
}
}
}

392
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -0,0 +1,392 @@
// 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.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A drawing context which builds a scene graph.
/// </summary>
internal class DeferredDrawingContextImpl : IDrawingContextImpl
{
private readonly ISceneBuilder _sceneBuilder;
private VisualNode _node;
private int _childIndex;
private int _drawOperationindex;
/// <summary>
/// Initializes a new instance of the <see cref="DeferredDrawingContextImpl"/> class.
/// </summary>
/// <param name="sceneBuilder">
/// A scene builder used for constructing child scenes for visual brushes.
/// </param>
/// <param name="layers">The scene layers.</param>
public DeferredDrawingContextImpl(ISceneBuilder sceneBuilder, SceneLayers layers)
{
_sceneBuilder = sceneBuilder;
Layers = layers;
}
/// <inheritdoc/>
public Matrix Transform { get; set; } = Matrix.Identity;
/// <summary>
/// Gets the layers in the scene being built.
/// </summary>
public SceneLayers Layers { get; }
/// <summary>
/// Informs the drawing context of the visual node that is about to be rendered.
/// </summary>
/// <param name="node">The visual node.</param>
/// <returns>
/// An object which when disposed will commit the changes to visual node.
/// </returns>
public UpdateState BeginUpdate(VisualNode node)
{
Contract.Requires<ArgumentNullException>(node != null);
if (_node != null)
{
if (_childIndex < _node.Children.Count)
{
_node.ReplaceChild(_childIndex, node);
}
else
{
_node.AddChild(node);
}
++_childIndex;
}
var state = new UpdateState(this, _node, _childIndex, _drawOperationindex);
_node = node;
_childIndex = _drawOperationindex = 0;
return state;
}
/// <inheritdoc/>
public void Clear(Color color)
{
// Cannot clear a deferred scene.
}
/// <inheritdoc/>
public void Dispose()
{
// Nothing to do here as we allocate no unmanaged resources.
}
/// <summary>
/// Removes any remaining drawing operations from the visual node.
/// </summary>
/// <remarks>
/// Drawing operations are updated in place, overwriting existing drawing operations if
/// they are different. Once drawing has completed for the current visual node, it is
/// possible that there are stale drawing operations at the end of the list. This method
/// trims these stale drawing operations.
/// </remarks>
public void TrimChildren()
{
_node.TrimChildren(_childIndex);
}
/// <inheritdoc/>
public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
{
var next = NextDrawAs<GeometryNode>();
if (next == null || !next.Equals(Transform, brush, pen, geometry))
{
Add(new GeometryNode(Transform, brush, pen, geometry, CreateChildScene(brush)));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
var next = NextDrawAs<ImageNode>();
if (next == null || !next.Equals(Transform, source, opacity, sourceRect, destRect))
{
Add(new ImageNode(Transform, source, opacity, sourceRect, destRect));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
{
// This method is currently only used to composite layers so shouldn't be called here.
throw new NotSupportedException();
}
/// <inheritdoc/>
public void DrawLine(Pen pen, Point p1, Point p2)
{
var next = NextDrawAs<LineNode>();
if (next == null || !next.Equals(Transform, pen, p1, p2))
{
Add(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush)));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0)
{
var next = NextDrawAs<RectangleNode>();
if (next == null || !next.Equals(Transform, null, pen, rect, cornerRadius))
{
Add(new RectangleNode(Transform, null, pen, rect, cornerRadius, CreateChildScene(pen.Brush)));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{
var next = NextDrawAs<TextNode>();
if (next == null || !next.Equals(Transform, foreground, origin, text))
{
Add(new TextNode(Transform, foreground, origin, text, CreateChildScene(foreground)));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
var next = NextDrawAs<RectangleNode>();
if (next == null || !next.Equals(Transform, brush, null, rect, cornerRadius))
{
Add(new RectangleNode(Transform, brush, null, rect, cornerRadius, CreateChildScene(brush)));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PopClip()
{
var next = NextDrawAs<ClipNode>();
if (next == null || !next.Equals(null))
{
Add(new ClipNode());
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PopGeometryClip()
{
var next = NextDrawAs<GeometryClipNode>();
if (next == null || !next.Equals(null))
{
Add(new GeometryClipNode());
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PopOpacity()
{
var next = NextDrawAs<OpacityNode>();
if (next == null || !next.Equals(null))
{
Add(new OpacityNode());
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PopOpacityMask()
{
var next = NextDrawAs<OpacityMaskNode>();
if (next == null || !next.Equals(null, null))
{
Add(new OpacityMaskNode());
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PushClip(Rect clip)
{
var next = NextDrawAs<ClipNode>();
if (next == null || !next.Equals(clip))
{
Add(new ClipNode(clip));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PushGeometryClip(IGeometryImpl clip)
{
var next = NextDrawAs<GeometryClipNode>();
if (next == null || !next.Equals(clip))
{
Add(new GeometryClipNode(clip));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PushOpacity(double opacity)
{
var next = NextDrawAs<OpacityNode>();
if (next == null || !next.Equals(opacity))
{
Add(new OpacityNode(opacity));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PushOpacityMask(IBrush mask, Rect bounds)
{
var next = NextDrawAs<OpacityMaskNode>();
if (next == null || !next.Equals(mask, bounds))
{
Add(new OpacityMaskNode(mask, bounds, CreateChildScene(mask)));
}
else
{
++_drawOperationindex;
}
}
public struct UpdateState : IDisposable
{
public UpdateState(
DeferredDrawingContextImpl owner,
VisualNode node,
int childIndex,
int drawOperationIndex)
{
Owner = owner;
Node = node;
ChildIndex = childIndex;
DrawOperationIndex = drawOperationIndex;
}
public void Dispose()
{
Owner._node.TrimDrawOperations(Owner._drawOperationindex);
var dirty = Owner.Layers.GetOrAdd(Owner._node.LayerRoot).Dirty;
foreach (var operation in Owner._node.DrawOperations)
{
dirty.Add(operation.Bounds);
}
Owner._node = Node;
Owner._childIndex = ChildIndex;
Owner._drawOperationindex = DrawOperationIndex;
}
public DeferredDrawingContextImpl Owner { get; }
public VisualNode Node { get; }
public int ChildIndex { get; }
public int DrawOperationIndex { get; }
}
private void Add(IDrawOperation node)
{
if (_drawOperationindex < _node.DrawOperations.Count)
{
_node.ReplaceDrawOperation(_drawOperationindex, node);
}
else
{
_node.AddDrawOperation(node);
}
++_drawOperationindex;
}
private T NextDrawAs<T>() where T : class, IDrawOperation
{
return _drawOperationindex < _node.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as T : null;
}
private IDictionary<IVisual, Scene> CreateChildScene(IBrush brush)
{
var visualBrush = brush as VisualBrush;
if (visualBrush != null)
{
var visual = visualBrush.Visual;
if (visual != null)
{
(visual as IVisualBrushInitialize)?.EnsureInitialized();
var scene = new Scene(visual);
_sceneBuilder.UpdateAll(scene);
return new Dictionary<IVisual, Scene> { { visualBrush.Visual, scene } };
}
}
return null;
}
}
}

64
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs

@ -0,0 +1,64 @@
using System;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a geometry clip push or pop.
/// </summary>
internal class GeometryClipNode : IDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="GeometryClipNode"/> class that represents a
/// geometry clip push.
/// </summary>
/// <param name="clip">The clip to push.</param>
public GeometryClipNode(IGeometryImpl clip)
{
Clip = clip;
}
/// <summary>
/// Initializes a new instance of the <see cref="GeometryClipNode"/> class that represents a
/// geometry clip pop.
/// </summary>
public GeometryClipNode()
{
}
/// <inheritdoc/>
public Rect Bounds => Rect.Empty;
/// <summary>
/// Gets the clip to be pushed or null if the operation represents a pop.
/// </summary>
public IGeometryImpl Clip { get; }
/// <inheritdoc/>
public bool HitTest(Point p) => false;
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="clip">The clip of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(IGeometryImpl clip) => Clip == clip;
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
{
if (Clip != null)
{
context.PushGeometryClip(Clip);
}
else
{
context.PopGeometryClip();
}
}
}
}

101
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -0,0 +1,101 @@
// 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.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a geometry draw.
/// </summary>
internal class GeometryNode : BrushDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="GeometryNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public GeometryNode(
Matrix transform,
IBrush brush,
Pen pen,
IGeometryImpl geometry,
IDictionary<IVisual, Scene> childScenes = null)
{
Bounds = geometry.GetRenderBounds(pen?.Thickness ?? 0).TransformToAABB(transform);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
Geometry = geometry;
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the fill brush.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the stroke pen.
/// </summary>
public Pen Pen { get; }
/// <summary>
/// Gets the geometry to draw.
/// </summary>
public IGeometryImpl Geometry { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="geometry">The geometry of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry)
{
return transform == Transform &&
Equals(brush, Brush) &&
pen == Pen &&
Equals(geometry, Geometry);
}
/// <inheritdoc/>
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawGeometry(Brush, Pen, Geometry);
}
/// <inheritdoc/>
public override bool HitTest(Point p)
{
p *= Transform.Invert();
return (Brush != null && Geometry.FillContains(p)) ||
(Pen != null && Geometry.StrokeContains(Pen, p));
}
}
}

36
src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs

@ -0,0 +1,36 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Represents a node in the low-level scene graph that represents geometry.
/// </summary>
public interface IDrawOperation
{
/// <summary>
/// Gets the bounds of the visible content in the node.
/// </summary>
Rect Bounds { get; }
/// <summary>
/// Hit test the geometry in this node.
/// </summary>
/// <param name="p">The point in global coordinates.</param>
/// <returns>True if the point hits the node's geometry; otherwise false.</returns>
/// <remarks>
/// This method does not recurse to child <see cref="IVisualNode"/>s, if you want
/// to hit test children they must be hit tested manually.
/// </remarks>
bool HitTest(Point p);
/// <summary>
/// Renders the node to a drawing context.
/// </summary>
/// <param name="context">The drawing context.</param>
void Render(IDrawingContextImpl context);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save