Browse Source

Merge branch 'master' into ghosting-fix

pull/1697/head
Jeremy Koritzinsky 8 years ago
committed by GitHub
parent
commit
0aa9ec6458
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 2
      .travis.yml
  3. 855
      Avalonia.sln
  4. 6
      appveyor.yml
  5. 76
      build.cake
  6. 8
      build/AndroidWorkarounds.props
  7. 2
      build/Base.props
  8. 5
      build/Binding.props
  9. 14
      build/Markup.props
  10. 5
      build/Sprache.props
  11. 2
      build/System.Memory.props
  12. 5
      build/iOSWorkarounds.props
  13. 2
      build/readme.md
  14. 25
      dirs.proj
  15. 6
      global.json
  16. 74
      packages.cake
  17. 31
      parameters.cake
  18. 3
      samples/BindingDemo/BindingDemo.csproj
  19. 3
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  20. 3
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  21. 4
      samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
  22. 3
      samples/RenderDemo/RenderDemo.csproj
  23. 3
      samples/VirtualizationDemo/VirtualizationDemo.csproj
  24. 144
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  25. 30
      src/Android/Avalonia.Android/Properties/AssemblyInfo.cs
  26. 5
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  27. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  28. 49
      src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs
  29. 1
      src/Avalonia.Base/Data/Core/ExpressionParseException.cs
  30. 13
      src/Avalonia.Base/Threading/Dispatcher.cs
  31. 11
      src/Avalonia.Base/Threading/IDispatcher.cs
  32. 168
      src/Avalonia.Base/Threading/JobRunner.cs
  33. 65
      src/Avalonia.Base/Utilities/CharacterReader.cs
  34. 17
      src/Avalonia.Base/Utilities/IdentifierParser.cs
  35. 1
      src/Avalonia.Controls/Window.cs
  36. 26
      src/Avalonia.Input/InputElement.cs
  37. 24
      src/Avalonia.Input/MouseDevice.cs
  38. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  39. 3
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  40. 5
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  41. 10
      src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs
  42. 4
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
  43. 21
      src/Markup/Avalonia.Markup/Data/Binding.cs
  44. 13
      src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs
  45. 6
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs
  46. 140
      src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs
  47. 395
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs
  48. 118
      src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs
  49. 14
      src/OSX/Avalonia.MonoMac/PopupImpl.cs
  50. 16
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  51. 19
      src/Shared/SharedAssemblyInfo.cs
  52. 20
      src/Shared/avalonia.platform.targets
  53. 138
      src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
  54. 36
      src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs
  55. 13
      src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.shproj
  56. 121
      src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
  57. 36
      src/iOS/Avalonia.iOS/Properties/AssemblyInfo.cs
  58. 17
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  59. 3
      src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj
  60. 126
      src/tools/Avalonia.Designer.HostApp.NetFX/Avalonia.Designer.HostApp.NetFX.csproj
  61. 2
      tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
  62. 34
      tests/Avalonia.Benchmarks/Markup/Parsing.cs
  63. 4
      tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj
  64. 159
      tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs
  65. 1
      tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj
  66. 146
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs
  67. 44
      tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs
  68. 4
      tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs
  69. 1
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  70. 74
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/PropertyParserTests.cs
  71. 22
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs
  72. 6
      tests/Avalonia.UnitTests/ImmediateDispatcher.cs
  73. 20
      tests/run-tests.sh

2
.gitignore

@ -187,3 +187,5 @@ project.lock.json
## BenchmarkDotNet ## BenchmarkDotNet
################## ##################
BenchmarkDotNet.Artifacts/ BenchmarkDotNet.Artifacts/
dirs.sln

2
.travis.yml

@ -11,7 +11,7 @@ mono:
- 5.2.0 - 5.2.0
dotnet: 2.1.200 dotnet: 2.1.200
script: script:
- ./build.sh --target "Travis" --platform "NetCoreOnly" --configuration "Release" - ./build.sh --target "Travis" --configuration "Release"
notifications: notifications:
email: false email: false
webhooks: webhooks:

855
Avalonia.sln

File diff suppressed because it is too large

6
appveyor.yml

@ -11,10 +11,6 @@ environment:
MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package
init: init:
- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")} - 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 dotnet-2.0.0.exe appveyor DownloadFile https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-x64.exe -FileName "dotnet-2.0.0.exe"
- ps: Start-Process -FilePath "dotnet-2.0.0.exe" -ArgumentList "/quiet" -Wait
- cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH%
before_build: before_build:
- git submodule update --init - git submodule update --init
build_script: build_script:
@ -25,5 +21,3 @@ artifacts:
- path: artifacts\nuget\*.nupkg - path: artifacts\nuget\*.nupkg
- path: artifacts\zip\*.zip - path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml - path: artifacts\inspectcode.xml
cache:
- dotnet-2.0.0.exe

76
build.cake

@ -2,7 +2,6 @@
// ADDINS // ADDINS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#addin "nuget:?package=Polly&version=5.3.1"
#addin "nuget:?package=NuGet.Core&version=2.14.0" #addin "nuget:?package=NuGet.Core&version=2.14.0"
#tool "nuget:?package=NuGet.CommandLine&version=4.3.0" #tool "nuget:?package=NuGet.CommandLine&version=4.3.0"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2017.1.20170613.162720" #tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2017.1.20170613.162720"
@ -22,7 +21,6 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Polly;
using NuGet; using NuGet;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -57,9 +55,8 @@ Setup<AvaloniaBuildData>(context =>
var parameters = new Parameters(context); var parameters = new Parameters(context);
var buildContext = new AvaloniaBuildData(parameters, new Packages(context, parameters)); var buildContext = new AvaloniaBuildData(parameters, new Packages(context, parameters));
Information("Building version {0} of Avalonia ({1}, {2}) using version {3} of Cake.", Information("Building version {0} of Avalonia ({1}) using version {2} of Cake.",
parameters.Version, parameters.Version,
parameters.Platform,
parameters.Configuration, parameters.Configuration,
typeof(ICakeContext).Assembly.GetName().Version.ToString()); typeof(ICakeContext).Assembly.GetName().Version.ToString());
@ -69,7 +66,6 @@ Setup<AvaloniaBuildData>(context =>
Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch); Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch);
} }
Information("Target:" + context.TargetTask.Name); Information("Target:" + context.TargetTask.Name);
Information("Platform: " + parameters.Platform);
Information("Configuration: " + parameters.Configuration); Information("Configuration: " + parameters.Configuration);
Information("IsLocalBuild: " + parameters.IsLocalBuild); Information("IsLocalBuild: " + parameters.IsLocalBuild);
Information("IsRunningOnUnix: " + parameters.IsRunningOnUnix); Information("IsRunningOnUnix: " + parameters.IsRunningOnUnix);
@ -109,60 +105,27 @@ Task("Clean-Impl")
CleanDirectory(data.Parameters.BinRoot); CleanDirectory(data.Parameters.BinRoot);
}); });
Task("Restore-NuGet-Packages-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPlatformNetCoreOnly)
.Does<AvaloniaBuildData>(data =>
{
var maxRetryCount = 5;
var toolTimeout = 2d;
Policy
.Handle<Exception>()
.Retry(maxRetryCount, (exception, retryCount, context) => {
if (retryCount == maxRetryCount)
{
throw exception;
}
else
{
Verbose("{0}", exception);
toolTimeout+=0.5;
}})
.Execute(()=> {
NuGetRestore(data.Parameters.MSBuildSolution, new NuGetRestoreSettings {
ToolTimeout = TimeSpan.FromMinutes(toolTimeout)
});
});
});
void DotNetCoreBuild(Parameters parameters) void DotNetCoreBuild(Parameters parameters)
{ {
var settings = new DotNetCoreBuildSettings var settings = new DotNetCoreBuildSettings
{ {
Configuration = parameters.Configuration, Configuration = parameters.Configuration,
MSBuildSettings = new DotNetCoreMSBuildSettings(),
}; };
settings.MSBuildSettings.SetConfiguration(parameters.Configuration);
settings.MSBuildSettings.WithProperty("Platform", "\"" + parameters.Platform + "\"");
DotNetCoreBuild(parameters.MSBuildSolution, settings); DotNetCoreBuild(parameters.MSBuildSolution, settings);
} }
Task("Build-Impl") Task("Build-Impl")
.Does<AvaloniaBuildData>(data => .Does<AvaloniaBuildData>(data =>
{ {
if(data.Parameters.IsRunningOnWindows && !data.Parameters.IsPlatformNetCoreOnly) if(data.Parameters.IsRunningOnWindows)
{ {
MSBuild(data.Parameters.MSBuildSolution, settings => { MSBuild(data.Parameters.MSBuildSolution, settings => {
settings.SetConfiguration(data.Parameters.Configuration); settings.SetConfiguration(data.Parameters.Configuration);
settings.SetVerbosity(Verbosity.Minimal); settings.SetVerbosity(Verbosity.Minimal);
settings.WithProperty("Platform", "\"" + data.Parameters.Platform + "\""); settings.WithProperty("iOSRoslynPathHackRequired", "true");
settings.WithProperty("UseRoslynPathHack", "true");
settings.UseToolVersion(MSBuildToolVersion.VS2017); settings.UseToolVersion(MSBuildToolVersion.VS2017);
settings.WithProperty("Windows", "True"); settings.WithRestore();
settings.SetNodeReuse(false);
settings.SetMaxCpuCount(0);
}); });
} }
else else
@ -207,9 +170,9 @@ Task("Run-Unit-Tests-Impl")
RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false); RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false);
if (data.Parameters.IsRunningOnWindows && !data.Parameters.IsPlatformNetCoreOnly) if (data.Parameters.IsRunningOnWindows)
{ {
RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false);
} }
}); });
@ -223,7 +186,6 @@ Task("Run-Designer-Tests-Impl")
Task("Run-Render-Tests-Impl") Task("Run-Render-Tests-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.SkipTests) .WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.SkipTests)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPlatformNetCoreOnly)
.Does<AvaloniaBuildData>(data => .Does<AvaloniaBuildData>(data =>
{ {
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true);
@ -233,7 +195,6 @@ Task("Run-Render-Tests-Impl")
Task("Run-Leak-Tests-Impl") Task("Run-Leak-Tests-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.SkipTests) .WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.SkipTests)
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPlatformNetCoreOnly)
.Does(() => .Does(() =>
{ {
var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe");
@ -266,15 +227,13 @@ Task("Zip-Files-Impl")
Zip(data.Parameters.NugetRoot, data.Parameters.ZipNuGetArtifacts); Zip(data.Parameters.NugetRoot, data.Parameters.ZipNuGetArtifacts);
if (!data.Parameters.IsPlatformNetCoreOnly) { Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs,
Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, data.Parameters.ZipTargetControlCatalogDesktopDirs,
data.Parameters.ZipTargetControlCatalogDesktopDirs, GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") +
GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") +
GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") +
GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") +
GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
}
}); });
Task("Create-NuGet-Packages-Impl") Task("Create-NuGet-Packages-Impl")
@ -353,7 +312,6 @@ Task("Publish-NuGet-Impl")
Task("Inspect-Impl") Task("Inspect-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows) .WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
.WithCriteria<AvaloniaBuildData>((context, data) => !data.Parameters.IsPlatformNetCoreOnly)
.Does(() => .Does(() =>
{ {
var badIssues = new []{"PossibleNullReferenceException"}; var badIssues = new []{"PossibleNullReferenceException"};
@ -392,10 +350,12 @@ Task("Inspect-Impl")
// TARGETS // TARGETS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
Task("Run-Tests") Task("Build")
.IsDependentOn("Clean-Impl") .IsDependentOn("Clean-Impl")
.IsDependentOn("Restore-NuGet-Packages-Impl") .IsDependentOn("Build-Impl");
.IsDependentOn("Build-Impl")
Task("Run-Tests")
.IsDependentOn("Build")
.IsDependentOn("Run-Unit-Tests-Impl") .IsDependentOn("Run-Unit-Tests-Impl")
.IsDependentOn("Run-Render-Tests-Impl") .IsDependentOn("Run-Render-Tests-Impl")
.IsDependentOn("Run-Designer-Tests-Impl") .IsDependentOn("Run-Designer-Tests-Impl")

8
build/AndroidWorkarounds.props

@ -0,0 +1,8 @@
<Project>
<ItemGroup Condition="'$(AndroidApplication)' == 'true'">
<!-- WORKAROUND: The packages below are transitively referenced by System.Memory,
but Xamarin.Android applications need the newest versions directly referenced for the linker. -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
</Project>

2
build/Base.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.ValueTuple" Version="4.3.1" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

5
build/Binding.props

@ -1,8 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.3.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.5.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" />
</ItemGroup> </ItemGroup>
</Project> </Project>

14
build/Markup.props

@ -1,14 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.3.0" />
<PackageReference Include="System.Globalization" Version="4.3.0" />
<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>

5
build/Sprache.props

@ -1,5 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Sprache" Version="2.1.0" />
</ItemGroup>
</Project>

2
build/System.Memory.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.0" /> <PackageReference Include="System.Memory" Version="4.5.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

5
build/iOSWorkarounds.props

@ -0,0 +1,5 @@
<Project>
<PropertyGroup Condition="'$(iOSRoslynPathHackRequired)' == 'true'">
<CscToolPath>$(MSBuildToolsPath)\..\Roslyn</CscToolPath>
</PropertyGroup>
</Project>

2
build/readme.md

@ -4,7 +4,6 @@
<Import Project="..\..\build\JetBrains.Annotations.props" /> <Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\JetBrains.dotMemoryUnit.props" /> <Import Project="..\..\build\JetBrains.dotMemoryUnit.props" />
<Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" /> <Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" />
<Import Project="..\..\build\Markup.props" />
<Import Project="..\..\build\Microsoft.CSharp.props" /> <Import Project="..\..\build\Microsoft.CSharp.props" />
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
<Import Project="..\..\build\Moq.props" /> <Import Project="..\..\build\Moq.props" />
@ -16,7 +15,6 @@
<Import Project="..\..\build\SkiaSharp.Desktop.props" /> <Import Project="..\..\build\SkiaSharp.Desktop.props" />
<Import Project="..\..\build\SkiaSharp.props" /> <Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\Splat.props" /> <Import Project="..\..\build\Splat.props" />
<Import Project="..\..\build\Sprache.props" />
<Import Project="..\..\build\XUnit.props" /> <Import Project="..\..\build\XUnit.props" />
``` ```

25
dirs.proj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.Build.Traversal">
<ItemGroup>
<ProjectReference Include="src/**/*.*proj" />
<ProjectReference Include="samples/**/*.*proj" />
<ProjectReference Include="tests/**/*.*proj" />
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
<ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />
<ProjectReference Remove="samples/interop/**/*.*proj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" />
</ItemGroup>
</Project>

6
global.json

@ -0,0 +1,6 @@
{
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.41",
"MSBuild.Sdk.Extras": "1.6.46"
}
}

74
packages.cake

@ -110,7 +110,6 @@ public class Packages
var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1; var SerilogVersion = packageVersions["Serilog"].FirstOrDefault().Item1;
var SerilogSinksDebugVersion = packageVersions["Serilog.Sinks.Debug"].FirstOrDefault().Item1; var SerilogSinksDebugVersion = packageVersions["Serilog.Sinks.Debug"].FirstOrDefault().Item1;
var SerilogSinksTraceVersion = packageVersions["Serilog.Sinks.Trace"].FirstOrDefault().Item1; var SerilogSinksTraceVersion = packageVersions["Serilog.Sinks.Trace"].FirstOrDefault().Item1;
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1; var ReactiveUIVersion = packageVersions["reactiveui"].FirstOrDefault().Item1;
var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1; var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1;
@ -121,10 +120,9 @@ public class Packages
var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1; var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1;
var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1; var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1;
var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1; var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1;
var SystemComponentModelAnnotationsVersion = packageVersions["System.ComponentModel.Annotations"].FirstOrDefault().Item1; var SystemMemoryVersion = packageVersions["System.Memory"].FirstOrDefault().Item1;
context.Information("Package: Serilog, version: {0}", SerilogVersion); context.Information("Package: Serilog, version: {0}", SerilogVersion);
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion); context.Information("Package: reactiveui, version: {0}", ReactiveUIVersion);
context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion); context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion);
@ -135,6 +133,7 @@ public class Packages
context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version); context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version);
context.Information("Package: SharpDX.Direct3D9, version: {0}", SharpDXDirect3D9Version); context.Information("Package: SharpDX.Direct3D9, version: {0}", SharpDXDirect3D9Version);
context.Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion); context.Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion);
context.Information("Package: System.Memory, version: {0}", SystemMemoryVersion);
var nugetPackagesDir = System.Environment.GetEnvironmentVariable("NUGET_HOME") var nugetPackagesDir = System.Environment.GetEnvironmentVariable("NUGET_HOME")
?? System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("USERPROFILE") ?? System.Environment.GetEnvironmentVariable("HOME"), ".nuget"); ?? System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("USERPROFILE") ?? System.Environment.GetEnvironmentVariable("HOME"), ".nuget");
@ -187,9 +186,9 @@ public class Packages
}; };
}); });
var win32CoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { var netFrameworkCoreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => {
return new NuSpecContent { return new NuSpecContent {
Source = file.FullPath, Target = "lib/net45" Source = file.FullPath, Target = "lib/net461"
}; };
}); });
@ -199,10 +198,10 @@ public class Packages
}; };
}); });
var net45RuntimePlatform = extensionsToPack.Select(libSuffix => { var netFrameworkRuntimePlatform = extensionsToPack.Select(libSuffix => {
return new NuSpecContent { return new NuSpecContent {
Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath, Source = ((FilePath)context.File("./src/Avalonia.DotNetFrameworkRuntime/bin/" + parameters.DirSuffix + "/net461/Avalonia.DotNetFrameworkRuntime" + libSuffix)).FullPath,
Target = "lib/net45" Target = "lib/net461"
}; };
}); });
@ -219,26 +218,16 @@ public class Packages
}; };
var toolHostAppNetFx = new NuSpecContent{ var toolHostAppNetFx = new NuSpecContent{
Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp.NetFx/bin/" + parameters.DirSuffix + "/Avalonia.Designer.HostApp.exe")).FullPath, Source = ((FilePath)context.File("./src/tools/Avalonia.Designer.HostApp.NetFx/bin/" + parameters.DirSuffix + "/net461/Avalonia.Designer.HostApp.exe")).FullPath,
Target = "tools/net461/previewer" Target = "tools/net461/previewer"
}; };
IList<NuSpecContent> coreFiles; var toolsContent = new[] { toolHostApp, toolHostAppNetFx };
var coreFiles = coreLibrariesNuSpecContent
if (!parameters.IsPlatformNetCoreOnly) { .Concat(netFrameworkCoreLibrariesNuSpecContent).Concat(netFrameworkRuntimePlatform)
var toolsContent = new[] { toolHostApp, toolHostAppNetFx }; .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform)
coreFiles = coreLibrariesNuSpecContent .Concat(toolsContent)
.Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) .ToList();
.Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform)
.Concat(toolsContent)
.ToList();
} else {
var toolsContent = new[] { toolHostApp };
coreFiles = coreLibrariesNuSpecContent
.Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform)
.Concat(toolsContent)
.ToList();
}
var nuspecNuGetSettingsCore = new [] var nuspecNuGetSettingsCore = new []
{ {
@ -250,27 +239,16 @@ public class Packages
Id = "Avalonia", Id = "Avalonia",
Dependencies = new DependencyBuilder(this) Dependencies = new DependencyBuilder(this)
{ {
new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion }, new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version, TargetFramework="netstandard2.0" },
new NuSpecDependency() { Id = "Serilog.Sinks.Debug", Version = SerilogSinksDebugVersion }, new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version, TargetFramework="netcoreapp2.0" },
new NuSpecDependency() { Id = "Serilog.Sinks.Trace", Version = SerilogSinksTraceVersion }, new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version, TargetFramework="net461" },
new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion }, new NuSpecDependency() { Id = "System.ValueTuple", Version = SystemValueTupleVersion, TargetFramework="net461" },
new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }, new NuSpecDependency() { Id = "System.ComponentModel.TypeConverter", Version = "4.3.0", TargetFramework="net461" },
new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", Version = parameters.Version }, new NuSpecDependency() { Id = "NETStandard.Library", Version = "2.0.0", TargetFramework="net461"}
new NuSpecDependency() { Id = "System.ComponentModel.Annotations", Version = SystemComponentModelAnnotationsVersion },
//.NET Core
new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp2.0", Version = "4.3.0" },
new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp2.0", Version = "1.1.0" },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp2.0", Version = "1.6.0" },
new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp2.0", Version = SerilogVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Debug", TargetFramework = "netcoreapp2.0", Version = SerilogSinksDebugVersion },
new NuSpecDependency() { Id = "Serilog.Sinks.Trace", TargetFramework = "netcoreapp2.0", Version = SerilogSinksTraceVersion },
new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp2.0", Version = SpracheVersion },
new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp2.0", Version = SystemReactiveVersion },
new NuSpecDependency() { Id = "Avalonia.Remote.Protocol", TargetFramework = "netcoreapp2.0", Version = parameters.Version },
} }
.Deps(new string[]{null, "netcoreapp2.0"}, .Deps(new string[]{"netstandard2.0", "netcoreapp2.0", "net461"},
"System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", "Serilog", "Serilog.Sinks.Debug", "Serilog.Sinks.Trace",
"System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter", "System.Memory") "System.Memory", "System.Reactive", "System.ComponentModel.Annotations")
.ToArray(), .ToArray(),
Files = coreFiles, Files = coreFiles,
BasePath = context.Directory("./"), BasePath = context.Directory("./"),
@ -325,7 +303,7 @@ public class Packages
{ {
new NuSpecContent { Source = "Avalonia.Android.dll", Target = "lib/MonoAndroid10" } new NuSpecContent { Source = "Avalonia.Android.dll", Target = "lib/MonoAndroid10" }
}, },
BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix), BasePath = context.Directory("./src/Android/Avalonia.Android/bin/" + parameters.DirSuffix + "/monoandroid44/"),
OutputDirectory = parameters.NugetRoot OutputDirectory = parameters.NugetRoot
}, },
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -343,7 +321,7 @@ public class Packages
{ {
new NuSpecContent { Source = "Avalonia.iOS.dll", Target = "lib/Xamarin.iOS10" } new NuSpecContent { Source = "Avalonia.iOS.dll", Target = "lib/Xamarin.iOS10" }
}, },
BasePath = context.Directory("./src/iOS/Avalonia.iOS/bin/" + parameters.DirSuffixIOS), BasePath = context.Directory("./src/iOS/Avalonia.iOS/bin/" + parameters.DirSuffix + "/xamarin.ios10/"),
OutputDirectory = parameters.NugetRoot OutputDirectory = parameters.NugetRoot
} }
}; };
@ -495,7 +473,7 @@ public class Packages
}, },
Files = new [] Files = new []
{ {
new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/net461/Avalonia.Win32.Interop.dll", Target = "lib/net461" }
}, },
BasePath = context.Directory("./src/Windows"), BasePath = context.Directory("./src/Windows"),
OutputDirectory = parameters.NugetRoot OutputDirectory = parameters.NugetRoot
@ -506,7 +484,7 @@ public class Packages
NuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsCore);
NuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsDesktop);
if (!parameters.IsPlatformNetCoreOnly) { if (parameters.IsRunningOnWindows) {
NuspecNuGetSettings.Add(nuspecNuGetSettingInterop); NuspecNuGetSettings.Add(nuspecNuGetSettingInterop);
NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile); NuspecNuGetSettings.AddRange(nuspecNuGetSettingsMobile);
} }

