Browse Source

Merge branch 'master' into DisableButtonOnNullCommandBinding

pull/894/head
Jeremy Koritzinsky 9 years ago
committed by GitHub
parent
commit
6d32e6a36c
  1. 5
      .ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject
  2. 5
      .ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject
  3. 5
      .ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject
  4. 5
      .ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject
  5. 5
      .ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject
  6. 5
      .ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject
  7. 5
      .ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject
  8. 5
      .ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject
  9. 5
      .ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject
  10. 5
      .ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject
  11. 5
      .ncrunch/Direct3DInteropSample.v3.ncrunchproject
  12. 1
      Avalonia.sln.DotSettings
  13. 1
      appveyor.yml
  14. 43
      build.cake
  15. 5
      build/SkiaSharp.Desktop.props
  16. 3
      build/SkiaSharp.props
  17. 2
      docs/guidelines/build.md
  18. 2
      docs/index.md
  19. 2
      docs/tutorial/gettingstarted.md
  20. 24
      packages.cake
  21. 14
      parameters.cake
  22. 2
      readme.md
  23. 3
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  24. 2
      samples/interop/Direct3DInteropSample/MainWindow.cs
  25. 1
      samples/interop/GtkInteropDemo/GtkInteropDemo.csproj
  26. 1
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  27. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  28. 17
      src/Avalonia.Base/AvaloniaObject.cs
  29. 2
      src/Avalonia.Base/AvaloniaProperty.cs
  30. 25
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  31. 4
      src/Avalonia.Base/PriorityValue.cs
  32. 237
      src/Avalonia.Base/Utilities/TypeUtilities.cs
  33. 24
      src/Avalonia.Controls/AppBuilderBase.cs
  34. 2
      src/Avalonia.Controls/Button.cs
  35. 2
      src/Avalonia.Controls/Control.cs
  36. 12
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  37. 2
      src/Avalonia.Controls/ItemsControl.cs
  38. 3
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  39. 7
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  40. 7
      src/Avalonia.Controls/TextBox.cs
  41. 35
      src/Avalonia.Controls/TopLevel.cs
  42. 28
      src/Avalonia.Controls/Window.cs
  43. 34
      src/Avalonia.Controls/WindowBase.cs
  44. 4
      src/Avalonia.DesignerSupport/DesignerAssist.cs
  45. 8
      src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
  46. 7
      src/Avalonia.Layout/Layoutable.cs
  47. 2
      src/Avalonia.Styling/Controls/NameScope.cs
  48. 7
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  49. 2
      src/Avalonia.Visuals/Rendering/ZIndexComparer.cs
  50. 2
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  51. 1
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  52. 6
      src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
  53. 6
      src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
  54. 1
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  55. 5
      src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
  56. 1
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  57. 1
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  58. 2
      src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs
  59. 4
      src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs
  60. 64
      src/Markup/Avalonia.Markup/DefaultValueConverter.cs
  61. 6
      src/Shared/SharedAssemblyInfo.cs
  62. 1
      src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj
  63. 2
      src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj
  64. 4
      src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs
  65. 4
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  66. 2
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  67. 4
      src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs
  68. 1
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  69. 25
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  70. 53
      tests/Avalonia.Layout.UnitTests/MeasureTests.cs
  71. 17
      tests/Avalonia.Markup.Xaml.UnitTests/Parsers/SelectorParserTests.cs

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

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

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

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

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

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

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

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

5
.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject

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

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

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

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

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

5
.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject

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

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

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

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

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

5
.ncrunch/Direct3DInteropSample.v3.ncrunchproject

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

1
Avalonia.sln.DotSettings

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>

1
appveyor.yml

@ -35,6 +35,7 @@ test: off
artifacts:
- path: artifacts\nuget\*.nupkg
- path: artifacts\zip\*.zip
- path: artifacts\inspectcode.xml
cache:
- gtk-sharp-2.12.26.msi
- dotnet-1.0.1.exe

43
build.cake