31
parameters.cake

@ -1,18 +1,15 @@
using System.Xml.Linq;
using System.Linq;
public class Parameters public class Parameters
{ {
public string Platform { get; private set; }
public string Configuration { get; private set; } public string Configuration { get; private set; }
public bool SkipTests { get; private set; } public bool SkipTests { get; private set; }
public string MainRepo { get; private set; } public string MainRepo { get; private set; }
public string MasterBranch { get; private set; } public string MasterBranch { get; private set; }
public string AssemblyInfoPath { get; private set; }
public string ReleasePlatform { get; private set; } public string ReleasePlatform { get; private set; }
public string ReleaseConfiguration { get; private set; } public string ReleaseConfiguration { get; private set; }
public string MSBuildSolution { get; private set; } public string MSBuildSolution { get; private set; }
public bool IsPlatformAnyCPU { get; private set; }
public bool IsPlatformX86 { get; private set; }
public bool IsPlatformX64 { get; private set; }
public bool IsPlatformNetCoreOnly { get; private set; }
public bool IsLocalBuild { get; private set; } public bool IsLocalBuild { get; private set; }
public bool IsRunningOnUnix { get; private set; } public bool IsRunningOnUnix { get; private set; }
public bool IsRunningOnWindows { get; private set; } public bool IsRunningOnWindows { get; private set; }
@ -30,7 +27,6 @@ public class Parameters
public DirectoryPath ZipRoot { get; private set; } public DirectoryPath ZipRoot { get; private set; }
public DirectoryPath BinRoot { get; private set; } public DirectoryPath BinRoot { get; private set; }
public string DirSuffix { get; private set; } public string DirSuffix { get; private set; }
public string DirSuffixIOS { get; private set; }
public DirectoryPathCollection BuildDirs { get; private set; } public DirectoryPathCollection BuildDirs { get; private set; }
public string FileZipSuffix { get; private set; } public string FileZipSuffix { get; private set; }
public FilePath ZipCoreArtifacts { get; private set; } public FilePath ZipCoreArtifacts { get; private set; }
@ -43,23 +39,16 @@ public class Parameters
var buildSystem = context.BuildSystem(); var buildSystem = context.BuildSystem();
// ARGUMENTS // ARGUMENTS
Platform = context.Argument("platform", "Any CPU");
Configuration = context.Argument("configuration", "Release"); Configuration = context.Argument("configuration", "Release");
SkipTests = context.HasArgument("skip-tests"); SkipTests = context.HasArgument("skip-tests");
// CONFIGURATION // CONFIGURATION
MainRepo = "AvaloniaUI/Avalonia"; MainRepo = "AvaloniaUI/Avalonia";
MasterBranch = "master"; MasterBranch = "master";
AssemblyInfoPath = context.File("./src/Shared/SharedAssemblyInfo.cs");
ReleasePlatform = "Any CPU";
ReleaseConfiguration = "Release"; ReleaseConfiguration = "Release";
MSBuildSolution = "./Avalonia.sln"; MSBuildSolution = "./dirs.proj";
// PARAMETERS // PARAMETERS
IsPlatformAnyCPU = StringComparer.OrdinalIgnoreCase.Equals(Platform, "Any CPU");
IsPlatformX86 = StringComparer.OrdinalIgnoreCase.Equals(Platform, "x86");
IsPlatformX64 = StringComparer.OrdinalIgnoreCase.Equals(Platform, "x64");
IsPlatformNetCoreOnly = StringComparer.OrdinalIgnoreCase.Equals(Platform, "NetCoreOnly");
IsLocalBuild = buildSystem.IsLocalBuild; IsLocalBuild = buildSystem.IsLocalBuild;
IsRunningOnUnix = context.IsRunningOnUnix(); IsRunningOnUnix = context.IsRunningOnUnix();
IsRunningOnWindows = context.IsRunningOnWindows(); IsRunningOnWindows = context.IsRunningOnWindows();
@ -69,12 +58,11 @@ public class Parameters
IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, buildSystem.AppVeyor.Environment.Repository.Branch); IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, buildSystem.AppVeyor.Environment.Repository.Branch);
IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag
&& !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name); && !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name);
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
&& StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable; IsMyGetRelease = !IsTagged && IsReleasable;
// VERSION // VERSION
Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); Version = context.Argument("force-nuget-version", GetVersion());
if (IsRunningOnAppVeyor) if (IsRunningOnAppVeyor)
{ {
@ -105,11 +93,16 @@ public class Parameters
BinRoot = ArtifactsDir.Combine("bin"); BinRoot = ArtifactsDir.Combine("bin");
BuildDirs = context.GetDirectories("**/bin") + context.GetDirectories("**/obj"); BuildDirs = context.GetDirectories("**/bin") + context.GetDirectories("**/obj");
DirSuffix = Configuration; DirSuffix = Configuration;
DirSuffixIOS = "iPhone" + "/" + Configuration;
FileZipSuffix = Version + ".zip"; FileZipSuffix = Version + ".zip";
ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix); ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix);
ZipNuGetArtifacts = ZipRoot.CombineWithFilePath("Avalonia-NuGet-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot.CombineWithFilePath("Avalonia-NuGet-" + FileZipSuffix);
ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461"); ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461");
ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix); ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix);
} }
private static string GetVersion()
{
var xdoc = XDocument.Load("./build/SharedVersion.props");
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
}
} }