@ -5,7 +5,8 @@
#addin "nuget:?package=Polly&version=4.2.0"
#addin "nuget:?package=NuGet.Core&version=2.12.0"
#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.1.20150828.125449"
#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140"
#tool "JetBrains.ReSharper.CommandLineTools"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
///////////////////////////////////////////////////////////////////////////////
@ -104,7 +105,7 @@ Task("Restore-NuGet-Packages")
.Does(() =>
{
var maxRetryCount = 5;
var toolTimeout = 1d;
var toolTimeout = 2d;
Policy
.Handle<Exception>()
.Retry(maxRetryCount, (exception, retryCount, context) => {
@ -279,11 +280,15 @@ Task("Zip-Files")
Zip(parameters.ZipSourceControlCatalogDesktopDirs,
parameters.ZipTargetControlCatalogDesktopDirs,
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") +
GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
});
Task("Create-NuGet-Packages")
.IsDependentOn("Run-Unit-Tests")
.IsDependentOn("Inspect")
.Does(() =>
{
foreach(var nuspec in packages.NuspecNuGetSettings)
@ -331,7 +336,6 @@ Task("Publish-NuGet")
.WithCriteria(() => !parameters.IsLocalBuild)
.WithCriteria(() => !parameters.IsPullRequest)
.WithCriteria(() => parameters.IsMainRepo)
.WithCriteria(() => parameters.IsMasterBranch)
.WithCriteria(() => parameters.IsNuGetRelease)
.Does(() =>
{
@ -360,6 +364,39 @@ Task("Publish-NuGet")
Information("Publish-NuGet Task failed, but continuing with next Task...");
});
Task("Inspect")
.WithCriteria(parameters.IsRunningOnWindows)
.IsDependentOn("Restore-NuGet-Packages")
.Does(() =>
{
var badIssues = new []{"PossibleNullReferenceException"};
var whitelist = new []{"tests", "src\\android", "src\\ios",
"src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"};
Information("Running code inspections");
StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe",
new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" });
Information("Analyzing report");
var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml"));
var failBuild = false;
foreach(var xml in doc.Descendants("Issue"))
{
var typeId = xml.Attribute("TypeId").Value.ToString();
if(badIssues.Contains(typeId))
{
var file = xml.Attribute("File").Value.ToString().ToLower();
if(whitelist.Any(wh => file.StartsWith(wh)))
continue;
var line = xml.Attribute("Line").Value.ToString();
Error(typeId + " - " + file + " on line " + line);
failBuild = true;
}
}
if(failBuild)
throw new Exception("Issues found");
});
///////////////////////////////////////////////////////////////////////////////
// TARGETS
///////////////////////////////////////////////////////////////////////////////

5
build/SkiaSharp.Desktop.props

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

3
build/SkiaSharp.props

@ -1,5 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.56.1-beta" />
<PackageReference Include="SkiaSharp" Version="1.57.1" />
<PackageReference Condition="$(TargetFramework.Trim('.').ToLower().StartsWith('netframework'))" Include="Avalonia.Skia.Linux.Natives" Version="1.57.1.3" />
</ItemGroup>
</Project>

2
docs/guidelines/build.md

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

2
docs/index.md

@ -10,7 +10,7 @@ What does alpha mean? Well, it means that it's now at a stage where you can have
## How do I try it out
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/a4542e8a-b56c-4295-8df1-7e220178b873).
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ):

2
docs/tutorial/gettingstarted.md

@ -4,7 +4,7 @@
![](images/add-dialogs.png)
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213).
The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio).
This will add a Avalonia project template and a Window template to the standard Visual Studo “Add”
dialog (yes, icons still to come :) ):

24
packages.cake

@ -7,6 +7,7 @@ public class Packages
public FilePath[] BinFiles { get; private set; }
public string NugetPackagesDir {get; private set;}
public string SkiaSharpVersion {get; private set; }
public string SkiaSharpLinuxVersion {get; private set; }
public Packages(ICakeContext context, Parameters parameters)
{
// NUGET NUSPECS
@ -75,6 +76,7 @@ public class Packages
var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1;
var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1;
SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1;
SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1;
var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1;
var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1;
var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1;
@ -85,6 +87,7 @@ public class Packages
context.Information("Package: Sprache, version: {0}", SpracheVersion);
context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion);
context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion);
context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion);
context.Information("Package: SharpDX, version: {0}", SharpDXVersion);
context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version);
context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version);
@ -425,10 +428,7 @@ public class Packages
{
new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion },
//netstandard1.3
new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netstandard1.3", Version = parameters.Version },
new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netstandard1.3", Version = SkiaSharpVersion },
new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netstandard1.3", Version = "1.6.0" }
new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion }
},
Files = new []
{
@ -446,11 +446,17 @@ public class Packages
Id = "Avalonia.Desktop",
Dependencies = new []
{
new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Cairo", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version }
//Full .NET
new NuSpecDependency() { Id = "Avalonia.Direct2D1", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Cairo", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version },
//.NET Core
new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version },
new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version }
},
Files = new NuSpecContent[]
{

14
parameters.cake

@ -75,17 +75,25 @@ public class Parameters
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform)
&& StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable;
IsNuGetRelease = IsTagged && IsReleasable;
// VERSION
Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion);
if (IsRunningOnAppVeyor)
{
string tagVersion = null;
if (IsTagged)
{
// Use Tag Name as version
Version = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
var nugetReleasePrefix = "nuget-release-";
IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix);
if(IsNuGetRelease)
tagVersion = tag.Substring(nugetReleasePrefix.Length);
}
if(tagVersion != null)
{
Version = tagVersion;
}
else
{

2
readme.md

@ -42,7 +42,7 @@ using Direct2D and other operating systems using Gtk & Cairo.
Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you
can have a play and hopefully create simple applications. There's now a [Visual
Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213)
Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
containing project and item templates that will help you get started, and
there's an initial complement of controls. There's still a lot missing, and you
*will* find bugs, and the API *will* change, but this represents the first time

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

@ -26,7 +26,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
@ -142,6 +142,7 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\src\Shared\nuget.workaround.targets" />
</Project>

2
samples/interop/Direct3DInteropSample/MainWindow.cs

@ -58,7 +58,7 @@ namespace Direct3DInteropSample
new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height,
new Rational(60, 1), Format.R8G8B8A8_UNorm),
IsWindowed = true,
OutputHandle = PlatformImpl.Handle.Handle,
OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput

1
samples/interop/GtkInteropDemo/GtkInteropDemo.csproj

@ -149,6 +149,7 @@
<Name>ControlCatalog</Name>
</ProjectReference>
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project>

1
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@ -179,6 +179,7 @@
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildThisFileDirectory)..\..\..\src\Shared\nuget.workaround.targets" />
</Project>

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

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

17
src/Avalonia.Base/AvaloniaObject.cs