3
samples/BindingDemo/BindingDemo.csproj

@ -31,4 +31,5 @@
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" /> <Import Project="..\..\build\ReactiveUI.props" />
</Project> <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
</Project>

3
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -154,4 +154,5 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> <Import Project="..\..\build\AndroidWorkarounds.props" />
</Project>

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

@ -18,4 +18,5 @@
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
</Project> <Import Project="..\..\build\NetFX.props" />
</Project>

4
samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj

@ -174,6 +174,6 @@
<Name>ControlCatalog</Name> <Name>ControlCatalog</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Sprache.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> <Import Project="..\..\build\iOSWorkarounds.props" />
</Project>

3
samples/RenderDemo/RenderDemo.csproj

@ -31,4 +31,5 @@
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" /> <Import Project="..\..\build\ReactiveUI.props" />
</Project> <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
</Project>

3
samples/VirtualizationDemo/VirtualizationDemo.csproj

@ -31,4 +31,5 @@
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" /> <Import Project="..\..\build\ReactiveUI.props" />
</Project> <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />
</Project>

144
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,136 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="MSBuild.Sdk.Extras">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>monoandroid44</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.Android</RootNamespace>
<AssemblyName>Avalonia.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
<Reference Include="System.IO" />
<Reference Include="System.Reflection" />
<Reference Include="System.ObjectModel" />
<Reference Include="System.Threading.Tasks" />
<Reference Include="System.Runtime" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ActivityTracker.cs" />
<Compile Include="AndroidPlatform.cs" />
<Compile Include="AndroidThreadingInterface.cs" />
<Compile Include="AppBuilder.cs" />
<Compile Include="AvaloniaActivity.cs" />
<Compile Include="AvaloniaView.cs" />
<Compile Include="PlatformIconLoader.cs" />
<Compile Include="Platform\ClipboardImpl.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="Platform\Input\AndroidKeyboardDevice.cs" />
<Compile Include="Platform\Input\AndroidMouseDevice.cs" />
<Compile Include="Platform\SkiaPlatform\AndroidFramebuffer.cs" />
<Compile Include="Platform\SkiaPlatform\InvalidationAwareSurfaceView.cs" />
<Compile Include="Platform\SkiaPlatform\PopupImpl.cs" />
<Compile Include="Platform\SkiaPlatform\TopLevelImpl.cs" />
<Compile Include="Platform\Specific\IAndroidView.cs" />
<Compile Include="Platform\Specific\Helpers\AndroidTouchEventsHelper.cs" />
<Compile Include="Platform\Specific\Helpers\AndroidKeyboardEventsHelper.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeInfo.cs" />
<Compile Include="SystemDialogImpl.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Resources\AboutResources.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Values\Strings.xml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<Name>Avalonia.Markup.Xaml</Name> <ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
</ProjectReference> <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj"> <ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> <ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
<Name>Avalonia.Animation</Name> <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
</ProjectReference> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj"> <ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj">
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
<Name>Avalonia.ReactiveUI</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" /> <Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
</Project> <Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project>

30
src/Android/Avalonia.Android/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.Android")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

5
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -150,6 +150,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Serilog.props" /> <Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Sprache.props" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
</Project> <Import Project="..\..\..\build\System.Memory.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project>

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -8,4 +8,5 @@
<Import Project="..\..\build\Binding.props" /> <Import Project="..\..\build\Binding.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" /> <Import Project="..\..\build\JetBrains.Annotations.props" />
</Project> <Import Project="..\..\build\System.Memory.props" />
</Project>

49
src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs

@ -0,0 +1,49 @@
using System;
using System.Globalization;
namespace Avalonia.Data.Converters
{
/// <summary>
/// A value converter which calls <see cref="string.Format(string, object)"/>
/// </summary>
public class StringFormatValueConverter : IValueConverter
{
/// <summary>
/// Initializes a new instance of the <see cref="StringFormatValueConverter"/> class.
/// </summary>
/// <param name="format">The format string.</param>
/// <param name="inner">
/// An optional inner converter to be called before the format takes place.
/// </param>
public StringFormatValueConverter(string format, IValueConverter inner)
{
Contract.Requires<ArgumentNullException>(format != null);
Format = format;
Inner = inner;
}
/// <summary>
/// Gets an inner value converter which will be called before the string format takes place.
/// </summary>
public IValueConverter Inner { get; }
/// <summary>
/// Gets the format string.
/// </summary>
public string Format { get; }
/// <inheritdoc/>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
value = Inner?.Convert(value, targetType, parameter, culture) ?? value;
return string.Format(culture, Format, value);
}
/// <inheritdoc/>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Two way bindings are not supported with a string format");
}
}
}

1
src/Avalonia.Base/Data/Core/ExpressionParseException.cs

@ -17,6 +17,7 @@ namespace Avalonia.Data.Core
/// </summary> /// </summary>
/// <param name="column">The column position of the error.</param> /// <param name="column">The column position of the error.</param>
/// <param name="message">The exception message.</param> /// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the parsing failure.</param>
public ExpressionParseException(int column, string message, Exception innerException = null) public ExpressionParseException(int column, string message, Exception innerException = null)
: base(message, innerException) : base(message, innerException)
{ {

13
src/Avalonia.Base/Threading/Dispatcher.cs

@ -69,7 +69,7 @@ namespace Avalonia.Threading
/// </summary> /// </summary>
public void RunJobs() public void RunJobs()
{ {
_jobRunner?.RunJobs(null); _jobRunner.RunJobs(null);
} }
/// <summary> /// <summary>
@ -82,14 +82,21 @@ namespace Avalonia.Threading
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{ {
Contract.Requires<ArgumentNullException>(action != null); Contract.Requires<ArgumentNullException>(action != null);
return _jobRunner?.InvokeAsync(action, priority); return _jobRunner.InvokeAsync(action, priority);
}
/// <inheritdoc/>
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority = DispatcherPriority.Normal)
{
Contract.Requires<ArgumentNullException>(function != null);
return _jobRunner.InvokeAsync(function, priority);
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{ {
Contract.Requires<ArgumentNullException>(action != null); Contract.Requires<ArgumentNullException>(action != null);
_jobRunner?.Post(action, priority); _jobRunner.Post(action, priority);
} }
/// <summary> /// <summary>

11
src/Avalonia.Base/Threading/IDispatcher.cs

@ -28,12 +28,17 @@ namespace Avalonia.Threading
void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal); void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
/// <summary> /// <summary>
/// Post action that will be invoked on main thread /// Posts an action that will be invoked on the dispatcher thread.
/// </summary> /// </summary>
/// <param name="action">The method.</param> /// <param name="action">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param> /// <param name="priority">The priority with which to invoke the method.</param>
// TODO: The naming of this method is confusing: the Async suffix usually means return a task.
// Remove this and rename InvokeTaskAsync as InvokeAsync. See #816.
Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal); Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
/// <summary>
/// Posts a function that will be invoked on the dispatcher thread.
/// </summary>
/// <param name="function">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority = DispatcherPriority.Normal);
} }
} }

168
src/Avalonia.Base/Threading/JobRunner.cs

@ -14,32 +14,16 @@ namespace Avalonia.Threading
/// </summary> /// </summary>
internal class JobRunner internal class JobRunner
{ {
private IPlatformThreadingInterface _platform; private IPlatformThreadingInterface _platform;
private Queue<Job>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1) private readonly Queue<IJob>[] _queues = Enumerable.Range(0, (int) DispatcherPriority.MaxValue + 1)
.Select(_ => new Queue<Job>()).ToArray(); .Select(_ => new Queue<IJob>()).ToArray();
public JobRunner(IPlatformThreadingInterface platform) public JobRunner(IPlatformThreadingInterface platform)
{ {
_platform = platform; _platform = platform;
} }
Job GetNextJob(DispatcherPriority minimumPriority)
{
for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--)
{
var q = _queues[c];
lock (q)
{
if (q.Count > 0)
return q.Dequeue();
}
}
return null;
}
/// <summary> /// <summary>
/// Runs continuations pushed on the loop. /// Runs continuations pushed on the loop.
/// </summary> /// </summary>
@ -52,24 +36,8 @@ namespace Avalonia.Threading
var job = GetNextJob(minimumPriority); var job = GetNextJob(minimumPriority);
if (job == null) if (job == null)
return; return;
if (job.TaskCompletionSource == null) job.Run();
{
job.Action();
}
else
{
try
{
job.Action();
job.TaskCompletionSource.SetResult(null);
}
catch (Exception e)
{
job.TaskCompletionSource.SetException(e);
}
}
} }
} }
@ -83,7 +51,20 @@ namespace Avalonia.Threading
{ {
var job = new Job(action, priority, false); var job = new Job(action, priority, false);
AddJob(job); AddJob(job);
return job.TaskCompletionSource.Task; return job.Task;
}
/// <summary>
/// Invokes a method on the main loop.
/// </summary>
/// <param name="function">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
/// <returns>A task that can be used to track the method's execution.</returns>
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority)
{
var job = new Job<TResult>(function, priority);
AddJob(job);
return job.Task;
} }
/// <summary> /// <summary>
@ -105,9 +86,9 @@ namespace Avalonia.Threading
_platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>(); _platform = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
} }
private void AddJob(Job job) private void AddJob(IJob job)
{ {
var needWake = false; bool needWake;
var queue = _queues[(int) job.Priority]; var queue = _queues[(int) job.Priority];
lock (queue) lock (queue)
{ {
@ -118,38 +99,129 @@ namespace Avalonia.Threading
_platform?.Signal(job.Priority); _platform?.Signal(job.Priority);
} }
private IJob GetNextJob(DispatcherPriority minimumPriority)
{
for (int c = (int) DispatcherPriority.MaxValue; c >= (int) minimumPriority; c--)
{
var q = _queues[c];
lock (q)
{
if (q.Count > 0)
return q.Dequeue();
}
}
return null;
}
private interface IJob
{
/// <summary>
/// Gets the job priority.
/// </summary>
DispatcherPriority Priority { get; }
/// <summary>
/// Runs the job.
/// </summary>
void Run();
}
/// <summary> /// <summary>
/// A job to run. /// A job to run.
/// </summary> /// </summary>
private class Job private sealed class Job : IJob
{ {
/// <summary>
/// The method to call.
/// </summary>
private readonly Action _action;
/// <summary>
/// The task completion source.
/// </summary>
private readonly TaskCompletionSource<object> _taskCompletionSource;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Job"/> class. /// Initializes a new instance of the <see cref="Job"/> class.
/// </summary> /// </summary>
/// <param name="action">The method to call.</param> /// <param name="action">The method to call.</param>
/// <param name="priority">The job priority.</param> /// <param name="priority">The job priority.</param>
/// <param name="throwOnUiThread">Do not wrap excepption in TaskCompletionSource</param> /// <param name="throwOnUiThread">Do not wrap exception in TaskCompletionSource</param>
public Job(Action action, DispatcherPriority priority, bool throwOnUiThread) public Job(Action action, DispatcherPriority priority, bool throwOnUiThread)
{ {
Action = action; _action = action;
Priority = priority; Priority = priority;
TaskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>(); _taskCompletionSource = throwOnUiThread ? null : new TaskCompletionSource<object>();
} }
/// <inheritdoc/>
public DispatcherPriority Priority { get; }
/// <summary> /// <summary>
/// Gets the method to call. /// The task.
/// </summary> /// </summary>
public Action Action { get; } public Task Task => _taskCompletionSource?.Task;
/// <inheritdoc/>
void IJob.Run()
{
if (_taskCompletionSource == null)
{
_action();
return;
}
try
{
_action();
_taskCompletionSource.SetResult(null);
}
catch (Exception e)
{
_taskCompletionSource.SetException(e);
}
}
}
/// <summary>
/// A job to run.
/// </summary>
private sealed class Job<TResult> : IJob
{
private readonly Func<TResult> _function;
private readonly TaskCompletionSource<TResult> _taskCompletionSource;
/// <summary> /// <summary>
/// Gets the job priority. /// Initializes a new instance of the <see cref="Job"/> class.
/// </summary> /// </summary>
public DispatcherPriority Priority { get; } /// <param name="function">The method to call.</param>
/// <param name="priority">The job priority.</param>
public Job(Func<TResult> function, DispatcherPriority priority)
{
_function = function;
Priority = priority;
_taskCompletionSource = new TaskCompletionSource<TResult>();
}
/// <inheritdoc/>
public DispatcherPriority Priority { get; }
/// <summary> /// <summary>
/// Gets the task completion source. /// The task.
/// </summary> /// </summary>
public TaskCompletionSource<object> TaskCompletionSource { get; } public Task<TResult> Task => _taskCompletionSource.Task;
/// <inheritdoc/>
void IJob.Run()
{
try
{
var result = _function();
_taskCompletionSource.SetResult(result);
}
catch (Exception e)
{
_taskCompletionSource.SetException(e);
}
}
} }
} }
} }

65
src/Avalonia.Base/Utilities/CharacterReader.cs

@ -2,30 +2,37 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Globalization;
using System.Text;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public class CharacterReader public ref struct CharacterReader
{ {
private readonly string _s; private ReadOnlySpan<char> _s;
private int _i;
public CharacterReader(string s) public CharacterReader(ReadOnlySpan<char> s)
:this()
{ {
_s = s; _s = s;
} }
public bool End => _i == _s.Length; public bool End => _s.IsEmpty;
public char Peek => _s[_i]; public char Peek => _s[0];
public int Position => _i; public int Position { get; private set; }
public char Take() => _s[_i++]; public char Take()
{
Position++;
char taken = _s[0];
_s = _s.Slice(1);
return taken;
}
public void SkipWhitespace() public void SkipWhitespace()
{ {
while (!End && char.IsWhiteSpace(Peek)) var trimmed = _s.TrimStart();
{ Position += _s.Length - trimmed.Length;
Take(); _s = trimmed;
}
} }
public bool TakeIf(char c) public bool TakeIf(char c)
@ -40,5 +47,39 @@ namespace Avalonia.Utilities
return false; return false;
} }
} }
public bool TakeIf(Func<char, bool> condition)
{
if (condition(Peek))
{
Take();
return true;
}
return false;
}
public ReadOnlySpan<char> TakeUntil(char c)
{
int len;
for (len = 0; len < _s.Length && _s[len] != c; len++)
{
}
var span = _s.Slice(0, len);
_s = _s.Slice(len);
Position += len;
return span;
}
public ReadOnlySpan<char> TakeWhile(Func<char, bool> condition)
{
int len;
for (len = 0; len < _s.Length && condition(_s[len]); len++)
{
}
var span = _s.Slice(0, len);
_s = _s.Slice(len);
Position += len;
return span;
}
} }
} }