@ -578,13 +578,13 @@ namespace Avalonia
if (notification == null)
{
return TypeUtilities.CastOrDefault(value, type);
return TypeUtilities.ConvertImplicitOrDefault(value, type);
}
else
{
if (notification.HasValue)
{
notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type));
notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type));
}
return notification;
@ -622,14 +622,9 @@ namespace Avalonia
/// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property)
{
if (property.Inherits && _inheritanceParent != null)
{
return (_inheritanceParent as AvaloniaObject).GetValueInternal(property);
}
else
{
return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType());
}
if (property.Inherits && _inheritanceParent is AvaloniaObject aobj)
return aobj.GetValueInternal(property);
return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}
/// <summary>
@ -735,7 +730,7 @@ namespace Avalonia
ThrowNotRegistered(property);
}
if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value))
{
throw new ArgumentException(string.Format(
"Invalid value for Property '{0}': '{1}' ({2})",

2
src/Avalonia.Base/AvaloniaProperty.cs

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

25
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -103,11 +103,9 @@ namespace Avalonia.Collections
_inner = new Dictionary<TKey, TValue>();
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[]"));
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]"));
if (CollectionChanged != null)
{
@ -144,12 +142,9 @@ namespace Avalonia.Collections
if (_inner.TryGetValue(key, out value))
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
@ -199,11 +194,9 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null)
{

4
src/Avalonia.Base/PriorityValue.cs

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

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

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

24
src/Avalonia.Controls/AppBuilderBase.cs

@ -55,8 +55,7 @@ namespace Avalonia.Controls
public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
/// <summary>
/// Gets or sets a method to call before <see cref="Start{TMainWindow}"/> is called on the
/// <see cref="Application"/>.
/// Gets or sets a method to call before Startis called on the <see cref="Application"/>.
/// </summary>
public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
@ -94,8 +93,7 @@ namespace Avalonia.Controls
protected TAppBuilder Self => (TAppBuilder) this;
/// <summary>
/// Registers a callback to call before <see cref="Start{TMainWindow}"/> is called on the
/// <see cref="Application"/>.
/// Registers a callback to call before Start is called on the <see cref="Application"/>.
/// </summary>
/// <param name="callback">The callback.</param>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
@ -129,6 +127,24 @@ namespace Avalonia.Controls
Instance.Run(window);
}
/// <summary>
/// Starts the application with the provided instance of <typeparamref name="TMainWindow"/>.
/// </summary>
/// <typeparam name="TMainWindow">The window type.</typeparam>
/// <param name="mainWindow">Instance of type TMainWindow to use when starting the app</param>
/// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
public void Start<TMainWindow>(TMainWindow mainWindow, Func<object> dataContextProvider = null)
where TMainWindow : Window
{
Setup();
BeforeStartCallback(Self);
if (dataContextProvider != null)
mainWindow.DataContext = dataContextProvider();
mainWindow.Show();
Instance.Run(mainWindow);
}
/// <summary>
/// Sets up the platform-specific services for the application, but does not run it.
/// </summary>

2
src/Avalonia.Controls/Button.cs

@ -291,7 +291,7 @@ namespace Avalonia.Controls
{
var button = e.Sender as Button;
var isDefault = (bool)e.NewValue;
var inputRoot = button.VisualRoot as IInputElement;
var inputRoot = button?.VisualRoot as IInputElement;
if (inputRoot != null)
{

2
src/Avalonia.Controls/Control.cs

@ -645,7 +645,7 @@ namespace Avalonia.Controls
if (_focusAdorner != null)
{
var adornerLayer = _focusAdorner.Parent as Panel;
var adornerLayer = (IPanel)_focusAdorner.Parent;
adornerLayer.Children.Remove(_focusAdorner);
_focusAdorner = null;
}

12
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -4,6 +4,7 @@ using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Styling;
using JetBrains.Annotations;
namespace Avalonia.Controls.Embedding
{
@ -18,6 +19,7 @@ namespace Avalonia.Controls.Embedding
{
}
[CanBeNull]
public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl;
public void Prepare()
@ -39,8 +41,9 @@ namespace Avalonia.Controls.Embedding
protected override Size MeasureOverride(Size availableSize)
{
base.MeasureOverride(PlatformImpl.ClientSize);
return PlatformImpl.ClientSize;
var cs = PlatformImpl?.ClientSize ?? default(Size);
base.MeasureOverride(cs);
return cs;
}
private readonly NameScope _nameScope = new NameScope();
@ -63,9 +66,6 @@ namespace Avalonia.Controls.Embedding
public void Unregister(string name) => _nameScope.Unregister(name);
Type IStyleable.StyleKey => typeof(EmbeddableControlRoot);
public void Dispose()
{
PlatformImpl.Dispose();
}
public void Dispose() => PlatformImpl?.Dispose();
}
}

2
src/Avalonia.Controls/ItemsControl.cs

@ -354,7 +354,7 @@ namespace Avalonia.Controls
}
var collection = sender as ICollection;
PseudoClasses.Set(":empty", collection.Count == 0);
PseudoClasses.Set(":empty", collection == null || collection.Count == 0);
}
/// <summary>

3
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -155,8 +155,7 @@ namespace Avalonia.Controls.Presenters
case NotifyCollectionChangedAction.Add:
CreateAndRemoveContainers();
if (e.NewStartingIndex >= FirstIndex &&
e.NewStartingIndex < NextIndex)
if (e.NewStartingIndex < NextIndex)
{
RecycleContainers();
}

7
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -9,6 +9,7 @@ using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls.Primitives
{
@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
[CanBeNull]
public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
/// <summary>
@ -65,10 +67,7 @@ namespace Avalonia.Controls.Primitives
IVisual IHostedVisualTreeRoot.Host => Parent;
/// <inheritdoc/>
public void Dispose()
{
this.PlatformImpl.Dispose();
}
public void Dispose() => PlatformImpl?.Dispose();
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)

7
src/Avalonia.Controls/TextBox.cs

@ -720,7 +720,7 @@ namespace Avalonia.Controls
if (pos < text.Length)
{
--pos;
if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n')
if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n')
{
--pos;
}
@ -771,6 +771,9 @@ namespace Avalonia.Controls
private string GetSelection()
{
var text = Text;
if (string.IsNullOrEmpty(text))
return "";
var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd);
@ -779,7 +782,7 @@ namespace Avalonia.Controls
{
return "";
}
return Text.Substring(start, end - start);
return text.Substring(start, end - start);
}
private int GetLine(int caretIndex, IList<FormattedTextLine> lines)

35
src/Avalonia.Controls/TopLevel.cs

@ -14,6 +14,7 @@ using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls
{
@ -92,25 +93,25 @@ namespace Avalonia.Controls
var rendererFactory = TryGetService<IRendererFactory>(dependencyResolver);
Renderer = rendererFactory?.CreateRenderer(this, renderLoop);
PlatformImpl.SetInputRoot(this);
impl.SetInputRoot(this);
PlatformImpl.Closed = HandleClosed;
PlatformImpl.Input = HandleInput;
PlatformImpl.Paint = HandlePaint;
PlatformImpl.Resized = HandleResized;
PlatformImpl.ScalingChanged = HandleScalingChanged;
impl.Closed = HandleClosed;
impl.Input = HandleInput;
impl.Paint = HandlePaint;
impl.Resized = HandleResized;
impl.ScalingChanged = HandleScalingChanged;
_keyboardNavigationHandler?.SetOwner(this);
_accessKeyHandler?.SetOwner(this);
styler?.ApplyStyles(this);
ClientSize = PlatformImpl.ClientSize;
ClientSize = impl.ClientSize;
this.GetObservable(PointerOverElementProperty)
.Select(
x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
.Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor));
.Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
if (_applicationLifecycle != null)
{
@ -135,10 +136,8 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
public ITopLevelImpl PlatformImpl
{
get;
}
[CanBeNull]
public ITopLevelImpl PlatformImpl { get; private set; }
/// <summary>
/// Gets the renderer for the window.
@ -177,7 +176,7 @@ namespace Avalonia.Controls
Size ILayoutRoot.MaxClientSize => Size.Infinity;
/// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling;
double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
IStyleHost IStyleHost.StylingParent
{
@ -189,25 +188,27 @@ namespace Avalonia.Controls
/// <inheritdoc/>
protected virtual IRenderTarget CreateRenderTarget()
{
if(PlatformImpl == null)
throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)");
return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
}
/// <inheritdoc/>
void IRenderRoot.Invalidate(Rect rect)
{
PlatformImpl.Invalidate(rect);
PlatformImpl?.Invalidate(rect);
}
/// <inheritdoc/>
Point IRenderRoot.PointToClient(Point p)
{
return PlatformImpl.PointToClient(p);
return PlatformImpl?.PointToClient(p) ?? default(Point);
}
/// <inheritdoc/>
Point IRenderRoot.PointToScreen(Point p)
{
return PlatformImpl.PointToScreen(p);
return PlatformImpl?.PointToScreen(p) ?? default(Point);
}
/// <summary>
@ -224,6 +225,8 @@ namespace Avalonia.Controls
/// </summary>
protected virtual void HandleClosed()
{
PlatformImpl = null;
Closed?.Invoke(this, EventArgs.Empty);
Renderer?.Dispose();
Renderer = null;

28
src/Avalonia.Controls/Window.cs

@ -12,6 +12,7 @@ using Avalonia.Platform;
using Avalonia.Styling;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Avalonia.Controls
{
@ -87,11 +88,11 @@ namespace Avalonia.Controls
static Window()
{
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue));
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue));
HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
(s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue));
(s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
IconProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl));
}
/// <summary>
@ -109,7 +110,7 @@ namespace Avalonia.Controls
public Window(IWindowImpl impl)
: base(impl)
{
_maxPlatformClientSize = this.PlatformImpl.MaxClientSize;
_maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
}
/// <inheritdoc/>
@ -129,6 +130,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
[CanBeNull]
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
/// <summary>
@ -164,8 +166,12 @@ namespace Avalonia.Controls
/// </summary>
public WindowState WindowState
{
get { return this.PlatformImpl.WindowState; }
set { this.PlatformImpl.WindowState = value; }
get { return PlatformImpl?.WindowState ?? WindowState.Normal; }
set
{
if (PlatformImpl != null)
PlatformImpl.WindowState = value;
}
}
/// <summary>
@ -189,7 +195,7 @@ namespace Avalonia.Controls
public void Close()
{
s_windows.Remove(this);
PlatformImpl.Dispose();
PlatformImpl?.Dispose();
IsVisible = false;
}
@ -221,7 +227,7 @@ namespace Avalonia.Controls
{
using (BeginAutoSizing())
{
PlatformImpl.Hide();
PlatformImpl?.Hide();
}
IsVisible = false;
@ -240,7 +246,7 @@ namespace Avalonia.Controls
using (BeginAutoSizing())
{
PlatformImpl.Show();
PlatformImpl?.Show();
}
}
@ -278,7 +284,7 @@ namespace Avalonia.Controls
var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault();
SetIsEnabled(affectedWindows, false);
var modal = PlatformImpl.ShowDialog();
var modal = PlatformImpl?.ShowDialog();
var result = new TaskCompletionSource<TResult>();
Observable.FromEventPattern<EventHandler, EventArgs>(
@ -287,7 +293,7 @@ namespace Avalonia.Controls
.Take(1)
.Subscribe(_ =>
{
modal.Dispose();
modal?.Dispose();
SetIsEnabled(affectedWindows, true);
activated?.Activate();
result.SetResult((TResult)_dialogResult);

34
src/Avalonia.Controls/WindowBase.cs

@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Platform;
using JetBrains.Annotations;
namespace Avalonia.Controls
{
@ -43,10 +44,10 @@ namespace Avalonia.Controls
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
{
PlatformImpl.Activated = HandleActivated;
PlatformImpl.Deactivated = HandleDeactivated;
PlatformImpl.PositionChanged = HandlePositionChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x));
impl.Activated = HandleActivated;
impl.Deactivated = HandleDeactivated;
impl.PositionChanged = HandlePositionChanged;
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
}
/// <summary>
@ -64,6 +65,7 @@ namespace Avalonia.Controls
/// </summary>
public event EventHandler<PointEventArgs> PositionChanged;
[CanBeNull]
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
/// <summary>
@ -80,8 +82,12 @@ namespace Avalonia.Controls
/// </summary>
public Point Position
{
get { return PlatformImpl.Position; }
set { PlatformImpl.Position = value; }
get { return PlatformImpl?.Position ?? default(Point); }
set
{
if (PlatformImpl is IWindowBaseImpl impl)
impl.Position = value;
}
}
/// <summary>
@ -98,7 +104,7 @@ namespace Avalonia.Controls
/// </summary>
public void Activate()
{
PlatformImpl.Activate();
PlatformImpl?.Activate();
}
/// <summary>
@ -110,7 +116,7 @@ namespace Avalonia.Controls
try
{
PlatformImpl.Hide();
PlatformImpl?.Hide();
IsVisible = false;
}
finally
@ -131,7 +137,7 @@ namespace Avalonia.Controls
EnsureInitialized();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
PlatformImpl.Show();
PlatformImpl?.Show();
}
finally
{
@ -163,10 +169,10 @@ namespace Avalonia.Controls
{
using (BeginAutoSizing())
{
PlatformImpl.Resize(finalSize);
PlatformImpl?.Resize(finalSize);
}
return base.ArrangeOverride(PlatformImpl.ClientSize);
return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size));
}
/// <summary>
@ -174,7 +180,7 @@ namespace Avalonia.Controls
/// </summary>
protected void EnsureInitialized()
{
if (!this.IsInitialized)
if (!IsInitialized)
{
var init = (ISupportInitialize)this;
init.BeginInit();
@ -268,12 +274,12 @@ namespace Avalonia.Controls
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
/// </summary>
public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag();
public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag();
/// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler
/// </summary>
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge);
public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge);
}
}