17
src/Avalonia.Base/Utilities/IdentifierParser.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
@ -8,22 +10,15 @@ namespace Avalonia.Utilities
{ {
public static class IdentifierParser public static class IdentifierParser
{ {
public static string Parse(CharacterReader r) public static ReadOnlySpan<char> ParseIdentifier(this ref CharacterReader r)
{ {
if (IsValidIdentifierStart(r.Peek)) if (IsValidIdentifierStart(r.Peek))
{ {
var result = new StringBuilder(); return r.TakeWhile(IsValidIdentifierChar);
while (!r.End && IsValidIdentifierChar(r.Peek))
{
result.Append(r.Take());
}
return result.ToString();
} }
else else
{ {
return null; return ReadOnlySpan<char>.Empty;
} }
} }

1
src/Avalonia.Controls/Window.cs

@ -49,6 +49,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope
{ {
/// <summary>
/// Defines the <see cref="SizeToContent"/> property. /// Defines the <see cref="SizeToContent"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<SizeToContent> SizeToContentProperty = public static readonly StyledProperty<SizeToContent> SizeToContentProperty =

26
src/Avalonia.Input/InputElement.cs

@ -162,8 +162,8 @@ namespace Avalonia.Input
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown); KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown);
KeyUpEvent.AddClassHandler<InputElement>(x => x.OnKeyUp); KeyUpEvent.AddClassHandler<InputElement>(x => x.OnKeyUp);
TextInputEvent.AddClassHandler<InputElement>(x => x.OnTextInput); TextInputEvent.AddClassHandler<InputElement>(x => x.OnTextInput);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter); PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnterCore);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave); PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeaveCore);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved); PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved);
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed); PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased); PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
@ -445,7 +445,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnPointerEnter(PointerEventArgs e) protected virtual void OnPointerEnter(PointerEventArgs e)
{ {
IsPointerOver = true;
} }
/// <summary> /// <summary>
@ -454,7 +453,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnPointerLeave(PointerEventArgs e) protected virtual void OnPointerLeave(PointerEventArgs e)
{ {
IsPointerOver = false;
} }
/// <summary> /// <summary>
@ -494,6 +492,26 @@ namespace Avalonia.Input
((InputElement)e.Sender).UpdateIsEnabledCore(); ((InputElement)e.Sender).UpdateIsEnabledCore();
} }
/// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerEnterCore(PointerEventArgs e)
{
IsPointerOver = true;
OnPointerEnter(e);
}
/// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerLeaveCore(PointerEventArgs e)
{
IsPointerOver = false;
OnPointerLeave(e);
}
/// <summary> /// <summary>
/// Updates the <see cref="IsEnabledCore"/> property value. /// Updates the <see cref="IsEnabledCore"/> property value.
/// </summary> /// </summary>

24
src/Avalonia.Input/MouseDevice.cs

@ -346,12 +346,7 @@ namespace Avalonia.Input
IInputElement branch = null; IInputElement branch = null;
var e = new PointerEventArgs var e = new PointerEventArgs { Device = device, };
{
RoutedEvent = InputElement.PointerEnterEvent,
Device = device,
};
var el = element; var el = element;
while (el != null) while (el != null)
@ -361,9 +356,6 @@ namespace Avalonia.Input
branch = el; branch = el;
break; break;
} }
e.Source = el;
el.RaiseEvent(e);
el = (IInputElement)el.VisualParent; el = (IInputElement)el.VisualParent;
} }
@ -373,11 +365,21 @@ namespace Avalonia.Input
while (el != null && el != branch) while (el != null && el != branch)
{ {
e.Source = el; e.Source = el;
e.Handled = false;
el.RaiseEvent(e); el.RaiseEvent(e);
el = (IInputElement)el.VisualParent; el = (IInputElement)el.VisualParent;
} }
root.PointerOverElement = element; el = root.PointerOverElement = element;
e.RoutedEvent = InputElement.PointerEnterEvent;
while (el != null && el != branch)
{
e.Source = el;
e.Handled = false;
el.RaiseEvent(e);
el = (IInputElement)el.VisualParent;
}
} }
} }
} }

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -77,6 +77,5 @@
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" /> <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" /> <ProjectReference Include="..\Avalonia.Markup\Avalonia.Markup.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Markup.props" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
</Project> </Project>

3
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -26,8 +26,7 @@ namespace Avalonia.Markup.Xaml.Converters
{ {
var registry = AvaloniaPropertyRegistry.Instance; var registry = AvaloniaPropertyRegistry.Instance;
var parser = new PropertyParser(); var parser = new PropertyParser();
var reader = new CharacterReader((string)value); var (ns, owner, propertyName) = parser.Parse(new CharacterReader(((string)value).AsSpan()));
var (ns, owner, propertyName) = parser.Parse(reader);
var ownerType = TryResolveOwnerByName(context, ns, owner); var ownerType = TryResolveOwnerByName(context, ns, owner);
var targetType = context.GetFirstAmbientValue<ControlTemplate>()?.TargetType ?? var targetType = context.GetFirstAmbientValue<ControlTemplate>()?.TargetType ??
context.GetFirstAmbientValue<Style>()?.Selector?.TargetType ?? context.GetFirstAmbientValue<Style>()?.Selector?.TargetType ??

5
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -43,8 +43,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Path = Path, Path = Path,
Priority = Priority, Priority = Priority,
Source = Source, Source = Source,
StringFormat = StringFormat,
RelativeSource = RelativeSource, RelativeSource = RelativeSource,
DefaultAnchor = new WeakReference(GetDefaultAnchor((ITypeDescriptorContext)serviceProvider)) DefaultAnchor = new WeakReference(GetDefaultAnchor(descriptorContext))
}; };
} }
@ -79,6 +80,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public object Source { get; set; } public object Source { get; set; }
public string StringFormat { get; set; }
public RelativeSource RelativeSource { get; set; } public RelativeSource RelativeSource { get; set; }
} }
} }

10
src/Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs

@ -22,9 +22,9 @@ namespace Avalonia.Markup.Xaml.Parsers
do do
{ {
var token = IdentifierParser.Parse(r); var token = r.ParseIdentifier();
if (token == null) if (token.IsEmpty)
{ {
if (r.End) if (r.End)
{ {
@ -47,18 +47,18 @@ namespace Avalonia.Markup.Xaml.Parsers
else if (!r.End && r.TakeIf(':')) else if (!r.End && r.TakeIf(':'))
{ {
ns = ns == null ? ns = ns == null ?
token : token.ToString() :
throw new ExpressionParseException(r.Position, "Unexpected ':'."); throw new ExpressionParseException(r.Position, "Unexpected ':'.");
} }
else if (!r.End && r.TakeIf('.')) else if (!r.End && r.TakeIf('.'))
{ {
owner = owner == null ? owner = owner == null ?
token : token.ToString() :
throw new ExpressionParseException(r.Position, "Unexpected '.'."); throw new ExpressionParseException(r.Position, "Unexpected '.'.");
} }
else else
{ {
name = token; name = token.ToString();
} }
} while (!r.End); } while (!r.End);

4
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@ -9,5 +9,5 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" /> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\Sprache.props" /> <Import Project="..\..\..\build\System.Memory.props" />
</Project> </Project>

21
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -84,6 +84,11 @@ namespace Avalonia.Data
/// </summary> /// </summary>
public object Source { get; set; } public object Source { get; set; }
/// <summary>
/// Gets or sets the string format.
/// </summary>
public string StringFormat { get; set; }
public WeakReference DefaultAnchor { get; set; } public WeakReference DefaultAnchor { get; set; }
/// <summary> /// <summary>
@ -181,11 +186,23 @@ namespace Avalonia.Data
fallback = null; fallback = null;
} }
var converter = Converter;
var targetType = targetProperty?.PropertyType ?? typeof(object);
// We only respect `StringFormat` if the type of the property we're assigning to will
// accept a string. Note that this is slightly different to WPF in that WPF only applies
// `StringFormat` for target type `string` (not `object`).
if (!string.IsNullOrWhiteSpace(StringFormat) &&
(targetType == typeof(string) || targetType == typeof(object)))
{
converter = new StringFormatValueConverter(StringFormat, converter);
}
var subject = new BindingExpression( var subject = new BindingExpression(
observer, observer,
targetProperty?.PropertyType ?? typeof(object), targetType,
fallback, fallback,
Converter ?? DefaultValueConverter.Instance, converter ?? DefaultValueConverter.Instance,
ConverterParameter, ConverterParameter,
Priority); Priority);

13
src/Markup/Avalonia.Markup/Markup/Parsers/ArgumentListParser.cs

@ -11,7 +11,7 @@ namespace Avalonia.Markup.Parsers
{ {
internal static class ArgumentListParser internal static class ArgumentListParser
{ {
public static IList<string> Parse(CharacterReader r, char open, char close, char delimiter = ',') public static IList<string> ParseArguments(this ref CharacterReader r, char open, char close, char delimiter = ',')
{ {
if (r.Peek == open) if (r.Peek == open)
{ {
@ -21,16 +21,13 @@ namespace Avalonia.Markup.Parsers
while (!r.End) while (!r.End)
{ {
var builder = new StringBuilder(); var argument = r.TakeWhile(c => c != delimiter && c != close && !char.IsWhiteSpace(c));
while (!r.End && r.Peek != delimiter && r.Peek != close && !char.IsWhiteSpace(r.Peek)) if (argument.IsEmpty)
{
builder.Append(r.Take());
}
if (builder.Length == 0)
{ {
throw new ExpressionParseException(r.Position, "Expected indexer argument."); throw new ExpressionParseException(r.Position, "Expected indexer argument.");
} }
result.Add(builder.ToString());
result.Add(argument.ToString());
r.SkipWhitespace(); r.SkipWhitespace();

6
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionObserverBuilder.cs

@ -15,10 +15,10 @@ namespace Avalonia.Markup.Parsers
{ {
return (new EmptyExpressionNode(), default); return (new EmptyExpressionNode(), default);
} }
var reader = new CharacterReader(expression); var reader = new CharacterReader(expression.AsSpan());
var parser = new ExpressionParser(enableValidation, typeResolver); var parser = new ExpressionParser(enableValidation, typeResolver);
var node = parser.Parse(reader); var node = parser.Parse(ref reader);
if (!reader.End) if (!reader.End)
{ {

140
src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs

@ -27,7 +27,7 @@ namespace Avalonia.Markup.Parsers
_enableValidation = enableValidation; _enableValidation = enableValidation;
} }
public (ExpressionNode Node, SourceMode Mode) Parse(CharacterReader r) public (ExpressionNode Node, SourceMode Mode) Parse(ref CharacterReader r)
{ {
var nodes = new List<ExpressionNode>(); var nodes = new List<ExpressionNode>();
var state = State.Start; var state = State.Start;
@ -38,32 +38,32 @@ namespace Avalonia.Markup.Parsers
switch (state) switch (state)
{ {
case State.Start: case State.Start:
state = ParseStart(r, nodes); state = ParseStart(ref r, nodes);
break; break;
case State.AfterMember: case State.AfterMember:
state = ParseAfterMember(r, nodes); state = ParseAfterMember(ref r, nodes);
break; break;
case State.BeforeMember: case State.BeforeMember:
state = ParseBeforeMember(r, nodes); state = ParseBeforeMember(ref r, nodes);
break; break;
case State.AttachedProperty: case State.AttachedProperty:
state = ParseAttachedProperty(r, nodes); state = ParseAttachedProperty(ref r, nodes);
break; break;
case State.Indexer: case State.Indexer:
state = ParseIndexer(r, nodes); state = ParseIndexer(ref r, nodes);
break; break;
case State.ElementName: case State.ElementName:
state = ParseElementName(r, nodes); state = ParseElementName(ref r, nodes);
mode = SourceMode.Control; mode = SourceMode.Control;
break; break;
case State.RelativeSource: case State.RelativeSource:
state = ParseRelativeSource(r, nodes); state = ParseRelativeSource(ref r, nodes);
mode = SourceMode.Control; mode = SourceMode.Control;
break; break;
} }
@ -82,36 +82,37 @@ namespace Avalonia.Markup.Parsers
return (nodes.FirstOrDefault(), mode); return (nodes.FirstOrDefault(), mode);
} }
private State ParseStart(CharacterReader r, IList<ExpressionNode> nodes) private State ParseStart(ref CharacterReader r, IList<ExpressionNode> nodes)
{ {
if (ParseNot(r)) if (ParseNot(ref r))
{ {
nodes.Add(new LogicalNotNode()); nodes.Add(new LogicalNotNode());
return State.Start; return State.Start;
} }
else if (ParseSharp(r))
else if (ParseSharp(ref r))
{ {
return State.ElementName; return State.ElementName;
} }
else if (ParseDollarSign(r)) else if (ParseDollarSign(ref r))
{ {
return State.RelativeSource; return State.RelativeSource;
} }
else if (ParseOpenBrace(r)) else if (ParseOpenBrace(ref r))
{ {
return State.AttachedProperty; return State.AttachedProperty;
} }
else if (PeekOpenBracket(r)) else if (PeekOpenBracket(ref r))
{ {
return State.Indexer; return State.Indexer;
} }
else else
{ {
var identifier = IdentifierParser.Parse(r); var identifier = r.ParseIdentifier();
if (identifier != null) if (!identifier.IsEmpty)
{ {
nodes.Add(new PropertyAccessorNode(identifier, _enableValidation)); nodes.Add(new PropertyAccessorNode(identifier.ToString(), _enableValidation));
return State.AfterMember; return State.AfterMember;
} }
} }
@ -119,18 +120,18 @@ namespace Avalonia.Markup.Parsers
return State.End; return State.End;
} }
private static State ParseAfterMember(CharacterReader r, IList<ExpressionNode> nodes) private static State ParseAfterMember(ref CharacterReader r, IList<ExpressionNode> nodes)
{ {
if (ParseMemberAccessor(r)) if (ParseMemberAccessor(ref r))
{ {
return State.BeforeMember; return State.BeforeMember;
} }
else if (ParseStreamOperator(r)) else if (ParseStreamOperator(ref r))
{ {
nodes.Add(new StreamNode()); nodes.Add(new StreamNode());
return State.AfterMember; return State.AfterMember;
} }
else if (PeekOpenBracket(r)) else if (PeekOpenBracket(ref r))
{ {
return State.Indexer; return State.Indexer;
} }
@ -138,19 +139,19 @@ namespace Avalonia.Markup.Parsers
return State.End; return State.End;
} }
private State ParseBeforeMember(CharacterReader r, IList<ExpressionNode> nodes) private State ParseBeforeMember(ref CharacterReader r, IList<ExpressionNode> nodes)
{ {
if (ParseOpenBrace(r)) if (ParseOpenBrace(ref r))
{ {
return State.AttachedProperty; return State.AttachedProperty;
} }
else else
{ {
var identifier = IdentifierParser.Parse(r); var identifier = r.ParseIdentifier();
if (identifier != null) if (!identifier.IsEmpty)
{ {
nodes.Add(new PropertyAccessorNode(identifier, _enableValidation)); nodes.Add(new PropertyAccessorNode(identifier.ToString(), _enableValidation));
return State.AfterMember; return State.AfterMember;
} }
@ -158,16 +159,16 @@ namespace Avalonia.Markup.Parsers
} }
} }
private State ParseAttachedProperty(CharacterReader r, List<ExpressionNode> nodes) private State ParseAttachedProperty(ref CharacterReader r, List<ExpressionNode> nodes)
{ {
var (ns, owner) = ParseTypeName(r); var (ns, owner) = ParseTypeName(ref r);
if (r.End || !r.TakeIf('.')) if (r.End || !r.TakeIf('.'))
{ {
throw new ExpressionParseException(r.Position, "Invalid attached property name."); throw new ExpressionParseException(r.Position, "Invalid attached property name.");
} }
var name = IdentifierParser.Parse(r); var name = r.ParseIdentifier();
if (r.End || !r.TakeIf(')')) if (r.End || !r.TakeIf(')'))
{ {
@ -179,15 +180,15 @@ namespace Avalonia.Markup.Parsers
throw new InvalidOperationException("Cannot parse a binding path with an attached property without a type resolver. Maybe you can use a LINQ Expression binding path instead?"); throw new InvalidOperationException("Cannot parse a binding path with an attached property without a type resolver. Maybe you can use a LINQ Expression binding path instead?");
} }
var property = AvaloniaPropertyRegistry.Instance.FindRegistered(_typeResolver(ns, owner), name); var property = AvaloniaPropertyRegistry.Instance.FindRegistered(_typeResolver(ns.ToString(), owner.ToString()), name.ToString());
nodes.Add(new AvaloniaPropertyAccessorNode(property, _enableValidation)); nodes.Add(new AvaloniaPropertyAccessorNode(property, _enableValidation));
return State.AfterMember; return State.AfterMember;
} }
private State ParseIndexer(CharacterReader r, List<ExpressionNode> nodes) private State ParseIndexer(ref CharacterReader r, List<ExpressionNode> nodes)
{ {
var args = ArgumentListParser.Parse(r, '[', ']'); var args = r.ParseArguments('[', ']');
if (args.Count == 0) if (args.Count == 0)
{ {
@ -197,36 +198,35 @@ namespace Avalonia.Markup.Parsers
nodes.Add(new StringIndexerNode(args)); nodes.Add(new StringIndexerNode(args));
return State.AfterMember; return State.AfterMember;
} }
private State ParseElementName(CharacterReader r, List<ExpressionNode> nodes) private State ParseElementName(ref CharacterReader r, List<ExpressionNode> nodes)
{ {
var name = IdentifierParser.Parse(r); var name = r.ParseIdentifier();
if (name == null) if (name == null)
{ {
throw new ExpressionParseException(r.Position, "Element name expected after '#'."); throw new ExpressionParseException(r.Position, "Element name expected after '#'.");
} }
nodes.Add(new ElementNameNode(name)); nodes.Add(new ElementNameNode(name.ToString()));
return State.AfterMember; return State.AfterMember;
} }
private State ParseRelativeSource(ref CharacterReader r, List<ExpressionNode> nodes)
private State ParseRelativeSource(CharacterReader r, List<ExpressionNode> nodes)
{ {
var mode = IdentifierParser.Parse(r); var mode = r.ParseIdentifier();
if (mode == "self") if (mode.Equals("self".AsSpan(), StringComparison.InvariantCulture))
{ {
nodes.Add(new SelfNode()); nodes.Add(new SelfNode());
} }
else if (mode == "parent") else if (mode.Equals("parent".AsSpan(), StringComparison.InvariantCulture))
{ {
Type ancestorType = null; Type ancestorType = null;
var ancestorLevel = 0; var ancestorLevel = 0;
if (PeekOpenBracket(r)) if (PeekOpenBracket(ref r))
{ {
var args = ArgumentListParser.Parse(r, '[', ']', ';'); var args = r.ParseArguments('[', ']', ';');
if (args.Count > 2 || args.Count == 0) if (args.Count > 2 || args.Count == 0)
{ {
throw new ExpressionParseException(r.Position, "Too many arguments in RelativeSource syntax sugar"); throw new ExpressionParseException(r.Position, "Too many arguments in RelativeSource syntax sugar");
@ -240,14 +240,16 @@ namespace Avalonia.Markup.Parsers
} }
else else
{ {
var typeName = ParseTypeName(new CharacterReader(args[0])); var reader = new CharacterReader(args[0].AsSpan());
ancestorType = _typeResolver(typeName.ns, typeName.typeName); var typeName = ParseTypeName(ref reader);
ancestorType = _typeResolver(typeName.Namespace.ToString(), typeName.Type.ToString());
} }
} }
else else
{ {
var typeName = ParseTypeName(new CharacterReader(args[0])); var reader = new CharacterReader(args[0].AsSpan());
ancestorType = _typeResolver(typeName.ns, typeName.typeName); var typeName = ParseTypeName(ref reader);
ancestorType = _typeResolver(typeName.Namespace.ToString(), typeName.Type.ToString());
ancestorLevel = int.Parse(args[1]); ancestorLevel = int.Parse(args[1]);
} }
} }
@ -261,56 +263,56 @@ namespace Avalonia.Markup.Parsers
return State.AfterMember; return State.AfterMember;
} }
private static (string ns, string typeName) ParseTypeName(CharacterReader r) private static TypeName ParseTypeName(ref CharacterReader r)
{ {
string ns, typeName; ReadOnlySpan<char> ns, typeName;
ns = string.Empty; ns = ReadOnlySpan<char>.Empty;
var typeNameOrNamespace = IdentifierParser.Parse(r); var typeNameOrNamespace = r.ParseIdentifier();
if (!r.End && r.TakeIf(':')) if (!r.End && r.TakeIf(':'))
{ {
ns = typeNameOrNamespace; ns = typeNameOrNamespace;
typeName = IdentifierParser.Parse(r); typeName = r.ParseIdentifier();
} }
else else
{ {
typeName = typeNameOrNamespace; typeName = typeNameOrNamespace;
} }
return (ns, typeName); return new TypeName(ns, typeName);
} }
private static bool ParseNot(CharacterReader r) private static bool ParseNot(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('!'); return !r.End && r.TakeIf('!');
} }
private static bool ParseMemberAccessor(CharacterReader r) private static bool ParseMemberAccessor(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('.'); return !r.End && r.TakeIf('.');
} }
private static bool ParseOpenBrace(CharacterReader r) private static bool ParseOpenBrace(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('('); return !r.End && r.TakeIf('(');
} }
private static bool PeekOpenBracket(CharacterReader r) private static bool PeekOpenBracket(ref CharacterReader r)
{ {
return !r.End && r.Peek == '['; return !r.End && r.Peek == '[';
} }
private static bool ParseStreamOperator(CharacterReader r) private static bool ParseStreamOperator(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('^'); return !r.End && r.TakeIf('^');
} }
private static bool ParseDollarSign(CharacterReader r) private static bool ParseDollarSign(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('$'); return !r.End && r.TakeIf('$');
} }
private static bool ParseSharp(CharacterReader r) private static bool ParseSharp(ref CharacterReader r)
{ {
return !r.End && r.TakeIf('#'); return !r.End && r.TakeIf('#');
} }
@ -326,5 +328,23 @@ namespace Avalonia.Markup.Parsers
Indexer, Indexer,
End, End,
} }
private readonly ref struct TypeName
{
public TypeName(ReadOnlySpan<char> ns, ReadOnlySpan<char> typeName)
{
Namespace = ns;
Type = typeName;
}
public readonly ReadOnlySpan<char> Namespace;
public readonly ReadOnlySpan<char> Type;
public void Deconstruct(out ReadOnlySpan<char> ns, out ReadOnlySpan<char> typeName)
{
ns = Namespace;
typeName = Type;
}
}
} }
} }

395
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs

@ -1,9 +1,11 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using Sprache; using Avalonia.Data.Core;
using Avalonia.Utilities;
// Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the // Don't need to override GetHashCode as the ISyntax objects will not be stored in a hash; the
// only reason they have overridden Equals methods is for unit testing. // only reason they have overridden Equals methods is for unit testing.
@ -11,123 +13,292 @@ using Sprache;
namespace Avalonia.Markup.Parsers namespace Avalonia.Markup.Parsers
{ {
internal class SelectorGrammar internal static class SelectorGrammar
{ {
public static readonly Parser<char> CombiningCharacter = Parse.Char( private enum State
c => {
{ Start,
var cat = CharUnicodeInfo.GetUnicodeCategory(c); Middle,
return cat == UnicodeCategory.NonSpacingMark || Colon,
cat == UnicodeCategory.SpacingCombiningMark; Class,
}, Name,
"Connecting Character"); CanHaveType,
Traversal,
public static readonly Parser<char> ConnectingCharacter = Parse.Char( TypeName,
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation, Property,
"Connecting Character"); Template,
End,
public static readonly Parser<char> FormattingCharacter = Parse.Char( }
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Format,
"Connecting Character"); public static IEnumerable<ISyntax> Parse(string s)
{
public static readonly Parser<char> IdentifierStart = Parse.Letter.Or(Parse.Char('_')); var r = new CharacterReader(s.AsSpan());
var state = State.Start;
public static readonly Parser<char> IdentifierChar = Parse var selector = new List<ISyntax>();
.LetterOrDigit while (!r.End && state != State.End)
.Or(ConnectingCharacter) {
.Or(CombiningCharacter) ISyntax syntax = null;
.Or(FormattingCharacter); switch (state)
{
public static readonly Parser<string> Identifier = case State.Start:
from start in IdentifierStart.Once().Text() state = ParseStart(ref r);
from @char in IdentifierChar.Many().Text() break;
select start + @char; case State.Middle:
state = ParseMiddle(ref r);
public static readonly Parser<string> Namespace = break;
from ns in Parse.Letter.Many().Text() case State.CanHaveType:
from bar in Parse.Char('|') state = ParseCanHaveType(ref r);
select ns; break;
case State.Colon:
public static readonly Parser<OfTypeSyntax> OfType = (state, syntax) = ParseColon(ref r);
from ns in Namespace.Optional() break;
from identifier in Identifier case State.Class:
select new OfTypeSyntax (state, syntax) = ParseClass(ref r);
{ break;
TypeName = identifier, case State.Traversal:
Xmlns = ns.GetOrDefault(), (state, syntax) = ParseTraversal(ref r);
}; break;
case State.TypeName:
public static readonly Parser<NameSyntax> Name = (state, syntax) = ParseTypeName(ref r);
from hash in Parse.Char('#') break;
from identifier in Identifier case State.Property:
select new NameSyntax { Name = identifier }; (state, syntax) = ParseProperty(ref r);
break;
public static readonly Parser<char> ClassStart = Parse.Char('_').Or(Parse.Letter); case State.Template:
(state, syntax) = ParseTemplate(ref r);
public static readonly Parser<char> ClassChar = ClassStart.Or(Parse.Numeric); break;
case State.Name:
public static readonly Parser<string> ClassIdentifier = (state, syntax) = ParseName(ref r);
from start in ClassStart.Once().Text() break;
from @char in ClassChar.Many().Text() }
select start + @char; if (syntax != null)
{
public static readonly Parser<ClassSyntax> StandardClass = selector.Add(syntax);
from dot in Parse.Char('.').Once() }
from identifier in ClassIdentifier }
select new ClassSyntax { Class = identifier };
if (state != State.Start && state != State.Middle && state != State.End && state != State.CanHaveType)
public static readonly Parser<ClassSyntax> Pseduoclass = {
from colon in Parse.Char(':').Once() throw new ExpressionParseException(r.Position, "Unexpected end of selector");
from identifier in ClassIdentifier }
select new ClassSyntax { Class = ':' + identifier };
return selector;
public static readonly Parser<ClassSyntax> Class = StandardClass.Or(Pseduoclass); }
public static readonly Parser<PropertySyntax> Property = private static State ParseStart(ref CharacterReader r)
from open in Parse.Char('[').Once() {
from identifier in Identifier r.SkipWhitespace();
from eq in Parse.Char('=').Once() if (r.End)
from value in Parse.CharExcept(']').Many().Text() {
from close in Parse.Char(']').Once() return State.End;
select new PropertySyntax { Property = identifier, Value = value }; }
public static readonly Parser<ChildSyntax> Child = Parse.Char('>').Token().Return(new ChildSyntax()); if (r.TakeIf(':'))
{
public static readonly Parser<DescendantSyntax> Descendant = return State.Colon;
from child in Parse.WhiteSpace.Many() }
select new DescendantSyntax(); else if (r.TakeIf('.'))
{
public static readonly Parser<TemplateSyntax> Template = return State.Class;
from template in Parse.String("/template/").Token() }
select new TemplateSyntax(); else if (r.TakeIf('#'))
{
public static readonly Parser<IsSyntax> Is = return State.Name;
from function in Parse.String(":is(") }
from type in OfType return State.TypeName;
from close in Parse.Char(')') }
select new IsSyntax { TypeName = type.TypeName, Xmlns = type.Xmlns };
private static State ParseMiddle(ref CharacterReader r)
public static readonly Parser<ISyntax> SingleSelector = {
OfType if (r.TakeIf(':'))
.Or<ISyntax>(Is) {
.Or<ISyntax>(Name) return State.Colon;
.Or<ISyntax>(Class) }
.Or<ISyntax>(Property) else if (r.TakeIf('.'))
.Or<ISyntax>(Child) {
.Or<ISyntax>(Template) return State.Class;
.Or<ISyntax>(Descendant); }
else if (r.TakeIf(char.IsWhiteSpace) || r.Peek == '>')
public static readonly Parser<IEnumerable<ISyntax>> Selector = SingleSelector.Many().End(); {
return State.Traversal;
}
else if (r.TakeIf('/'))
{
return State.Template;
}
else if (r.TakeIf('#'))
{
return State.Name;
}
return State.TypeName;
}
private static State ParseCanHaveType(ref CharacterReader r)
{
if (r.TakeIf('['))
{
return State.Property;
}
return State.Middle;
}
private static (State, ISyntax) ParseColon(ref CharacterReader r)
{
var identifier = r.ParseIdentifier();
if (identifier.IsEmpty)
{
throw new ExpressionParseException(r.Position, "Expected class name or is selector after ':'.");
}
const string IsKeyword = "is";
if (identifier.SequenceEqual(IsKeyword.AsSpan()) && r.TakeIf('('))
{
var syntax = ParseType(ref r, new IsSyntax());
if (r.End || !r.TakeIf(')'))
{
throw new ExpressionParseException(r.Position, $"Expected ')', got {r.Peek}");
}
return (State.CanHaveType, syntax);
}
else
{
return (
State.CanHaveType,
new ClassSyntax
{
Class = ":" + identifier.ToString()
});
}
}
private static (State, ISyntax) ParseTraversal(ref CharacterReader r)
{
r.SkipWhitespace();
if (r.TakeIf('>'))
{
r.SkipWhitespace();
return (State.Middle, new ChildSyntax());
}
else if (r.TakeIf('/'))
{
return (State.Template, null);
}
else if (!r.End)
{
return (State.Middle, new DescendantSyntax());
}
else
{
return (State.End, null);
}
}
private static (State, ISyntax) ParseClass(ref CharacterReader r)
{
var @class = r.ParseIdentifier();
if (@class.IsEmpty)
{
throw new ExpressionParseException(r.Position, $"Expected a class name after '.'.");
}
return (State.CanHaveType, new ClassSyntax { Class = @class.ToString() });
}
private static (State, ISyntax) ParseTemplate(ref CharacterReader r)
{
var template = r.ParseIdentifier();
const string TemplateKeyword = "template";
if (!template.SequenceEqual(TemplateKeyword.AsSpan()))
{
throw new ExpressionParseException(r.Position, $"Expected 'template', got '{template.ToString()}'");
}
else if (!r.TakeIf('/'))
{
throw new ExpressionParseException(r.Position, "Expected '/'");
}
return (State.Start, new TemplateSyntax());
}
private static (State, ISyntax) ParseName(ref CharacterReader r)
{
var name = r.ParseIdentifier();
if (name.IsEmpty)
{
throw new ExpressionParseException(r.Position, $"Expected a name after '#'.");
}
return (State.CanHaveType, new NameSyntax { Name = name.ToString() });
}
private static (State, ISyntax) ParseTypeName(ref CharacterReader r)
{
return (State.CanHaveType, ParseType(ref r, new OfTypeSyntax()));
}
private static (State, ISyntax) ParseProperty(ref CharacterReader r)
{
var property = r.ParseIdentifier();
if (!r.TakeIf('='))
{
throw new ExpressionParseException(r.Position, $"Expected '=', got '{r.Peek}'");
}
var value = r.TakeUntil(']');
r.Take();
return (State.CanHaveType, new PropertySyntax { Property = property.ToString(), Value = value.ToString() });
}
private static TSyntax ParseType<TSyntax>(ref CharacterReader r, TSyntax syntax)
where TSyntax : ITypeSyntax
{
ReadOnlySpan<char> ns = null;
ReadOnlySpan<char> type;
var namespaceOrTypeName = r.ParseIdentifier();
if (namespaceOrTypeName.IsEmpty)
{
throw new ExpressionParseException(r.Position, $"Expected an identifier, got '{r.Peek}");
}
if (!r.End && r.TakeIf('|'))
{
ns = namespaceOrTypeName;
if (r.End)
{
throw new ExpressionParseException(r.Position, $"Unexpected end of selector.");
}
type = r.ParseIdentifier();
}
else
{
type = namespaceOrTypeName;
}
syntax.Xmlns = ns.ToString();
syntax.TypeName = type.ToString();
return syntax;
}
public interface ISyntax public interface ISyntax
{ {
} }
public class OfTypeSyntax : ISyntax public interface ITypeSyntax
{
string TypeName { get; set; }
string Xmlns { get; set; }
}
public class OfTypeSyntax : ISyntax, ITypeSyntax
{ {
public string TypeName { get; set; } public string TypeName { get; set; }
public string Xmlns { get; set; } public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
@ -136,11 +307,11 @@ namespace Avalonia.Markup.Parsers
} }
} }
public class IsSyntax : ISyntax public class IsSyntax : ISyntax, ITypeSyntax
{ {
public string TypeName { get; set; } public string TypeName { get; set; }
public string Xmlns { get; set; } public string Xmlns { get; set; } = string.Empty;
public override bool Equals(object obj) public override bool Equals(object obj)
{ {

118
src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs

@ -2,10 +2,11 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using Avalonia.Data.Core;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Utilities; using Avalonia.Utilities;
using Sprache;
namespace Avalonia.Markup.Parsers namespace Avalonia.Markup.Parsers
{ {
@ -36,79 +37,68 @@ namespace Avalonia.Markup.Parsers
/// <returns>The parsed selector.</returns> /// <returns>The parsed selector.</returns>
public Selector Parse(string s) public Selector Parse(string s)
{ {
var syntax = SelectorGrammar.Selector.Parse(s); var syntax = SelectorGrammar.Parse(s);
var result = default(Selector); var result = default(Selector);
foreach (var i in syntax) foreach (var i in syntax)
{ {
var ofType = i as SelectorGrammar.OfTypeSyntax; switch (i)
var @is = i as SelectorGrammar.IsSyntax;
var @class = i as SelectorGrammar.ClassSyntax;
var name = i as SelectorGrammar.NameSyntax;
var property = i as SelectorGrammar.PropertySyntax;
var child = i as SelectorGrammar.ChildSyntax;
var descendant = i as SelectorGrammar.DescendantSyntax;
var template = i as SelectorGrammar.TemplateSyntax;
if (ofType != null)
{
result = result.OfType(_typeResolver(ofType.Xmlns, ofType.TypeName));
}
if (@is != null)
{
result = result.Is(_typeResolver(@is.Xmlns, @is.TypeName));
}
else if (@class != null)
{
result = result.Class(@class.Class);
}
else if (name != null)
{
result = result.Name(name.Name);
}
else if (property != null)
{ {
var type = result?.TargetType;
if (type == null) case SelectorGrammar.OfTypeSyntax ofType:
{ result = result.OfType(_typeResolver(ofType.Xmlns, ofType.TypeName));
throw new InvalidOperationException("Property selectors must be applied to a type."); break;
} case SelectorGrammar.IsSyntax @is:
result = result.Is(_typeResolver(@is.Xmlns, @is.TypeName));
break;
case SelectorGrammar.ClassSyntax @class:
result = result.Class(@class.Class);
break;
case SelectorGrammar.NameSyntax name:
result = result.Name(name.Name);
break;
case SelectorGrammar.PropertySyntax property:
{
var type = result?.TargetType;
var targetProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(type, property.Property); if (type == null)
{
throw new InvalidOperationException("Property selectors must be applied to a type.");
}
if (targetProperty == null) var targetProperty = AvaloniaPropertyRegistry.Instance.FindRegistered(type, property.Property);
{
throw new InvalidOperationException($"Cannot find '{property.Property}' on '{type}");
}
object typedValue; if (targetProperty == null)
{
throw new InvalidOperationException($"Cannot find '{property.Property}' on '{type}");
}
if (TypeUtilities.TryConvert( object typedValue;
targetProperty.PropertyType,
property.Value, if (TypeUtilities.TryConvert(
CultureInfo.InvariantCulture, targetProperty.PropertyType,
out typedValue)) property.Value,
{ CultureInfo.InvariantCulture,
result = result.PropertyEquals(targetProperty, typedValue); out typedValue))
} {
else result = result.PropertyEquals(targetProperty, typedValue);
{ }
throw new InvalidOperationException( else
$"Could not convert '{property.Value}' to '{targetProperty.PropertyType}"); {
} throw new InvalidOperationException(
} $"Could not convert '{property.Value}' to '{targetProperty.PropertyType}");
else if (child != null) }
{ break;
result = result.Child(); }
} case SelectorGrammar.ChildSyntax child:
else if (descendant != null) result = result.Child();
{ break;
result = result.Descendant(); case SelectorGrammar.DescendantSyntax descendant:
} result = result.Descendant();
else if (template != null) break;
{ case SelectorGrammar.TemplateSyntax template:
result = result.Template(); result = result.Template();
break;
} }
} }

14
src/OSX/Avalonia.MonoMac/PopupImpl.cs

@ -8,11 +8,23 @@ namespace Avalonia.MonoMac
public PopupImpl() public PopupImpl()
{ {
UpdateStyle(); UpdateStyle();
Window.Level = NSWindowLevel.PopUpMenu;
} }
protected override NSWindowStyle GetStyle() protected override NSWindowStyle GetStyle()
{ {
return NSWindowStyle.Borderless; return NSWindowStyle.Borderless;
} }
protected override CustomWindow CreateCustomWindow() => new CustomPopupWindow(this);
private class CustomPopupWindow : CustomWindow
{
public CustomPopupWindow(WindowBaseImpl impl)
: base(impl)
{ }
public override bool WorksWhenModal() => true;
}
} }
} }

16
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@ -19,14 +19,13 @@ namespace Avalonia.MonoMac
public WindowBaseImpl() public WindowBaseImpl()
{ {
_managedDrag = new ManagedWindowResizeDragHelper(this, _ => { }, ResizeForManagedDrag); _managedDrag = new ManagedWindowResizeDragHelper(this, _ => { }, ResizeForManagedDrag);
Window = new CustomWindow(this) // ReSharper disable once VirtualMemberCallInConstructor
{ Window = CreateCustomWindow();
StyleMask = NSWindowStyle.Titled, Window.StyleMask = NSWindowStyle.Titled;
BackingType = NSBackingStore.Buffered, Window.BackingType = NSBackingStore.Buffered;
ContentView = View, Window.ContentView = View;
// ReSharper disable once VirtualMemberCallInConstructor // ReSharper disable once VirtualMemberCallInConstructor
Delegate = CreateWindowDelegate() Window.Delegate = CreateWindowDelegate();
};
} }
public class CustomWindow : NSWindow public class CustomWindow : NSWindow
@ -57,6 +56,7 @@ namespace Avalonia.MonoMac
public void SetCanBecomeKeyAndMain() => _canBecomeKeyAndMain = true; public void SetCanBecomeKeyAndMain() => _canBecomeKeyAndMain = true;
} }
protected virtual CustomWindow CreateCustomWindow() => new CustomWindow(this);
protected virtual NSWindowDelegate CreateWindowDelegate() => new WindowBaseDelegate(this); protected virtual NSWindowDelegate CreateWindowDelegate() => new WindowBaseDelegate(this);
public class WindowBaseDelegate : NSWindowDelegate public class WindowBaseDelegate : NSWindowDelegate

19
src/Shared/SharedAssemblyInfo.cs

@ -1,19 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
[assembly: AssemblyCompany("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCopyright("Copyright AvaloniaUI 2016")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyProduct("Avalonia")]
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.6.2")]
[assembly: AssemblyFileVersion("0.6.2")]
[assembly: AssemblyInformationalVersion("0.6.2")]

20
src/Shared/avalonia.platform.targets

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Includes the Avalonia platform-specific libraries in an application's output directory -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition="'$(MSBuildThisFileDirectory)' != '' And HasTrailingSlash('$(MSBuildThisFileDirectory)')">
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Gtk\Avalonia.Cairo\bin\$(Configuration)\Avalonia.Cairo.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Gtk\Avalonia.Gtk\bin\$(Configuration)\Avalonia.Gtk.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Windows\Avalonia.Direct2D1\bin\$(Configuration)\Avalonia.Direct2D1.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Windows\Avalonia.Direct2D1\bin\$(Configuration)\SharpDX.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Windows\Avalonia.Direct2D1\bin\$(Configuration)\SharpDX.Direct2D1.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Windows\Avalonia.Direct2D1\bin\$(Configuration)\SharpDX.DXGI.dll" />
<PlatformLib Include="$(MSBuildThisFileDirectory)..\Windows\Avalonia.Win32\bin\$(Configuration)\Avalonia.Win32.dll" />
</ItemGroup>
<PropertyGroup>
<PrepareForRunDependsOn>$(PrepareForRunDependsOn);CopyPlatformLibs</PrepareForRunDependsOn>
</PropertyGroup>
<Target Name="CopyPlatformLibs">
<Copy SourceFiles="@(PlatformLib)" DestinationFolder="$(OutDir)" ContinueOnError="true"/>
</Target>
</Project>

138
src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj

@ -1,123 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="MSBuild.Sdk.Extras">
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net461</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ProjectGuid>{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}</ProjectGuid> <ExtrasEnableWpfProjectSetup>true</ExtrasEnableWpfProjectSetup>
<OutputType>Library</OutputType> <ExtrasEnableImplicitWinFormsReferences>true</ExtrasEnableImplicitWinFormsReferences>
<AppDesignerFolder>Properties</AppDesignerFolder> <UseDirect3D9>true</UseDirect3D9>
<RootNamespace>Avalonia.Win32.Interop</RootNamespace>
<AssemblyName>Avalonia.Win32.Interop</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="PresentationCore" /> <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
<Reference Include="PresentationFramework" /> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<Reference Include="System" /> <ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<Reference Include="System.Core" /> <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<Reference Include="System.Windows.Forms" /> <ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<Reference Include="System.Xaml" /> <ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
<Reference Include="System.Xml.Linq" /> <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<Reference Include="System.Data.DataSetExtensions" /> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<Reference Include="Microsoft.CSharp" /> <ProjectReference Include="..\..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<Reference Include="System.Data" /> <ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<Reference Include="System.Net.Http" /> <ProjectReference Include="..\..\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj" />
<Reference Include="System.Xml" /> <ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" />
<Reference Include="WindowsBase" /> <ProjectReference Include="..\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Win32\Interop\UnmanagedMethods.cs">
<Link>UnmanagedMethods.cs</Link>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WinForms\WinFormsAvaloniaControlHost.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Wpf\CursorShim.cs" />
<Compile Include="Wpf\Direct2DImageSurface.cs" />
<Compile Include="Wpf\IntSize.cs" />
<Compile Include="Wpf\WpfInteropExtensions.cs" />
<Compile Include="Wpf\WpfAvaloniaHost.cs" />
<Compile Include="Wpf\WpfMouseDevice.cs" />
<Compile Include="Wpf\WpfTopLevelImpl.cs" />
<Compile Include="Wpf\WritableBitmapSurface.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj"> <Compile Include="..\Avalonia.Win32\Interop\UnmanagedMethods.cs" Link="UnmanagedMethods.cs" />
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj">
<Project>{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}</Project>
<Name>Avalonia.DotNetFrameworkRuntime</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj">
<Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project>
<Name>Avalonia.Direct2D1</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj">
<Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project>
<Name>Avalonia.Win32</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<PropertyGroup>
<UseDirect3D9>true</UseDirect3D9>
</PropertyGroup>
<Import Project="..\..\..\build\SharpDX.props" /> <Import Project="..\..\..\build\SharpDX.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
</Project>

36
src/Windows/Avalonia.Win32.Interop/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Win32.Interop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.Win32.Interop")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("cbc4ff2f-92d4-420b-be21-9fe0b930b04e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

13
src/Windows/Avalonia.Win32/Avalonia.Win32.Shared.shproj

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>9defc6b7-845b-4d8f-afc0-d32bf0032b8c</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="Avalonia.Win32.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

121
src/iOS/Avalonia.iOS/Avalonia.iOS.csproj

@ -1,114 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <Project>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="Sdk.props" Sdk="MSBuild.Sdk.Extras" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>xamarin.ios10</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4488AD85-1495-4809-9AA4-DDFE0A48527E}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<RootNamespace>Avalonia.iOS</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>Avalonia.iOS</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchDebug>true</MtouchDebug>
<CodesignKey>iPhone Developer</CodesignKey>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<DisableSourceLink>true</DisableSourceLink>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AppBuilder.cs" /> <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
<Compile Include="AvaloniaRootViewController.cs" /> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<Compile Include="AvaloniaView.cs" /> <ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<Compile Include="AvaloniaWindow.cs" /> <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<Compile Include="Clipboard.cs" /> <ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<Compile Include="CursorFactory.cs" /> <ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
<Compile Include="DisplayLinkRenderLoop.cs" /> <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<Compile Include="EmbeddableImpl.cs" /> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<Compile Include="EmulatedFramebuffer.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="iOSPlatform.cs" />
<Compile Include="TopLevelImpl.cs" />
<Compile Include="PlatformIconLoader.cs" />
<Compile Include="PlatformSettings.cs" />
<Compile Include="PlatformThreadingInterface.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuntimeInfo.cs" />
<Compile Include="Specific\KeyboardEventsHelper.cs" />
<Compile Include="WindowingPlatformImpl.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
<IsAppExtension>false</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" /> <Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
<PropertyGroup Condition="'$(UseRoslynPathHack)' == 'true'"> <Import Project="Sdk.targets" Sdk="MSBuild.Sdk.Extras" />
<CscToolPath>$(MSBuildToolsPath)\..\Roslyn</CscToolPath> <Import Project="..\..\..\build\iOSWorkarounds.props" />
</PropertyGroup> </Project>
</Project>

36
src/iOS/Avalonia.iOS/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.iOS")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.iOS")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4488ad85-1495-4809-9aa4-ddfe0a48527e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

17
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -5,7 +5,6 @@ using Avalonia.Input.Platform;
using Avalonia.iOS; using Avalonia.iOS;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport; using Avalonia.Shared.PlatformSupport;
using Avalonia.Skia;
using UIKit; using UIKit;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Rendering; using Avalonia.Rendering;
@ -19,22 +18,6 @@ namespace Avalonia
builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS"); builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS");
return builder; return builder;
} }
/*
// TODO: Can we merge this with UseSkia somehow once HW/platform cleanup is done?
public static T UseSkiaViewHost<T>(this T builder) where T : AppBuilderBase<T>, new()
{
var window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new AvaloniaViewController(window);
window.RootViewController = controller;
window.MakeKeyAndVisible();
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformImpl(controller.AvaloniaView));
SkiaPlatform.Initialize();
return builder;
}*/
} }
} }

3
src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj

@ -186,4 +186,5 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
</Project> <Import Project="..\..\..\build\iOSWorkarounds.props" />
</Project>

126
src/tools/Avalonia.Designer.HostApp.NetFX/Avalonia.Designer.HostApp.NetFX.csproj

@ -1,111 +1,25 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <OutputType>Exe</OutputType>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <TargetFrameworks>net461</TargetFrameworks>
<ProjectGuid>{4ADA61C8-D191-428D-9066-EF4F0D86520F}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.Designer.HostApp</RootNamespace>
<AssemblyName>Avalonia.Designer.HostApp</AssemblyName> <AssemblyName>Avalonia.Designer.HostApp</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj"> <ProjectReference Include="..\..\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj" />
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> <ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<Name>Avalonia.DesignerSupport</Name> <ProjectReference Include="..\..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
</ProjectReference> <ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\..\Avalonia.DotNetFrameworkRuntime\Avalonia.DotNetFrameworkRuntime.csproj"> <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<Project>{4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}</Project> <ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
<Name>Avalonia.DotNetFrameworkRuntime</Name> <ProjectReference Include="..\..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
</ProjectReference> <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> <ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project> <ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
<Name>Avalonia.Markup.Xaml</Name> <ProjectReference Include="..\..\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
</ProjectReference> <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
<Project>{6417E941-21BC-467B-A771-0DE389353CE6}</Project> <ProjectReference Include="..\..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
<Name>Avalonia.Markup</Name> <ProjectReference Include="..\..\..\samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj" />
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj">
<Project>{B61B66A3-B82D-4875-8001-89D3394FE0C9}</Project>
<Name>Avalonia.Logging.Serilog</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\..\build\NetFX.props" />
</Project> </Project>