4
src/Avalonia.DesignerSupport/DesignerAssist.cs

@ -75,7 +75,7 @@ namespace Avalonia.DesignerSupport
private static void SetScalingFactor(double factor)
{
PlatformManager.SetDesignerScalingFactor(factor);
s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize);
s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize);
}
static Window s_currentWindow;
@ -149,6 +149,8 @@ namespace Avalonia.DesignerSupport
s_currentWindow = window;
window.Show();
Design.ApplyDesignerProperties(window, control);
// ReSharper disable once PossibleNullReferenceException
// Always not null at this point
Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle);
Api.OnResize?.Invoke();
}

8
src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs

@ -82,7 +82,13 @@ namespace Avalonia
private void LoadAssembliesInDirectory()
{
foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll"))
var location = Assembly.GetEntryAssembly().Location;
if (string.IsNullOrWhiteSpace(location))
return;
var dir = new FileInfo(location).Directory;
if (dir == null)
return;
foreach (var file in dir.EnumerateFiles("*.dll"))
{
try
{

7
src/Avalonia.Layout/Layoutable.cs

@ -456,10 +456,9 @@ namespace Avalonia.Layout
ApplyTemplate();
var constrained = LayoutHelper
.ApplyLayoutConstraints(this, availableSize)
.Deflate(margin);
var constrained = LayoutHelper.ApplyLayoutConstraints(
this,
availableSize.Deflate(margin));
var measured = MeasureOverride(constrained);
var width = measured.Width;

2
src/Avalonia.Styling/Controls/NameScope.cs

@ -50,7 +50,7 @@ namespace Avalonia.Controls
return result;
}
visual = (visual as ILogical).LogicalParent as Visual;
visual = (visual as ILogical)?.LogicalParent as Visual;
}
return null;

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

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

2
src/Avalonia.Visuals/Rendering/ZIndexComparer.cs

@ -8,6 +8,6 @@ namespace Avalonia.Rendering
{
public static readonly ZIndexComparer Instance = new ZIndexComparer();
public int Compare(IVisual x, IVisual y) => x.ZIndex.CompareTo(y.ZIndex);
public int Compare(IVisual x, IVisual y) => (x?.ZIndex ?? 0).CompareTo(y?.ZIndex ?? 0);
}
}

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