2
tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
</PropertyGroup> </PropertyGroup>

34
tests/Avalonia.Benchmarks/Markup/Parsing.cs

@ -0,0 +1,34 @@
using Avalonia.Controls;
using Avalonia.Markup.Parsers;
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Benchmarks.Markup
{
[MemoryDiagnoser]
public class Parsing
{
[Benchmark]
public void ParseComplexSelector()
{
var selectorString = "ListBox > TextBox /template/ TextBlock[IsFocused=True]";
var parser = new SelectorParser((ns, s) =>
{
switch (s)
{
case "ListBox":
return typeof(ListBox);
case "TextBox":
return typeof(TextBox);
case "TextBlock":
return typeof(TextBlock);
default:
return null;
}
});
var selector = parser.Parse(selectorString);
}
}
}

4
tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj

@ -9,9 +9,7 @@
<Import Project="..\..\build\XUnit.props" /> <Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
<ItemGroup> <Import Project="..\..\build\Base.props" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />

159
tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs

@ -1,10 +1,12 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Moq; using Moq;
using System; using System;
using System.Collections.Generic;
using Xunit; using Xunit;
namespace Avalonia.Input.UnitTests namespace Avalonia.Input.UnitTests
@ -30,7 +32,7 @@ namespace Avalonia.Input.UnitTests
} }
[Fact] [Fact]
public void MouseMove_Should_Update_PointerOver() public void MouseMove_Should_Update_IsPointerOver()
{ {
var renderer = new Mock<IRenderer>(); var renderer = new Mock<IRenderer>();
@ -59,40 +61,157 @@ namespace Avalonia.Input.UnitTests
} }
}; };
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>())) SetHit(renderer, decorator);
.Returns(new[] { decorator }); SendMouseMove(inputManager, root);
inputManager.ProcessInput(new RawMouseEventArgs(
root.MouseDevice,
0,
root,
RawMouseEventType.Move,
new Point(),
InputModifiers.None));
Assert.True(decorator.IsPointerOver); Assert.True(decorator.IsPointerOver);
Assert.True(border.IsPointerOver); Assert.True(border.IsPointerOver);
Assert.False(canvas.IsPointerOver); Assert.False(canvas.IsPointerOver);
Assert.True(root.IsPointerOver); Assert.True(root.IsPointerOver);
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>())) SetHit(renderer, canvas);
.Returns(new[] { canvas }); SendMouseMove(inputManager, root);
Assert.False(decorator.IsPointerOver);
Assert.False(border.IsPointerOver);
Assert.True(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
}
}
[Fact]
public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True()
{
var renderer = new Mock<IRenderer>();
using (TestApplication(renderer.Object))
{
var inputManager = InputManager.Instance;
inputManager.ProcessInput(new RawMouseEventArgs( Canvas canvas;
root.MouseDevice, Border border;
0, Decorator decorator;
root,
RawMouseEventType.Move, var root = new TestRoot
new Point(), {
InputModifiers.None)); MouseDevice = new MouseDevice(),
Renderer = renderer.Object,
Child = new Panel
{
Children =
{
(canvas = new Canvas()),
(border = new Border
{
Child = decorator = new Decorator(),
})
}
}
};
SetHit(renderer, canvas);
SendMouseMove(inputManager, root);
Assert.False(decorator.IsPointerOver); Assert.False(decorator.IsPointerOver);
Assert.False(border.IsPointerOver); Assert.False(border.IsPointerOver);
Assert.True(canvas.IsPointerOver); Assert.True(canvas.IsPointerOver);
Assert.True(root.IsPointerOver); Assert.True(root.IsPointerOver);
// Ensure that e.Handled is reset between controls.
decorator.PointerEnter += (s, e) => e.Handled = true;
SetHit(renderer, decorator);
SendMouseMove(inputManager, root);
Assert.True(decorator.IsPointerOver);
Assert.True(border.IsPointerOver);
Assert.False(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
} }
} }
[Fact]
public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order()
{
var renderer = new Mock<IRenderer>();
var result = new List<(object, string)>();
void HandleEvent(object sender, PointerEventArgs e)
{
result.Add((sender, e.RoutedEvent.Name));
}
using (TestApplication(renderer.Object))
{
var inputManager = InputManager.Instance;
Canvas canvas;
Border border;
Decorator decorator;
var root = new TestRoot
{
MouseDevice = new MouseDevice(),
Renderer = renderer.Object,
Child = new Panel
{
Children =
{
(canvas = new Canvas()),
(border = new Border
{
Child = decorator = new Decorator(),
})
}
}
};
SetHit(renderer, canvas);
SendMouseMove(inputManager, root);
AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator);
SetHit(renderer, decorator);
SendMouseMove(inputManager, root);
Assert.Equal(
new[]
{
((object)canvas, "PointerLeave"),
((object)decorator, "PointerEnter"),
((object)border, "PointerEnter"),
},
result);
}
}
private void AddEnterLeaveHandlers(
EventHandler<PointerEventArgs> handler,
params IControl[] controls)
{
foreach (var c in controls)
{
c.PointerEnter += handler;
c.PointerLeave += handler;
}
}
private void SendMouseMove(IInputManager inputManager, TestRoot root)
{
inputManager.ProcessInput(new RawMouseEventArgs(
root.MouseDevice,
0,
root,
RawMouseEventType.Move,
new Point(),
InputModifiers.None));
}
private void SetHit(Mock<IRenderer> renderer, IControl hit)
{
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
.Returns(new[] { hit });
}
private IDisposable TestApplication(IRenderer renderer) private IDisposable TestApplication(IRenderer renderer)
{ {
return UnitTestApplication.Start( return UnitTestApplication.Start(

1
tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj

@ -8,7 +8,6 @@
<Import Project="..\..\build\XUnit.props" /> <Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
<Import Project="..\..\build\Markup.props" />
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj" />

146
tests/Avalonia.Markup.UnitTests/Data/BindingTests_Converters.cs

@ -0,0 +1,146 @@
// 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.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Xunit;
namespace Avalonia.Markup.UnitTests.Data
{
public class BindingTests_Converters
{
[Fact]
public void Converter_Should_Be_Used()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
Converter = StringConverters.NullOrEmpty,
};
var expressionObserver = (BindingExpression)target.Initiate(
textBlock,
TextBlock.TextProperty).Observable;
Assert.Same(StringConverters.NullOrEmpty, expressionObserver.Converter);
}
public class When_Binding_To_String
{
[Fact]
public void StringFormatConverter_Should_Be_Used_When_Binding_Has_StringFormat()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
StringFormat = "Hello {0}",
};
var expressionObserver = (BindingExpression)target.Initiate(
textBlock,
TextBlock.TextProperty).Observable;
Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter);
}
}
public class When_Binding_To_Object
{
[Fact]
public void StringFormatConverter_Should_Be_Used_When_Binding_Has_StringFormat()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
StringFormat = "Hello {0}",
};
var expressionObserver = (BindingExpression)target.Initiate(
textBlock,
TextBlock.TagProperty).Observable;
Assert.IsType<StringFormatValueConverter>(expressionObserver.Converter);
}
}
public class When_Binding_To_Non_String_Or_Object
{
[Fact]
public void StringFormatConverter_Should_Not_Be_Used_When_Binding_Has_StringFormat()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
StringFormat = "Hello {0}",
};
var expressionObserver = (BindingExpression)target.Initiate(
textBlock,
TextBlock.MarginProperty).Observable;
Assert.Same(DefaultValueConverter.Instance, expressionObserver.Converter);
}
}
[Fact]
public void StringFormat_Should_Be_Applied()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
StringFormat = "Hello {0}",
};
textBlock.Bind(TextBlock.TextProperty, target);
Assert.Equal("Hello foo", textBlock.Text);
}
[Fact]
public void StringFormat_Should_Be_Applied_After_Converter()
{
var textBlock = new TextBlock
{
DataContext = new Class1(),
};
var target = new Binding(nameof(Class1.Foo))
{
Converter = StringConverters.NotNullOrEmpty,
StringFormat = "Hello {0}",
};
textBlock.Bind(TextBlock.TextProperty, target);
Assert.Equal("Hello True", textBlock.Text);
}
private class Class1
{
public string Foo { get; set; } = "foo";
}
}
}

44
tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs

@ -2,8 +2,8 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Linq; using System.Linq;
using Avalonia.Data.Core;
using Avalonia.Markup.Parsers; using Avalonia.Markup.Parsers;
using Sprache;
using Xunit; using Xunit;
namespace Avalonia.Markup.UnitTests.Parsers namespace Avalonia.Markup.UnitTests.Parsers
@ -13,17 +13,17 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType() public void OfType()
{ {
var result = SelectorGrammar.Selector.Parse("Button").ToList(); var result = SelectorGrammar.Parse("Button");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = null } }, new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = "" } },
result); result);
} }
[Fact] [Fact]
public void NamespacedOfType() public void NamespacedOfType()
{ {
var result = SelectorGrammar.Selector.Parse("x|Button").ToList(); var result = SelectorGrammar.Parse("x|Button");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = "x" } }, new[] { new SelectorGrammar.OfTypeSyntax { TypeName = "Button", Xmlns = "x" } },
@ -33,7 +33,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Name() public void Name()
{ {
var result = SelectorGrammar.Selector.Parse("#foo").ToList(); var result = SelectorGrammar.Parse("#foo");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.NameSyntax { Name = "foo" }, }, new[] { new SelectorGrammar.NameSyntax { Name = "foo" }, },
@ -43,7 +43,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Name() public void OfType_Name()
{ {
var result = SelectorGrammar.Selector.Parse("Button#foo").ToList(); var result = SelectorGrammar.Parse("Button#foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -57,17 +57,17 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Is() public void Is()
{ {
var result = SelectorGrammar.Selector.Parse(":is(Button)").ToList(); var result = SelectorGrammar.Parse(":is(Button)");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.IsSyntax { TypeName = "Button", Xmlns = null } }, new[] { new SelectorGrammar.IsSyntax { TypeName = "Button", Xmlns = "" } },
result); result);
} }
[Fact] [Fact]
public void Is_Name() public void Is_Name()
{ {
var result = SelectorGrammar.Selector.Parse(":is(Button)#foo").ToList(); var result = SelectorGrammar.Parse(":is(Button)#foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -81,7 +81,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void NamespacedIs_Name() public void NamespacedIs_Name()
{ {
var result = SelectorGrammar.Selector.Parse(":is(x|Button)#foo").ToList(); var result = SelectorGrammar.Parse(":is(x|Button)#foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -95,7 +95,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Class() public void Class()
{ {
var result = SelectorGrammar.Selector.Parse(".foo").ToList(); var result = SelectorGrammar.Parse(".foo");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.ClassSyntax { Class = "foo" } }, new[] { new SelectorGrammar.ClassSyntax { Class = "foo" } },
@ -105,7 +105,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Pseudoclass() public void Pseudoclass()
{ {
var result = SelectorGrammar.Selector.Parse(":foo").ToList(); var result = SelectorGrammar.Parse(":foo");
Assert.Equal( Assert.Equal(
new[] { new SelectorGrammar.ClassSyntax { Class = ":foo" } }, new[] { new SelectorGrammar.ClassSyntax { Class = ":foo" } },
@ -115,7 +115,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Class() public void OfType_Class()
{ {
var result = SelectorGrammar.Selector.Parse("Button.foo").ToList(); var result = SelectorGrammar.Parse("Button.foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -129,7 +129,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Child_Class() public void OfType_Child_Class()
{ {
var result = SelectorGrammar.Selector.Parse("Button > .foo").ToList(); var result = SelectorGrammar.Parse("Button > .foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -144,7 +144,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Child_Class_No_Spaces() public void OfType_Child_Class_No_Spaces()
{ {
var result = SelectorGrammar.Selector.Parse("Button>.foo").ToList(); var result = SelectorGrammar.Parse("Button>.foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -159,7 +159,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Descendant_Class() public void OfType_Descendant_Class()
{ {
var result = SelectorGrammar.Selector.Parse("Button .foo").ToList(); var result = SelectorGrammar.Parse("Button .foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -174,7 +174,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Template_Class() public void OfType_Template_Class()
{ {
var result = SelectorGrammar.Selector.Parse("Button /template/ .foo").ToList(); var result = SelectorGrammar.Parse("Button /template/ .foo");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -189,7 +189,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void OfType_Property() public void OfType_Property()
{ {
var result = SelectorGrammar.Selector.Parse("Button[Foo=bar]").ToList(); var result = SelectorGrammar.Parse("Button[Foo=bar]");
Assert.Equal( Assert.Equal(
new SelectorGrammar.ISyntax[] new SelectorGrammar.ISyntax[]
@ -203,25 +203,25 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Namespace_Alone_Fails() public void Namespace_Alone_Fails()
{ {
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("ns|").ToList()); Assert.Throws<ExpressionParseException>(() => SelectorGrammar.Parse("ns|"));
} }
[Fact] [Fact]
public void Dot_Alone_Fails() public void Dot_Alone_Fails()
{ {
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(". dot").ToList()); Assert.Throws<ExpressionParseException>(() => SelectorGrammar.Parse(". dot"));
} }
[Fact] [Fact]
public void Invalid_Identifier_Fails() public void Invalid_Identifier_Fails()
{ {
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse("%foo").ToList()); Assert.Throws<ExpressionParseException>(() => SelectorGrammar.Parse("%foo"));
} }
[Fact] [Fact]
public void Invalid_Class_Fails() public void Invalid_Class_Fails()
{ {
Assert.Throws<ParseException>(() => SelectorGrammar.Selector.Parse(".%foo").ToList()); Assert.Throws<ExpressionParseException>(() => SelectorGrammar.Parse(".%foo"));
} }
} }
} }

4
tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs

@ -1,6 +1,8 @@
using System; using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Parsers; using Avalonia.Markup.Parsers;
using Avalonia.Styling;
using Xunit; using Xunit;
namespace Avalonia.Markup.UnitTests.Parsers namespace Avalonia.Markup.UnitTests.Parsers
@ -10,7 +12,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
[Fact] [Fact]
public void Parses_Boolean_Property_Selector() public void Parses_Boolean_Property_Selector()
{ {
var target = new SelectorParser((type, ns) => typeof(TextBlock)); var target = new SelectorParser((ns, type) => typeof(TextBlock));
var result = target.Parse("TextBlock[IsPointerOver=True]"); var result = target.Parse("TextBlock[IsPointerOver=True]");
} }
} }

1
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@ -8,7 +8,6 @@
<Import Project="..\..\build\XUnit.props" /> <Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\Microsoft.Reactive.Testing.props" /> <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
<Import Project="..\..\build\Sprache.props" />
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" /> <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />

74
tests/Avalonia.Markup.Xaml.UnitTests/Parsers/PropertyParserTests.cs

@ -13,7 +13,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Parses_Name() public void Parses_Name()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo"); var reader = new CharacterReader("Foo".AsSpan());
var (ns, owner, name) = target.Parse(reader); var (ns, owner, name) = target.Parse(reader);
Assert.Null(ns); Assert.Null(ns);
@ -25,7 +25,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Parses_Owner_And_Name() public void Parses_Owner_And_Name()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo.Bar"); var reader = new CharacterReader("Foo.Bar".AsSpan());
var (ns, owner, name) = target.Parse(reader); var (ns, owner, name) = target.Parse(reader);
Assert.Null(ns); Assert.Null(ns);
@ -37,7 +37,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Parses_Namespace_Owner_And_Name() public void Parses_Namespace_Owner_And_Name()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("foo:Bar.Baz"); var reader = new CharacterReader("foo:Bar.Baz".AsSpan());
var (ns, owner, name) = target.Parse(reader); var (ns, owner, name) = target.Parse(reader);
Assert.Equal("foo", ns); Assert.Equal("foo", ns);
@ -49,7 +49,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Parses_Owner_And_Name_With_Parentheses() public void Parses_Owner_And_Name_With_Parentheses()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("(Foo.Bar)"); var reader = new CharacterReader("(Foo.Bar)".AsSpan());
var (ns, owner, name) = target.Parse(reader); var (ns, owner, name) = target.Parse(reader);
Assert.Null(ns); Assert.Null(ns);
@ -61,7 +61,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Parses_Namespace_Owner_And_Name_With_Parentheses() public void Parses_Namespace_Owner_And_Name_With_Parentheses()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("(foo:Bar.Baz)"); var reader = new CharacterReader("(foo:Bar.Baz)".AsSpan());
var (ns, owner, name) = target.Parse(reader); var (ns, owner, name) = target.Parse(reader);
Assert.Equal("foo", ns); Assert.Equal("foo", ns);
@ -73,9 +73,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Empty_String() public void Fails_With_Empty_String()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader)); var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader(ReadOnlySpan<char>.Empty)));
Assert.Equal(0, ex.Column); Assert.Equal(0, ex.Column);
Assert.Equal("Expected property name.", ex.Message); Assert.Equal("Expected property name.", ex.Message);
} }
@ -84,9 +83,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Only_Whitespace() public void Fails_With_Only_Whitespace()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader(" ");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader)); var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader(" ".AsSpan())));
Assert.Equal(0, ex.Column); Assert.Equal(0, ex.Column);
Assert.Equal("Unexpected ' '.", ex.Message); Assert.Equal("Unexpected ' '.", ex.Message);
} }
@ -95,9 +93,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Leading_Whitespace() public void Fails_With_Leading_Whitespace()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader(" Foo");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader(" Foo".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(0, ex.Column); Assert.Equal(0, ex.Column);
Assert.Equal("Unexpected ' '.", ex.Message); Assert.Equal("Unexpected ' '.", ex.Message);
} }
@ -106,9 +103,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Trailing_Whitespace() public void Fails_With_Trailing_Whitespace()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo ");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo ".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(3, ex.Column); Assert.Equal(3, ex.Column);
Assert.Equal("Unexpected ' '.", ex.Message); Assert.Equal("Unexpected ' '.", ex.Message);
} }
@ -117,9 +113,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Invalid_Property_Name() public void Fails_With_Invalid_Property_Name()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("123");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("123".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(0, ex.Column); Assert.Equal(0, ex.Column);
Assert.Equal("Unexpected '1'.", ex.Message); Assert.Equal("Unexpected '1'.", ex.Message);
} }
@ -128,9 +123,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Trailing_Junk() public void Fails_With_Trailing_Junk()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo%");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo%".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(3, ex.Column); Assert.Equal(3, ex.Column);
Assert.Equal("Unexpected '%'.", ex.Message); Assert.Equal("Unexpected '%'.", ex.Message);
} }
@ -139,9 +133,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Invalid_Property_Name_After_Owner() public void Fails_With_Invalid_Property_Name_After_Owner()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo.123");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo.123".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(4, ex.Column); Assert.Equal(4, ex.Column);
Assert.Equal("Unexpected '1'.", ex.Message); Assert.Equal("Unexpected '1'.", ex.Message);
} }
@ -150,9 +143,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Whitespace_Between_Owner_And_Name() public void Fails_With_Whitespace_Between_Owner_And_Name()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo. Bar");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo. Bar".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(4, ex.Column); Assert.Equal(4, ex.Column);
Assert.Equal("Unexpected ' '.", ex.Message); Assert.Equal("Unexpected ' '.", ex.Message);
} }
@ -161,9 +153,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Too_Many_Segments() public void Fails_With_Too_Many_Segments()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo.Bar.Baz");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo.Bar.Baz".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(8, ex.Column); Assert.Equal(8, ex.Column);
Assert.Equal("Unexpected '.'.", ex.Message); Assert.Equal("Unexpected '.'.", ex.Message);
} }
@ -172,9 +163,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Too_Many_Namespaces() public void Fails_With_Too_Many_Namespaces()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("foo:bar:Baz");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("foo:bar:Baz".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(8, ex.Column); Assert.Equal(8, ex.Column);
Assert.Equal("Unexpected ':'.", ex.Message); Assert.Equal("Unexpected ':'.", ex.Message);
} }
@ -183,9 +173,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Parens_But_No_Owner() public void Fails_With_Parens_But_No_Owner()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("(Foo)");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("(Foo)".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(1, ex.Column); Assert.Equal(1, ex.Column);
Assert.Equal("Expected property owner.", ex.Message); Assert.Equal("Expected property owner.", ex.Message);
} }
@ -194,9 +183,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Parens_And_Namespace_But_No_Owner() public void Fails_With_Parens_And_Namespace_But_No_Owner()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("(foo:Bar)");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("(foo:Bar)".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(1, ex.Column); Assert.Equal(1, ex.Column);
Assert.Equal("Expected property owner.", ex.Message); Assert.Equal("Expected property owner.", ex.Message);
} }
@ -205,9 +193,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Missing_Close_Parens() public void Fails_With_Missing_Close_Parens()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("(Foo.Bar");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("(Foo.Bar".AsSpan())));
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader));
Assert.Equal(8, ex.Column); Assert.Equal(8, ex.Column);
Assert.Equal("Expected ')'.", ex.Message); Assert.Equal("Expected ')'.", ex.Message);
} }
@ -216,9 +203,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Parsers
public void Fails_With_Unexpected_Close_Parens() public void Fails_With_Unexpected_Close_Parens()
{ {
var target = new PropertyParser(); var target = new PropertyParser();
var reader = new CharacterReader("Foo.Bar)");
var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(reader)); var ex = Assert.Throws<ExpressionParseException>(() => target.Parse(new CharacterReader("Foo.Bar)".AsSpan())));
Assert.Equal(7, ex.Column); Assert.Equal(7, ex.Column);
Assert.Equal("Unexpected ')'.", ex.Message); Assert.Equal("Unexpected ')'.", ex.Message);
} }

22
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs

@ -308,5 +308,27 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(5.6, AttachedPropertyOwner.GetDouble(textBlock)); Assert.Equal(5.6, AttachedPropertyOwner.GetDouble(textBlock));
} }
} }
[Fact]
public void Binding_To_TextBlock_Text_With_StringConverter_Works()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<TextBlock Name='textBlock' Text='{Binding Foo, StringFormat=Hello \{0\}}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
textBlock.DataContext = new { Foo = "world" };
window.ApplyTemplate();
Assert.Equal("Hello world", textBlock.Text);
}
}
} }
} }

6
tests/Avalonia.UnitTests/ImmediateDispatcher.cs

@ -25,6 +25,12 @@ namespace Avalonia.UnitTests
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
public Task<TResult> InvokeAsync<TResult>(Func<TResult> function, DispatcherPriority priority = DispatcherPriority.Normal)
{
var result = function();
return Task.FromResult(result);
}
public void VerifyAccess() public void VerifyAccess()
{ {
} }

20
tests/run-tests.sh

@ -1,20 +0,0 @@
# !/bin/bash
cd "$(dirname "$0")"
tests=(Avalonia.*.UnitTests/)
exclude=("*Direct2D*/")
result=0
for del in ${exclude[@]}; do
tests=(${tests[@]/$del})
done
for test in ${tests[@]}; do
echo Running test $test
mono ../testrunner/xunit.runner.console.2.1.0/tools/xunit.console.exe ${test}bin/Release/${test%/}.dll -parallel none
if [ $? -ne 0 ]; then result=1 ; fi
done
exit $result
Loading…
Cancel
Save