@ -189,7 +189,7 @@ namespace Avalonia.VisualTree
{
Contract.Requires<ArgumentNullException>(visual != null);
return visual.VisualRoot as IRenderRoot ?? visual.VisualRoot;
return visual as IRenderRoot ?? visual.VisualRoot;
}
/// <summary>

1
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@ -9,6 +9,7 @@ using Avalonia.Cairo.Media.Imaging;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
// ReSharper disable PossibleNullReferenceException
namespace Avalonia.Cairo.Media
{

6
src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs

@ -40,6 +40,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]

6
src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs

@ -22,7 +22,7 @@ using System.Runtime.CompilerServices;
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]

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

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

5
src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs

@ -2,6 +2,8 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Avalonia.Gtk3;
using Avalonia.Platform;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@ -28,3 +30,6 @@ using System.Runtime.InteropServices;
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]

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

@ -28,6 +28,7 @@ namespace Avalonia.Gtk3
List<IDisposable> disposables = null;
Action dispose = () =>
{
// ReSharper disable once PossibleNullReferenceException
foreach (var d in disposables)
d.Dispose();
disposables.Clear();

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

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

2
src/Markup/Avalonia.Markup.Xaml/Parsers/SelectorParser.cs

@ -68,7 +68,7 @@ namespace Avalonia.Markup.Xaml.Parsers
}
else if (property != null)
{
var type = result.TargetType;
var type = result?.TargetType;
if (type == null)
{

4
src/Markup/Avalonia.Markup/Data/Plugins/TaskStreamPlugin.cs

@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins
if (task != null)
{
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result");
var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null)
{
@ -61,7 +61,7 @@ namespace Avalonia.Markup.Data.Plugins
protected IObservable<object> HandleCompleted(Task task)
{
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result");
var resultProperty = task.GetType().GetRuntimeProperty("Result");
if (resultProperty != null)
{

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

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

6
src/Shared/SharedAssemblyInfo.cs

@ -13,6 +13,6 @@ using System.Resources;
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.4.1")]
[assembly: AssemblyFileVersion("0.4.1")]
[assembly: AssemblyInformationalVersion("0.4.1")]
[assembly: AssemblyVersion("0.5.1")]
[assembly: AssemblyFileVersion("0.5.1")]
[assembly: AssemblyInformationalVersion("0.5.1")]

1
src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj

@ -40,7 +40,6 @@
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\SkiaSharp.props" />
<Import Project="..\..\..\build\SkiaSharp.Desktop.props" />
<Import Project="..\Avalonia.Skia\Avalonia.Skia.projitems" Label="Shared" />
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" />
</Project>

2
src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj

@ -50,7 +50,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

4
src/Skia/Avalonia.Skia.Desktop/Properties/AssemblyInfo.cs

@ -37,4 +37,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 2, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 1, "Skia", typeof(SkiaPlatform), nameof(SkiaPlatform.Initialize))]

4
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -285,7 +285,7 @@ namespace Avalonia.Skia
paint.StrokeCap = SKStrokeCap.Butt;
if (pen.LineJoin == PenLineJoin.Miter)
paint.StrokeJoin = SKStrokeJoin.Mitter;
paint.StrokeJoin = SKStrokeJoin.Miter;
else if (pen.LineJoin == PenLineJoin.Round)
paint.StrokeJoin = SKStrokeJoin.Round;
else
@ -397,7 +397,7 @@ namespace Avalonia.Skia
public void PopOpacityMask()
{
Canvas.SaveLayer(new SKPaint { XferMode = SKXferMode.DstIn });
Canvas.SaveLayer(new SKPaint { BlendMode = SKBlendMode.DstIn });
using (var paintWrapper = maskStack.Pop())
{
Canvas.DrawPaint(paintWrapper.Paint);

2
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -42,7 +42,7 @@ namespace Avalonia.Skia
_paint.Typeface = skiaTypeface;
_paint.TextSize = (float)(typeface?.FontSize ?? 12);
_paint.TextAlign = textAlignment.ToSKTextAlign();
_paint.XferMode = SKXferMode.Src;
_paint.BlendMode = SKBlendMode.Src;
_wrapping = wrapping;
_constraint = constraint;

4
src/Windows/Avalonia.Win32/Embedding/WinFormsAvaloniaControlHost.cs

@ -15,7 +15,7 @@ namespace Avalonia.Win32.Embedding
{
private readonly EmbeddableControlRoot _root = new EmbeddableControlRoot();
private IntPtr WindowHandle => ((WindowImpl) _root.PlatformImpl).Handle.Handle;
private IntPtr WindowHandle => ((WindowImpl) _root?.PlatformImpl)?.Handle?.Handle ?? IntPtr.Zero;
public WinFormsAvaloniaControlHost()
{
@ -25,6 +25,8 @@ namespace Avalonia.Win32.Embedding
if (_root.IsFocused)
FocusManager.Instance.Focus(null);
_root.GotFocus += RootGotFocus;
// ReSharper disable once PossibleNullReferenceException
// Always non-null at this point
_root.PlatformImpl.LostFocus += PlatformImpl_LostFocus;
FixPosition();
}

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

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

25
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -206,6 +206,30 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(expected, actual);
}
[Fact]
public void Inserting_Items_Before_Visibile_Containers_Should_Update_Containers()
{
var target = CreateTarget();
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
((ILogicalScrollable)target).Offset = new Vector(0, 5);
var expected = Enumerable.Range(5, 10).Select(x => $"Item {x}").ToList();
var items = (ObservableCollection<string>)target.Items;
var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
items.Insert(0, "Inserted");
expected = Enumerable.Range(4, 10).Select(x => $"Item {x}").ToList();
actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
}
[Fact]
public void Removing_First_Materialized_Item_Should_Update_Containers()
{
@ -477,7 +501,6 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Arrange(new Rect(0, 0, 100, 100));
var expected = Enumerable.Range(0, 6).Select(x => $"Item {x}").ToList();
var items = (ObservableCollection<string>)target.Items;
var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);

53
tests/Avalonia.Layout.UnitTests/MeasureTests.cs

@ -100,5 +100,58 @@ namespace Avalonia.Layout.UnitTests
Assert.Equal(0, target.DesiredSize.Height);
}
[Fact]
public void Margin_Should_Affect_AvailableSize()
{
MeasureTest target;
var outer = new Decorator
{
Width = 100,
Height = 100,
Child = target = new MeasureTest
{
Margin = new Thickness(10),
}
};
outer.Measure(Size.Infinity);
Assert.Equal(new Size(80, 80), target.AvailableSize);
}
[Fact]
public void Margin_Should_Be_Applied_Before_Width_Height()
{
MeasureTest target;
var outer = new Decorator
{
Width = 100,
Height = 100,
Child = target = new MeasureTest
{
Width = 80,
Height = 80,
Margin = new Thickness(10),
}
};
outer.Measure(Size.Infinity);
Assert.Equal(new Size(80, 80), target.AvailableSize);
}
class MeasureTest : Control
{
public Size? AvailableSize { get; private set; }
protected override Size MeasureOverride(Size availableSize)
{
AvailableSize = availableSize;
return availableSize;
}
}
}
}

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

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