diff --git a/.gitmodules b/.gitmodules index 0b2349ad42..bdd5d5ca4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,6 @@ path = src/Avalonia.HtmlRenderer/external url = https://github.com/AvaloniaUI/HTML-Renderer.git branch = perspex-pcl -[submodule "src/Markup/Avalonia.Markup.Xaml/OmniXAML"] - path = src/Markup/Avalonia.Markup.Xaml/OmniXAML - url = https://github.com/AvaloniaUI/OmniXAML.git -[submodule "src/Markup/Avalonia.Markup.Xaml/glass"] - path = src/Markup/Avalonia.Markup.Xaml/glass - url = https://github.com/SuperJMN/glass +[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"] + path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github + url = https://github.com/cwensley/Portable.Xaml.git diff --git a/Avalonia.sln b/Avalonia.sln index f12af02236..afaee6a907 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -191,6 +191,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.LinuxFramebuffer", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13 @@ -2589,6 +2591,46 @@ Global {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.Build.0 = Release|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.ActiveCfg = Release|Any CPU {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2649,5 +2691,6 @@ Global {4D6FAF79-58B4-482F-9122-0668C346364C} = {74487168-7D91-487E-BF93-055F2251461E} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} EndGlobalSection EndGlobal diff --git a/appveyor.yml b/appveyor.yml index 6b63176a89..7457a1d5bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ environment: secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package init: -- ps: (New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/appveyor/ci/master/scripts/xamarin-vs2017-151-fixed.targets', "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Microsoft.Common.Targets\ImportAfter\Xamarin.Common.targets") +- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")} install: - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi - if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe" @@ -26,7 +26,6 @@ before_build: build_script: - ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration" after_build: -- tools\JetBrains.dotMemoryUnit\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll" - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%" - pip install codecov - codecov -f "./artifacts/coverage.xml" diff --git a/build.cake b/build.cake index b3822271d4..c56089ab55 100644 --- a/build.cake +++ b/build.cake @@ -4,8 +4,8 @@ #addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=NuGet.Core&version=2.12.0" +#tool "nuget:?package=xunit.runner.console&version=2.2.0" #tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease" -#tool "nuget:?package=JetBrains.dotMemoryUnit&version=2.3.20160517.113140" #tool "JetBrains.ReSharper.CommandLineTools" /////////////////////////////////////////////////////////////////////////////// // TOOLS @@ -119,7 +119,6 @@ Task("Restore-NuGet-Packages") }}) .Execute(()=> { NuGetRestore(parameters.MSBuildSolution, new NuGetRestoreSettings { - ToolPath = "./tools/NuGet.CommandLine/tools/NuGet.exe", ToolTimeout = TimeSpan.FromMinutes(toolTimeout) }); }); @@ -194,6 +193,7 @@ Task("Run-Net-Core-Unit-Tests") Task("Run-Unit-Tests") .IsDependentOn("Run-Net-Core-Unit-Tests") .IsDependentOn("Build") + .IsDependentOn("Run-Leak-Tests") .WithCriteria(() => !parameters.SkipTests) .Does(() => { @@ -206,13 +206,6 @@ Task("Run-Unit-Tests") .Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + parameters.DirSuffix + "/" + name + ".dll"))) .ToList(); - if (parameters.IsRunningOnWindows) - { - var leakTests = GetFiles("./tests/Avalonia.LeakTests/bin/" + parameters.DirSuffix + "/*.LeakTests.dll"); - - unitTests.AddRange(leakTests); - } - var toolPath = (parameters.IsPlatformAnyCPU || parameters.IsPlatformX86) ? "./tools/xunit.runner.console/tools/xunit.console.x86.exe" : "./tools/xunit.runner.console/tools/xunit.console.exe"; @@ -365,6 +358,44 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); +Task("Run-Leak-Tests") + .WithCriteria(parameters.IsRunningOnWindows) + .IsDependentOn("Build") + .Does(() => + { + DotNetCoreRestore("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj"); + DotNetBuild("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj", settings => settings.SetConfiguration("Release")); + var report = "tests\\Avalonia.LeakTests\\bin\\Release\\report.xml"; + if(System.IO.File.Exists(report)) + System.IO.File.Delete(report); + var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName="tests\\Avalonia.LeakTests\\toolproject\\bin\\dotMemoryUnit.exe", + Arguments="-targetExecutable=\"tools\\xunit.runner.console\\tools\\xunit.console.x86.exe\" -returnTargetExitCode -- tests\\Avalonia.LeakTests\\bin\\Release\\Avalonia.LeakTests.dll -xml tests\\Avalonia.LeakTests\\bin\\Release\\report.xml ", + UseShellExecute = false, + }); + var st = System.Diagnostics.Stopwatch.StartNew(); + while(!proc.HasExited && !System.IO.File.Exists(report)) + { + if(st.Elapsed.TotalSeconds>60) + { + Error("Timed out, probably a bug in dotMemoryUnit"); + proc.Kill(); + throw new Exception("dotMemory issue"); + } + proc.WaitForExit(100); + } + try{ + proc.Kill(); + }catch{} + var doc = System.Xml.Linq.XDocument.Load(report); + if(doc.Root.Descendants("assembly").Any(x=>x.Attribute("failed").Value.ToString() != "0")) + { + throw new Exception("Tests failed"); + } + + }); + Task("Inspect") .WithCriteria(parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages") @@ -372,7 +403,8 @@ Task("Inspect") { var badIssues = new []{"PossibleNullReferenceException"}; var whitelist = new []{"tests", "src\\android", "src\\ios", - "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external"}; + "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external", + "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; Information("Running code inspections"); diff --git a/build/Base.props b/build/Base.props new file mode 100644 index 0000000000..6689465338 --- /dev/null +++ b/build/Base.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Markup.props b/build/Markup.props index 36c4cb28d8..0e4baf5da9 100644 --- a/build/Markup.props +++ b/build/Markup.props @@ -5,5 +5,9 @@ + + + + diff --git a/build/SharpDX.props b/build/SharpDX.props index de5dc9e926..b9864bdd42 100644 --- a/build/SharpDX.props +++ b/build/SharpDX.props @@ -4,5 +4,6 @@ + diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index 77407f9996..04e8a3ad4f 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,6 +1,6 @@  - + diff --git a/build/UnitTests.NetCore.targets b/build/UnitTests.NetCore.targets index a8886fe028..13bb4ed230 100644 --- a/build/UnitTests.NetCore.targets +++ b/build/UnitTests.NetCore.targets @@ -25,5 +25,4 @@ - \ No newline at end of file diff --git a/build/XUnit.props b/build/XUnit.props index 27e0afc987..d59cee6536 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -12,4 +12,16 @@ + + true + + + + + true + + diff --git a/packages.cake b/packages.cake index 1e8e356694..5f268188dc 100644 --- a/packages.cake +++ b/packages.cake @@ -8,6 +8,42 @@ public class Packages public string NugetPackagesDir {get; private set;} public string SkiaSharpVersion {get; private set; } public string SkiaSharpLinuxVersion {get; private set; } + public Dictionary>> PackageVersions{get; private set;} + + + + class DependencyBuilder : List + { + Packages _parent; + public DependencyBuilder(Packages parent) + { + _parent = parent; + } + + string GetVersion(string name) + { + return _parent.PackageVersions[name].First().Item1; + } + + + public DependencyBuilder Dep(string name, params string[] fws) + { + if(fws.Length == 0) + Add(new NuSpecDependency() { Id = name, Version = GetVersion(name) }); + foreach(var fw in fws) + Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) }); + return this; + } + public DependencyBuilder Deps(string[] fws, params string[] deps) + { + foreach(var fw in fws) + foreach(var name in deps) + Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) }); + return this; + } + } + + //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, public Packages(ICakeContext context, Parameters parameters) { // NUGET NUSPECS @@ -26,7 +62,7 @@ public class Packages // Key: Package Id // Value is Tuple where Item1: Package Version, Item2: The *.csproj/*.props file path. var packageVersions = new Dictionary>>(); - + PackageVersions = packageVersions; System.IO.Directory.EnumerateFiles(((DirectoryPath)context.Directory("./build")).FullPath, "*.props", SearchOption.AllDirectories).ToList().ForEach(fileName => { @@ -75,22 +111,26 @@ public class Packages var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1; var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; + var SystemValueTupleVersion = packageVersions["System.ValueTuple"].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; + var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1; var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1; context.Information("Package: Serilog, version: {0}", SerilogVersion); context.Information("Package: Splat, version: {0}", SplatVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); + context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion); 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); + context.Information("Package: SharpDX.Direct3D9, version: {0}", SharpDXDirect3D9Version); context.Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion); var nugetPackagesDir = System.Environment.GetEnvironmentVariable("NUGET_HOME") @@ -146,12 +186,12 @@ public class Packages }; var coreLibrariesFiles = coreLibraries.Select((lib) => { - return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.1/" + lib[1] + lib[2]); + return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]); }).ToList(); var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { return new NuSpecContent { - Source = file.FullPath, Target = "lib/netstandard1.1" + Source = file.FullPath, Target = "lib/netstandard1.3" }; }); @@ -191,7 +231,7 @@ public class Packages new NuGetPackSettings() { Id = "Avalonia", - Dependencies = new [] + Dependencies = new DependencyBuilder(this) { new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion }, new NuSpecDependency() { Id = "Splat", Version = SplatVersion }, @@ -204,8 +244,12 @@ public class Packages new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion }, new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion }, new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion }, - new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion } - }, + new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion }, + } + .Deps(new string[]{null, "netcoreapp1.0"}, + "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", + "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument") + .ToArray(), Files = coreLibrariesNuSpecContent .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) @@ -225,9 +269,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.1" } + new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" } }, - BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.1"), + BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot } }; @@ -341,7 +385,7 @@ public class Packages Files = new [] { new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" }, - new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.1/Avalonia.Win32.dll", Target = "lib/netstandard1.1" } + new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" } }, BasePath = context.Directory("./src/Windows"), OutputDirectory = parameters.NugetRoot @@ -396,9 +440,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.1" } + new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" } }, - BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.1"), + BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -454,9 +498,9 @@ public class Packages new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version }, new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version }, //.NET Core - new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.1", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.1", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.1", Version = parameters.Version } + new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version } }, Files = new NuSpecContent[] { @@ -465,6 +509,22 @@ public class Packages BasePath = context.Directory("./"), OutputDirectory = parameters.NugetRoot }, + new NuGetPackSettings() + { + Id = "Avalonia.Win32.Interoperability", + Dependencies = new [] + { + new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, + new NuSpecDependency() { Id = "SharpDX.Direct3D9", Version = SharpDXDirect3D9Version }, + }, + Files = new [] + { + new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } + }, + BasePath = context.Directory("./src/Windows"), + OutputDirectory = parameters.NugetRoot + }, /////////////////////////////////////////////////////////////////////////////// // Avalonia.LinuxFramebuffer /////////////////////////////////////////////////////////////////////////////// diff --git a/samples/BindingTest/BindingTest.csproj b/samples/BindingTest/BindingTest.csproj index f2db8d3f46..8e78b0c2cd 100644 --- a/samples/BindingTest/BindingTest.csproj +++ b/samples/BindingTest/BindingTest.csproj @@ -162,5 +162,4 @@ - \ No newline at end of file diff --git a/samples/BindingTest/MainWindow.xaml b/samples/BindingTest/MainWindow.xaml index 95f671fd84..7d633bcd0c 100644 --- a/samples/BindingTest/MainWindow.xaml +++ b/samples/BindingTest/MainWindow.xaml @@ -44,6 +44,8 @@ + + diff --git a/samples/BindingTest/ViewModels/MainWindowViewModel.cs b/samples/BindingTest/ViewModels/MainWindowViewModel.cs index 4b58bf2279..2ec052c258 100644 --- a/samples/BindingTest/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingTest/ViewModels/MainWindowViewModel.cs @@ -49,6 +49,9 @@ namespace BindingTest.ViewModels Thread.Sleep(1000); } }); + + CurrentTimeObservable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)) + .Select(x => DateTimeOffset.Now.ToString()); } public ObservableCollection Items { get; } @@ -85,6 +88,7 @@ namespace BindingTest.ViewModels private set { this.RaiseAndSetIfChanged(ref _currentTime, value); } } + public IObservable CurrentTimeObservable { get; } public ReactiveCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 4db67a8b60..71e3c4169c 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -158,5 +158,4 @@ - \ No newline at end of file diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 403ab7065b..a3940a3a7d 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -144,5 +144,4 @@ - \ No newline at end of file diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 01d287ea83..d0c73b2553 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -181,5 +181,4 @@ - \ No newline at end of file diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 2a9f8f70de..3f978fa7f4 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 False false diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index 98171f29d6..9c5591c849 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -31,5 +31,28 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj index ea876ba4f2..95f1479b6b 100644 --- a/samples/RenderTest/RenderTest.csproj +++ b/samples/RenderTest/RenderTest.csproj @@ -183,5 +183,4 @@ - \ No newline at end of file diff --git a/samples/VirtualizationTest/VirtualizationTest.csproj b/samples/VirtualizationTest/VirtualizationTest.csproj index 81046f4ef4..327e659966 100644 --- a/samples/VirtualizationTest/VirtualizationTest.csproj +++ b/samples/VirtualizationTest/VirtualizationTest.csproj @@ -158,5 +158,4 @@ - \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj index 465374a1a7..27c7ee29db 100644 --- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj +++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj @@ -31,5 +31,4 @@ - \ No newline at end of file diff --git a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj index d9a61064f1..56fa426f3c 100644 --- a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj +++ b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj @@ -9,7 +9,7 @@ Properties GtkInteropDemo GtkInteropDemo - v4.6 + v4.6.1 512 true @@ -151,5 +151,4 @@ - \ No newline at end of file diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 1115cf5768..1d8dc32a69 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -1,10 +1,12 @@  @@ -14,8 +16,18 @@ + + + + + + + + + + - + diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index e60c9ced0a..c7a23c22fc 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -11,7 +11,9 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Avalonia; using Avalonia.Controls; +using Avalonia.VisualTree; using ControlCatalog; using Window = System.Windows.Window; @@ -25,7 +27,18 @@ namespace WindowsInteropTest public EmbedToWpfDemo() { InitializeComponent(); - Host.Content = new MainView(); + var view = new MainView(); + view.AttachedToVisualTree += delegate + { + ((TopLevel) view.GetVisualRoot()).AttachDevTools(); + }; + Host.Content = view; + var btn = (Avalonia.Controls.Button) RightBtn.Content; + btn.Click += delegate + { + btn.Content += "!"; + }; + } } } diff --git a/samples/interop/WindowsInteropTest/Program.cs b/samples/interop/WindowsInteropTest/Program.cs index 4770688ecf..fac06d74b0 100644 --- a/samples/interop/WindowsInteropTest/Program.cs +++ b/samples/interop/WindowsInteropTest/Program.cs @@ -15,7 +15,7 @@ namespace WindowsInteropTest { System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); - AppBuilder.Configure().UseWin32().UseSkia().SetupWithoutStarting(); + AppBuilder.Configure().UseWin32().UseDirect2D1().SetupWithoutStarting(); System.Windows.Forms.Application.Run(new SelectorForm()); } } diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index ac7d25a91e..2fa3ba30b9 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -9,12 +9,12 @@ Properties WindowsInteropTest WindowsInteropTest - v4.6 + v4.6.1 512 true - AnyCPU + x86 true full false @@ -164,6 +164,10 @@ {3e908f67-5543-4879-a1dc-08eace79b3cd} Avalonia.Direct2D1 + + {cbc4ff2f-92d4-420b-be21-9fe0b930b04e} + Avalonia.Win32.Interop + {811a76cf-1cf6-440f-963b-bbe31bd72a82} Avalonia.Win32 @@ -182,5 +186,4 @@ - \ No newline at end of file diff --git a/scripts/ReplaceNugetCache.ps1 b/scripts/ReplaceNugetCache.ps1 new file mode 100644 index 0000000000..854442eb09 --- /dev/null +++ b/scripts/ReplaceNugetCache.ps1 @@ -0,0 +1,5 @@ +copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.1\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.1\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.1\ diff --git a/scripts/ReplaceNugetCache.sh b/scripts/ReplaceNugetCache.sh new file mode 100755 index 0000000000..2ce3e7648d --- /dev/null +++ b/scripts/ReplaceNugetCache.sh @@ -0,0 +1,7 @@ + #!/usr/bin/env bash + + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp1.0/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard1.1/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard1.1/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.skia.desktop/$1/lib/netstandard1.3/ + diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj index 4babdc8bff..091fdcc4b5 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -133,8 +133,4 @@ - - - $(MSBuildToolsPath)\Roslyn - \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index 826f1f0132..87d1f67986 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -157,5 +157,4 @@ - \ No newline at end of file diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index f02ec2f31c..8f832dabcf 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 95be67c98c..e0d12ce262 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false @@ -30,6 +30,7 @@ Properties\SharedAssemblyInfo.cs + \ No newline at end of file diff --git a/src/Avalonia.Base/Metadata/AmbientAttribute.cs b/src/Avalonia.Base/Metadata/AmbientAttribute.cs new file mode 100644 index 0000000000..db36953300 --- /dev/null +++ b/src/Avalonia.Base/Metadata/AmbientAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Metadata +{ + /// + /// Defines the ambient class/property + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, Inherited = true)] + public class AmbientAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Metadata/TemplateContent.cs b/src/Avalonia.Base/Metadata/TemplateContent.cs new file mode 100644 index 0000000000..ecc16b04a2 --- /dev/null +++ b/src/Avalonia.Base/Metadata/TemplateContent.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Metadata +{ + /// + /// Defines the property that contains the object's content in markup. + /// + [AttributeUsage(AttributeTargets.Property)] + public class TemplateContentAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 37f348340c..037546b186 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 2b3bbc8ad2..78bbd836be 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -207,7 +207,11 @@ namespace Avalonia.Controls /// The event args. protected virtual void OnClick(RoutedEventArgs e) { - Command?.Execute(CommandParameter); + if (Command != null) + { + Command.Execute(CommandParameter); + e.Handled = true; + } } /// @@ -215,13 +219,16 @@ namespace Avalonia.Controls { base.OnPointerPressed(e); - PseudoClasses.Add(":pressed"); - e.Device.Capture(this); - e.Handled = true; - - if (ClickMode == ClickMode.Press) + if (e.MouseButton == MouseButton.Left) { - RaiseClickEvent(); + PseudoClasses.Add(":pressed"); + e.Device.Capture(this); + e.Handled = true; + + if (ClickMode == ClickMode.Press) + { + RaiseClickEvent(); + } } } @@ -230,13 +237,16 @@ namespace Avalonia.Controls { base.OnPointerReleased(e); - e.Device.Capture(null); - PseudoClasses.Remove(":pressed"); - e.Handled = true; - - if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) + if (e.MouseButton == MouseButton.Left) { - RaiseClickEvent(); + e.Device.Capture(null); + PseudoClasses.Remove(":pressed"); + e.Handled = true; + + if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) + { + RaiseClickEvent(); + } } } diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 0a69a5277f..fdb04f4ade 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -19,7 +19,7 @@ namespace Avalonia.Controls { ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); - MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick); + MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick, handledEventsToo: true); } /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 83a76cb1a7..eca5967a58 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -355,18 +355,28 @@ namespace Avalonia.Controls if (--_initCount == 0 && _isAttachedToLogicalTree) { - if (!_styled) - { - RegisterWithNameScope(); - ApplyStyling(); - _styled = true; - } + InitializeStylesIfNeeded(); - if (!IsInitialized) - { - IsInitialized = true; - Initialized?.Invoke(this, EventArgs.Empty); - } + InitializeIfNeeded(); + } + } + + private void InitializeStylesIfNeeded(bool force = false) + { + if (_initCount == 0 && (!_styled || force)) + { + RegisterWithNameScope(); + ApplyStyling(); + _styled = true; + } + } + + private void InitializeIfNeeded() + { + if (_initCount == 0 && !IsInitialized) + { + IsInitialized = true; + Initialized?.Invoke(this, EventArgs.Empty); } } @@ -580,11 +590,7 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTreeCore(e); - if (!IsInitialized) - { - IsInitialized = true; - Initialized?.Invoke(this, EventArgs.Empty); - } + InitializeIfNeeded(); } /// @@ -752,12 +758,7 @@ namespace Avalonia.Controls { _isAttachedToLogicalTree = true; - if (_initCount == 0) - { - RegisterWithNameScope(); - ApplyStyling(); - _styled = true; - } + InitializeStylesIfNeeded(true); OnAttachedToLogicalTree(e); } diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index b8d54fa67b..179dccaf76 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -22,6 +22,8 @@ namespace Avalonia.Controls.Embedding [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; + protected bool EnforceClientSize { get; set; } = true; + public void Prepare() { EnsureInitialized(); @@ -38,12 +40,12 @@ namespace Avalonia.Controls.Embedding init.EndInit(); } } - + protected override Size MeasureOverride(Size availableSize) { - var cs = PlatformImpl?.ClientSize ?? default(Size); - base.MeasureOverride(cs); - return cs; + if (EnforceClientSize) + availableSize = PlatformImpl?.ClientSize ?? default(Size); + return base.MeasureOverride(availableSize); } private readonly NameScope _nameScope = new NameScope(); diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs index e919275d4f..994af9dab8 100644 --- a/src/Avalonia.Controls/Menu.cs +++ b/src/Avalonia.Controls/Menu.cs @@ -47,7 +47,7 @@ namespace Avalonia.Controls static Menu() { ItemsPanelProperty.OverrideDefaultValue(typeof(Menu), DefaultPanel); - MenuItem.ClickEvent.AddClassHandler(x => x.OnMenuClick); + MenuItem.ClickEvent.AddClassHandler(x => x.OnMenuClick, handledEventsToo: true); MenuItem.SubmenuOpenedEvent.AddClassHandler(x => x.OnSubmenuOpened); } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 3d15ed99e7..3d66fbc51b 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -102,6 +102,11 @@ namespace Avalonia.Controls AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler(x => x.AccessKeyPressed); } + public MenuItem() + { + + } + /// /// Occurs when a without a submenu is clicked. /// @@ -192,7 +197,11 @@ namespace Avalonia.Controls /// The click event args. protected virtual void OnClick(RoutedEventArgs e) { - Command?.Execute(CommandParameter); + if (Command != null) + { + Command.Execute(CommandParameter); + e.Handled = true; + } } /// diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 793399841c..3272d3779b 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -64,9 +64,12 @@ namespace Avalonia.Controls { Contract.Requires(value != null); - VisualChildren.Clear(); - _children.Clear(); - _children.AddRange(value); + if (_children != value) + { + VisualChildren.Clear(); + _children.Clear(); + _children.AddRange(value); + } } } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 40fc2f302c..c1adff402a 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Templates; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters { @@ -88,6 +89,7 @@ namespace Avalonia.Controls.Presenters static ContentPresenter() { ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); + ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); } @@ -313,27 +315,22 @@ namespace Avalonia.Controls.Presenters if (content != null && newChild == null) { - // We have content and it isn't a control, so first try to recycle the existing - // child control to display the new data by querying if the template that created - // the child can recycle items and that it also matches the new data. - if (oldChild != null && - _dataTemplate != null && - _dataTemplate.SupportsRecycling && - _dataTemplate.Match(content)) + var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; + + // We have content and it isn't a control, so if the new data template is the same + // as the old data template, try to recycle the existing child control to display + // the new data. + if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling) { newChild = oldChild; } else { - // We couldn't recycle an existing control so find a data template for the data - // and use it to create a control. - _dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; + _dataTemplate = dataTemplate; newChild = _dataTemplate.Build(content); - // Try to give the new control its own name scope. - var controlResult = newChild as Control; - - if (controlResult != null) + // Give the new control its own name scope. + if (newChild is Control controlResult) { NameScope.SetNameScope(controlResult, new NameScope()); } @@ -424,6 +421,19 @@ namespace Avalonia.Controls.Presenters private void ContentChanged(AvaloniaPropertyChangedEventArgs e) { _createdChild = false; + + if (((ILogical)this).IsAttachedToLogicalTree) + { + UpdateChild(); + } + else if (Child != null) + { + VisualChildren.Remove(Child); + LogicalChildren.Remove(Child); + Child = null; + _dataTemplate = null; + } + InvalidateMeasure(); } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index d2e8085d8c..92ab12f82e 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -236,6 +236,11 @@ namespace Avalonia.Controls { _presenter = e.NameScope.Get("PART_TextPresenter"); _presenter.Cursor = new Cursor(StandardCursorType.Ibeam); + + if(IsFocused) + { + _presenter.ShowCaret(); + } } protected override void OnGotFocus(GotFocusEventArgs e) @@ -254,7 +259,7 @@ namespace Avalonia.Controls } else { - _presenter.ShowCaret(); + _presenter?.ShowCaret(); } } @@ -263,7 +268,7 @@ namespace Avalonia.Controls base.OnLostFocus(e); SelectionStart = 0; SelectionEnd = 0; - _presenter.HideCaret(); + _presenter?.HideCaret(); } protected override void OnTextInput(TextInputEventArgs e) diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index 22bc589a36..e1b69637af 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -106,24 +106,28 @@ namespace Avalonia.Controls if (control != null && control.IsVisible && control.GetVisualRoot() != null) { var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control); - var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22); - if (s_popup == null) + if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value)) { - s_popup = new PopupRoot(); - s_popup.Content = new ToolTip(); - } - else - { - ((ISetLogicalParent)s_popup).SetParent(null); - } + var position = control.PointToScreen(cp.Value) + new Vector(0, 22); + + if (s_popup == null) + { + s_popup = new PopupRoot(); + s_popup.Content = new ToolTip(); + } + else + { + ((ISetLogicalParent)s_popup).SetParent(null); + } ((ISetLogicalParent)s_popup).SetParent(control); - ((ToolTip)s_popup.Content).Content = GetTip(control); - s_popup.Position = position; - s_popup.Show(); + ((ToolTip)s_popup.Content).Content = GetTip(control); + s_popup.Position = position; + s_popup.Show(); - s_current = control; + s_current = control; + } } } diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index b966d09b1f..5d1b9a1462 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -16,7 +16,7 @@ namespace Avalonia.Controls /// /// Displays a hierachical tree of data. /// - public class TreeView : ItemsControl + public class TreeView : ItemsControl, ICustomKeyboardNavigation { /// /// Defines the property. @@ -90,6 +90,26 @@ namespace Avalonia.Controls } } + (bool handled, IInputElement next) ICustomKeyboardNavigation.GetNext(IInputElement element, NavigationDirection direction) + { + if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) + { + if (!this.IsVisualAncestorOf(element)) + { + IControl result = _selectedItem != null ? + ItemContainerGenerator.Index.ContainerFromItem(_selectedItem) : + ItemContainerGenerator.ContainerFromIndex(0); + return (true, result); + } + else + { + return (true, null); + } + } + + return (false, null); + } + /// protected override IItemContainerGenerator CreateItemContainerGenerator() { diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 1f484fd6cb..fbdf64b14a 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -29,6 +29,7 @@ namespace Avalonia.Controls public static readonly DirectProperty IsActiveProperty = AvaloniaProperty.RegisterDirect(nameof(IsActive), o => o.IsActive); + private bool _hasExecutedInitialLayoutPass; private bool _isActive; private bool _ignoreVisibilityChange; @@ -136,7 +137,13 @@ namespace Avalonia.Controls { EnsureInitialized(); IsVisible = true; - LayoutManager.Instance.ExecuteInitialLayoutPass(this); + + if (!_hasExecutedInitialLayoutPass) + { + LayoutManager.Instance.ExecuteInitialLayoutPass(this); + _hasExecutedInitialLayoutPass = true; + } + PlatformImpl?.Show(); } finally diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index be0b98b24c..a68e8760f2 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.DesignerSupport/DesignerAssist.cs b/src/Avalonia.DesignerSupport/DesignerAssist.cs index 8d30f3cf25..2f5fc79147 100644 --- a/src/Avalonia.DesignerSupport/DesignerAssist.cs +++ b/src/Avalonia.DesignerSupport/DesignerAssist.cs @@ -5,8 +5,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using OmniXaml; -using OmniXaml.ObjectAssembler; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Markup.Xaml; diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index e4752f6662..be3f397283 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index b735372b59..6593a8cd42 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -14,11 +14,11 @@ using Avalonia.VisualTree; namespace Avalonia { - public static class WindowExtensions + public static class DevToolsExtensions { - public static void AttachDevTools(this Window window) + public static void AttachDevTools(this TopLevel control) { - Avalonia.Diagnostics.DevTools.Attach(window); + Avalonia.Diagnostics.DevTools.Attach(control); } } } @@ -27,7 +27,7 @@ namespace Avalonia.Diagnostics { public class DevTools : UserControl { - private static Dictionary s_open = new Dictionary(); + private static Dictionary s_open = new Dictionary(); private IDisposable _keySubscription; public DevTools(IControl root) @@ -43,9 +43,9 @@ namespace Avalonia.Diagnostics public IControl Root { get; } - public static IDisposable Attach(Window window) + public static IDisposable Attach(TopLevel control) { - return window.AddHandler( + return control.AddHandler( KeyDownEvent, WindowPreviewKeyDown, RoutingStrategies.Tunnel); @@ -55,16 +55,16 @@ namespace Avalonia.Diagnostics { if (e.Key == Key.F12) { - var window = (Window)sender; + var control = (TopLevel)sender; var devToolsWindow = default(Window); - if (s_open.TryGetValue(window, out devToolsWindow)) + if (s_open.TryGetValue(control, out devToolsWindow)) { devToolsWindow.Activate(); } else { - var devTools = new DevTools(window); + var devTools = new DevTools(control); devToolsWindow = new Window { @@ -78,7 +78,7 @@ namespace Avalonia.Diagnostics }; devToolsWindow.Closed += devTools.DevToolsClosed; - s_open.Add((Window)sender, devToolsWindow); + s_open.Add(control, devToolsWindow); devToolsWindow.Show(); } } @@ -88,9 +88,7 @@ namespace Avalonia.Diagnostics { var devToolsWindow = (Window)sender; var devTools = (DevTools)devToolsWindow.Content; - var window = (Window)devTools.Root; - - s_open.Remove(window); + s_open.Remove((TopLevel)devTools.Root); _keySubscription.Dispose(); devToolsWindow.Closed -= DevToolsClosed; } diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj index b475c4b3ff..2fbcba40c8 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj +++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj @@ -9,7 +9,7 @@ Properties Avalonia.DotNetFrameworkRuntime Avalonia.DotNetFrameworkRuntime - v4.5 + v4.6.1 512 @@ -69,5 +69,4 @@ - \ No newline at end of file diff --git a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj index b1fc7a4278..f715217e42 100644 --- a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj +++ b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 False False false diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj index e9e74e24fe..b5482ebce1 100644 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ b/src/Avalonia.Input/Avalonia.Input.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs index e5cc5a8557..102da6efc4 100644 --- a/src/Avalonia.Input/FocusManager.cs +++ b/src/Avalonia.Input/FocusManager.cs @@ -176,9 +176,10 @@ namespace Avalonia.Input /// The event args. private void OnPreviewPointerPressed(object sender, RoutedEventArgs e) { - if (sender == e.Source) + var ev = (PointerPressedEventArgs)e; + + if (sender == e.Source && ev.MouseButton == MouseButton.Left) { - var ev = (PointerPressedEventArgs)e; var element = (ev.Device?.Captured as IInputElement) ?? (e.Source as IInputElement); if (element == null || !CanFocus(element)) diff --git a/src/Avalonia.Input/ICustomKeyboardNavigation.cs b/src/Avalonia.Input/ICustomKeyboardNavigation.cs new file mode 100644 index 0000000000..de5f98e04b --- /dev/null +++ b/src/Avalonia.Input/ICustomKeyboardNavigation.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Input +{ + /// + /// Designates a control as handling its own keyboard navigation. + /// + public interface ICustomKeyboardNavigation + { + (bool handled, IInputElement next) GetNext(IInputElement element, NavigationDirection direction); + } +} diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs index 57da49fa03..bf2b61d08b 100644 --- a/src/Avalonia.Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using Avalonia.Input.Navigation; +using Avalonia.VisualTree; namespace Avalonia.Input { @@ -52,6 +54,31 @@ namespace Avalonia.Input { Contract.Requires(element != null); + var customHandler = element.GetSelfAndVisualAncestors() + .OfType() + .FirstOrDefault(); + + if (customHandler != null) + { + var (handled, next) = customHandler.GetNext(element, direction); + + if (handled) + { + if (next != null) + { + return next; + } + else if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) + { + return TabNavigation.GetNextInTabOrder((IInputElement)customHandler, direction, true); + } + else + { + return null; + } + } + } + if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) { return TabNavigation.GetNextInTabOrder(element, direction); diff --git a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs index a88ed1e8aa..75cb3a39e8 100644 --- a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs +++ b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs @@ -41,7 +41,7 @@ namespace Avalonia.Input.Navigation { case KeyboardNavigationMode.Continue: return GetNextInContainer(element, container, direction) ?? - GetFirstInNextContainer(element, direction); + GetFirstInNextContainer(element, element, direction); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container, direction) ?? GetFocusableDescendant(container, direction); @@ -173,10 +173,12 @@ namespace Avalonia.Input.Navigation /// /// Gets the first item that should be focused in the next container. /// + /// The element being navigated away from. /// The container. /// The direction of the search. /// The first element, or null if there are no more elements. private static IInputElement GetFirstInNextContainer( + IInputElement element, IInputElement container, NavigationDirection direction) { @@ -200,6 +202,16 @@ namespace Avalonia.Input.Navigation if (sibling != null) { + if (sibling is ICustomKeyboardNavigation custom) + { + var (handled, customNext) = custom.GetNext(element, direction); + + if (handled) + { + return customNext; + } + } + if (sibling.CanFocus()) { next = sibling; @@ -214,7 +226,7 @@ namespace Avalonia.Input.Navigation if (next == null) { - next = GetFirstInNextContainer(parent, direction); + next = GetFirstInNextContainer(element, parent, direction); } } else diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Input/Navigation/TabNavigation.cs index 6ba7ab1a0c..6e077e887f 100644 --- a/src/Avalonia.Input/Navigation/TabNavigation.cs +++ b/src/Avalonia.Input/Navigation/TabNavigation.cs @@ -18,13 +18,17 @@ namespace Avalonia.Input.Navigation /// /// The element. /// The tab direction. Must be Next or Previous. + /// + /// If true will not descend into to find next control. + /// /// /// The next element in the specified direction, or null if /// was the last in the requested direction. /// public static IInputElement GetNextInTabOrder( IInputElement element, - NavigationDirection direction) + NavigationDirection direction, + bool outsideElement = false) { Contract.Requires(element != null); Contract.Requires( @@ -40,20 +44,20 @@ namespace Avalonia.Input.Navigation switch (mode) { case KeyboardNavigationMode.Continue: - return GetNextInContainer(element, container, direction) ?? - GetFirstInNextContainer(element, direction); + return GetNextInContainer(element, container, direction, outsideElement) ?? + GetFirstInNextContainer(element, element, direction); case KeyboardNavigationMode.Cycle: - return GetNextInContainer(element, container, direction) ?? + return GetNextInContainer(element, container, direction, outsideElement) ?? GetFocusableDescendant(container, direction); case KeyboardNavigationMode.Contained: - return GetNextInContainer(element, container, direction); + return GetNextInContainer(element, container, direction, outsideElement); default: - return GetFirstInNextContainer(container, direction); + return GetFirstInNextContainer(element, container, direction); } } else { - return GetFocusableDescendants(element).FirstOrDefault(); + return GetFocusableDescendants(element, direction).FirstOrDefault(); } } @@ -66,16 +70,17 @@ namespace Avalonia.Input.Navigation private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction) { return direction == NavigationDirection.Next ? - GetFocusableDescendants(container).FirstOrDefault() : - GetFocusableDescendants(container).LastOrDefault(); + GetFocusableDescendants(container, direction).FirstOrDefault() : + GetFocusableDescendants(container, direction).LastOrDefault(); } /// /// Gets the focusable descendants of the specified element. /// /// The element. + /// The tab direction. Must be Next or Previous. /// The element's focusable descendants. - private static IEnumerable GetFocusableDescendants(IInputElement element) + private static IEnumerable GetFocusableDescendants(IInputElement element, NavigationDirection direction) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)element); @@ -103,16 +108,25 @@ namespace Avalonia.Input.Navigation foreach (var child in children) { - if (child.CanFocus()) + var customNext = GetCustomNext(child, direction); + + if (customNext.handled) { - yield return child; + yield return customNext.next; } - - if (child.CanFocusDescendants()) + else { - foreach (var descendant in GetFocusableDescendants(child)) + if (child.CanFocus()) { - yield return descendant; + yield return child; + } + + if (child.CanFocusDescendants()) + { + foreach (var descendant in GetFocusableDescendants(child, direction)) + { + yield return descendant; + } } } } @@ -124,15 +138,19 @@ namespace Avalonia.Input.Navigation /// The starting element/ /// The container. /// The direction. + /// + /// If true will not descend into to find next control. + /// /// The next element, or null if the element is the last. private static IInputElement GetNextInContainer( IInputElement element, IInputElement container, - NavigationDirection direction) + NavigationDirection direction, + bool outsideElement) { - if (direction == NavigationDirection.Next) + if (direction == NavigationDirection.Next && !outsideElement) { - var descendant = GetFocusableDescendants(element).FirstOrDefault(); + var descendant = GetFocusableDescendants(element, direction).FirstOrDefault(); if (descendant != null) { @@ -167,7 +185,7 @@ namespace Avalonia.Input.Navigation if (element != null && direction == NavigationDirection.Previous) { - var descendant = GetFocusableDescendants(element).LastOrDefault(); + var descendant = GetFocusableDescendants(element, direction).LastOrDefault(); if (descendant != null) { @@ -184,10 +202,12 @@ namespace Avalonia.Input.Navigation /// /// Gets the first item that should be focused in the next container. /// + /// The element being navigated away from. /// The container. /// The direction of the search. /// The first element, or null if there are no more elements. private static IInputElement GetFirstInNextContainer( + IInputElement element, IInputElement container, NavigationDirection direction) { @@ -210,6 +230,13 @@ namespace Avalonia.Input.Navigation if (sibling != null) { + var customNext = GetCustomNext(sibling, direction); + + if (customNext.handled) + { + return customNext.next; + } + if (sibling.CanFocus()) { next = sibling; @@ -217,24 +244,34 @@ namespace Avalonia.Input.Navigation else { next = direction == NavigationDirection.Next ? - GetFocusableDescendants(sibling).FirstOrDefault() : - GetFocusableDescendants(sibling).LastOrDefault(); + GetFocusableDescendants(sibling, direction).FirstOrDefault() : + GetFocusableDescendants(sibling, direction).LastOrDefault(); } } if (next == null) { - next = GetFirstInNextContainer(parent, direction); + next = GetFirstInNextContainer(element, parent, direction); } } else { next = direction == NavigationDirection.Next ? - GetFocusableDescendants(container).FirstOrDefault() : - GetFocusableDescendants(container).LastOrDefault(); + GetFocusableDescendants(container, direction).FirstOrDefault() : + GetFocusableDescendants(container, direction).LastOrDefault(); } return next; } + + private static (bool handled, IInputElement next) GetCustomNext(IInputElement element, NavigationDirection direction) + { + if (element is ICustomKeyboardNavigation custom) + { + return custom.GetNext(element, direction); + } + + return (false, null); + } } } diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index ab9ab88a37..9d22de86b3 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index 45a40d2fd4..d0260391d8 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs new file mode 100644 index 0000000000..24f0ccd82e --- /dev/null +++ b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Layout +{ + /// + /// A special layout root with enforced size for Arrange pass + /// + public interface IEmbeddedLayoutRoot : ILayoutRoot + { + Size AllocatedSize { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index 965ab3eee6..3244f5e7dc 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -186,12 +186,14 @@ namespace Avalonia.Layout if (!control.IsArrangeValid && control.IsAttachedToVisualTree) { - if (control is ILayoutRoot root) - { - root.Arrange(new Rect(control.DesiredSize)); - } - else + if (control is IEmbeddedLayoutRoot embeddedRoot) + control.Arrange(new Rect(embeddedRoot.AllocatedSize)); + else if (control is ILayoutRoot root) + control.Arrange(new Rect(root.DesiredSize)); + else if (control.PreviousArrange != null) { + // Has been observed that PreviousArrange sometimes is null, probably a bug somewhere else. + // Condition observed: control.VisualParent is Scrollbar, control is Border. control.Arrange(control.PreviousArrange.Value); } } diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index dad00d93d4..523c720e2f 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -367,6 +367,14 @@ namespace Avalonia.Layout } } + + /// + /// Called by InvalidateMeasure + /// + protected virtual void OnMeasureInvalidated() + { + } + /// /// Invalidates the measurement of the control and queues a new layout pass. /// @@ -384,6 +392,7 @@ namespace Avalonia.Layout LayoutManager.Instance?.InvalidateMeasure(this); InvalidateVisual(); } + OnMeasureInvalidated(); } } diff --git a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj index 9aa81c19f9..508ede7f7d 100644 --- a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj +++ b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 28522d8849..2d66b62eab 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 False false diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index 5965645391..6bf37b522b 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index be4282cdc0..3dfd9118af 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -13,10 +13,10 @@ namespace Avalonia.Styling /// public class Style : IStyle { - private static Dictionary> _applied = + private static Dictionary> _applied = new Dictionary>(); - private Dictionary _resources; + private StyleResources _resources; /// /// Initializes a new instance of the class. @@ -37,13 +37,13 @@ namespace Avalonia.Styling /// /// Gets or sets a dictionary of style resources. /// - public IDictionary Resources + public StyleResources Resources { get { if (_resources == null) { - _resources = new Dictionary(); + _resources = new StyleResources(); } return _resources; @@ -51,25 +51,30 @@ namespace Avalonia.Styling set { + var resources = Resources; - - foreach (var i in value) + if (!Equals(resources, value)) { - resources.Add(i); + foreach (var i in value) + { + resources[i.Key] = i.Value; + //resources.Add(i.Key, i.Value); + //(resources as IDictionary).Add(i); + } } } } /// - /// Gets or sets style's selector. + /// Gets or sets the style's selector. /// public Selector Selector { get; set; } /// - /// Gets or sets style's setters. + /// Gets or sets the style's setters. /// [Content] - public IEnumerable Setters { get; set; } = new List(); + public IList Setters { get; set; } = new List(); /// /// Attaches the style to a control if the style's selector matches. diff --git a/src/Avalonia.Styling/Styling/StyleResources.cs b/src/Avalonia.Styling/Styling/StyleResources.cs new file mode 100644 index 0000000000..e447c6adfd --- /dev/null +++ b/src/Avalonia.Styling/Styling/StyleResources.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Avalonia.Styling +{ + /// + /// Holds resources for a . + /// + public class StyleResources : IDictionary, IDictionary + { + private Dictionary _inner = new Dictionary(); + + public object this[string key] + { + get { return _inner[key]; } + set { _inner[key] = value; } + } + + public int Count => _inner.Count; + + ICollection IDictionary.Keys => _inner.Keys; + + ICollection IDictionary.Values => _inner.Values; + + bool ICollection>.IsReadOnly => false; + + object IDictionary.this[object key] + { + get { return ((IDictionary)_inner)[key]; } + set { ((IDictionary)_inner)[key] = value; } + } + + ICollection IDictionary.Keys => _inner.Keys; + + ICollection IDictionary.Values => _inner.Values; + + bool ICollection.IsSynchronized => false; + + object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot; + + bool IDictionary.IsFixedSize => false; + + bool IDictionary.IsReadOnly => false; + + public void Add(string key, object value) => _inner.Add(key, value); + + public void Clear() => _inner.Clear(); + + public bool ContainsKey(string key) => _inner.ContainsKey(key); + + public bool Remove(string key) => _inner.Remove(key); + + public IEnumerator> GetEnumerator() => _inner.GetEnumerator(); + + public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value); + + bool ICollection>.Contains(KeyValuePair item) + { + return ((IDictionary)_inner).Contains(item); + } + + void ICollection>.Add(KeyValuePair item) + { + ((IDictionary)_inner).Add(item); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_inner).CopyTo(array, arrayIndex); + } + + bool ICollection>.Remove(KeyValuePair item) + { + return ((IDictionary)_inner).Remove(item); + } + + void ICollection.CopyTo(Array array, int index) => ((IDictionary)_inner).CopyTo(array, index); + + IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator(); + + IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator(); + + void IDictionary.Add(object key, object value) => ((IDictionary)_inner).Add(key, value); + + bool IDictionary.Contains(object key) => ((IDictionary)_inner).Contains(key); + + void IDictionary.Remove(object key) => ((IDictionary)_inner).Remove(key); + } +} diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index be1d798e2d..4e980680d9 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index dd9a795937..127760d8ac 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 false Avalonia diff --git a/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj b/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj index cc6684f622..d09fd2ddc6 100644 --- a/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj +++ b/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj @@ -9,7 +9,7 @@ Properties Avalonia.Cairo Avalonia.Cairo - v4.5 + v4.6.1 512 @@ -103,5 +103,4 @@ - \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj index 837b83bdd7..b51377f29c 100644 --- a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj +++ b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj @@ -7,7 +7,7 @@ Library Avalonia.Gtk Avalonia.Gtk - v4.5 + v4.6.1 @@ -98,5 +98,4 @@ - \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 1874d92f4d..d292e99b30 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -1,6 +1,6 @@  - netstandard1.1 + netstandard1.3 False false diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 9f38861b07..fb1a9955e3 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -500,6 +500,24 @@ namespace Avalonia.Gtk3.Interop public gdouble delta_y; } + [StructLayout(LayoutKind.Sequential)] + unsafe struct GdkEventCrossing + { + public GdkEventType type; + public IntPtr window; + public gint8 send_event; + public IntPtr subwindow; + public guint32 time; + public gdouble x; + public gdouble y; + public gdouble x_root; + public gdouble y_root; + public int mode; + public int detail; + public bool focus; + public GdkModifierType state; + }; + [StructLayout(LayoutKind.Sequential)] unsafe struct GdkEventWindowState { diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs index 00130346e8..39304940d2 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs @@ -45,6 +45,7 @@ namespace Avalonia.Gtk3 ConnectEvent("window-state-event", OnStateChanged); ConnectEvent("key-press-event", OnKeyEvent); ConnectEvent("key-release-event", OnKeyEvent); + ConnectEvent("leave-notify-event", OnLeaveNotifyEvent); Connect("destroy", OnDestroy); Native.GtkWidgetRealize(gtkWidget); _lastSize = ClientSize; @@ -194,6 +195,18 @@ namespace Avalonia.Gtk3 return true; } + private unsafe bool OnLeaveNotifyEvent(IntPtr w, IntPtr pev, IntPtr userData) + { + var evnt = (GdkEventCrossing*) pev; + var position = new Point(evnt->x, evnt->y); + Input(new RawMouseEventArgs(Gtk3Platform.Mouse, + evnt->time, + _inputRoot, + RawMouseEventType.Move, + position, GetModifierKeys(evnt->state))); + return true; + } + private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata) { Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string))); diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 036639ee54..dbf985fd79 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -1,273 +1,98 @@  - netstandard1.1 + netstandard1.3 False false false - + true full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;NETSTANDARD1_3;PCL;NETSTANDARD prompt 4 CS1591 - + pdbonly true bin\Release\ - TRACE + NETSTANDARD1_3;PCL;NETSTANDARD prompt 4 bin\Release\Avalonia.Markup.Xaml.XML CS1591 - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -279,11 +104,6 @@ - - - - - diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index 7b3d0036ef..3142d954ff 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs @@ -1,225 +1,11 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using OmniXaml; -using Avalonia.Platform; -using Avalonia.Markup.Xaml.Context; -using Avalonia.Markup.Xaml.Styling; -using OmniXaml.ObjectAssembler; -using Avalonia.Controls; -using Avalonia.Markup.Xaml.Data; - -namespace Avalonia.Markup.Xaml +namespace Avalonia.Markup.Xaml { - /// - /// Loads XAML for a avalonia application. - /// - public class AvaloniaXamlLoader : XmlLoader + public class AvaloniaXamlLoader : AvaloniaXamlLoaderPortableXaml { - private static AvaloniaParserFactory s_parserFactory; - private static IInstanceLifeCycleListener s_lifeCycleListener = new AvaloniaLifeCycleListener(); - private static Stack s_uriStack = new Stack(); - - /// - /// Initializes a new instance of the class. - /// - public AvaloniaXamlLoader() - : this(GetParserFactory()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The parser factory to use. - public AvaloniaXamlLoader(IParserFactory xamlParserFactory) - : base(xamlParserFactory) - { - } - - /// - /// Gets the URI of the XAML file currently being loaded. - /// - /// - /// TODO: Making this internal for now as I'm not sure that this is the correct - /// thing to do, but its needed by to get the URL of - /// the currently loading XAML file, as we can't use the OmniXAML parsing context - /// there. Maybe we need a way to inject OmniXAML context into the objects its - /// constructing? - /// - internal static Uri UriContext => s_uriStack.Count > 0 ? s_uriStack.Peek() : null; - - /// - /// Loads the XAML into a Avalonia component. - /// - /// The object to load the XAML into. - public static void Load(object obj) - { - Contract.Requires(obj != null); - - var loader = new AvaloniaXamlLoader(); - loader.Load(obj.GetType(), obj); - } - - /// - /// Loads the XAML for a type. - /// - /// The type. - /// - /// The optional instance into which the XAML should be loaded. - /// - /// The loaded object. - public object Load(Type type, object rootInstance = null) - { - Contract.Requires(type != null); - - // HACK: Currently Visual Studio is forcing us to change the extension of xaml files - // in certain situations, so we try to load .xaml and if that's not found we try .xaml. - // Ideally we'd be able to use .xaml everywhere - var assetLocator = AvaloniaLocator.Current.GetService(); - - if (assetLocator == null) - { - throw new InvalidOperationException( - "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); - } - - foreach (var uri in GetUrisFor(type)) - { - if (assetLocator.Exists(uri)) - { - using (var stream = assetLocator.Open(uri)) - { - var initialize = rootInstance as ISupportInitialize; - initialize?.BeginInit(); - return Load(stream, rootInstance, uri); - } - } - } - - throw new FileNotFoundException("Unable to find view for " + type.FullName); - } - - /// - /// Loads XAML from a URI. - /// - /// The URI of the XAML file. - /// - /// A base URI to use if is relative. - /// - /// - /// The optional instance into which the XAML should be loaded. - /// - /// The loaded object. - public object Load(Uri uri, Uri baseUri = null, object rootInstance = null) - { - Contract.Requires(uri != null); - - var assetLocator = AvaloniaLocator.Current.GetService(); - - if (assetLocator == null) - { - throw new InvalidOperationException( - "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); - } - - using (var stream = assetLocator.Open(uri, baseUri)) - { - return Load(stream, rootInstance, uri); - } - } - - /// - /// Loads XAML from a string. - /// - /// The string containing the XAML. - /// - /// The optional instance into which the XAML should be loaded. - /// - /// The loaded object. - public object Load(string xaml, object rootInstance = null) - { - Contract.Requires(xaml != null); - - using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) - { - return Load(stream, rootInstance); - } - } - - /// - /// Loads XAML from a stream. - /// - /// The stream containing the XAML. - /// - /// The optional instance into which the XAML should be loaded. - /// - /// The URI of the XAML - /// The loaded object. - public object Load(Stream stream, object rootInstance = null, Uri uri = null) - { - try - { - if (uri != null) - { - s_uriStack.Push(uri); - } - - var result = base.Load(stream, new Settings - { - RootInstance = rootInstance, - InstanceLifeCycleListener = s_lifeCycleListener, - ParsingContext = new Dictionary - { - { "Uri", uri } - } - }); - - var topLevel = result as TopLevel; - - if (topLevel != null) - { - DelayedBinding.ApplyBindings(topLevel); - } - - return result; - } - finally - { - if (uri != null) - { - s_uriStack.Pop(); - } - } - } - - private static AvaloniaParserFactory GetParserFactory() - { - if (s_parserFactory == null) - { - s_parserFactory = new AvaloniaParserFactory(); - } - - return s_parserFactory; - } - - /// - /// Gets the URI for a type. - /// - /// The type. - /// The URI. - private static IEnumerable GetUrisFor(Type type) - { - var asm = type.GetTypeInfo().Assembly.GetName().Name; - var typeName = type.FullName; - yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm); - yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm); + public static object Parse(string xaml) + => new AvaloniaXamlLoader().Load(xaml); - } + public static T Parse(string xaml) + => (T)Parse(xaml); } -} +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs new file mode 100644 index 0000000000..c49797f2f8 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs @@ -0,0 +1,215 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Controls; +using Avalonia.Markup.Xaml.Data; +using Avalonia.Markup.Xaml.PortableXaml; +using Avalonia.Platform; +using Portable.Xaml; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace Avalonia.Markup.Xaml +{ + /// + /// Loads XAML for a avalonia application. + /// + public class AvaloniaXamlLoaderPortableXaml + { + private readonly AvaloniaXamlSchemaContext _context = GetContext(); + + private static AvaloniaXamlSchemaContext GetContext() + { + var result = AvaloniaLocator.Current.GetService(); + + if (result == null) + { + result = AvaloniaXamlSchemaContext.Create(); + + AvaloniaLocator.CurrentMutable + .Bind() + .ToConstant(result); + } + + return result; + } + + /// + /// Initializes a new instance of the class. + /// + public AvaloniaXamlLoaderPortableXaml() + { + } + + /// + /// Loads the XAML into a Avalonia component. + /// + /// The object to load the XAML into. + public static void Load(object obj) + { + Contract.Requires(obj != null); + + var loader = new AvaloniaXamlLoader(); + loader.Load(obj.GetType(), obj); + } + + /// + /// Loads the XAML for a type. + /// + /// The type. + /// + /// The optional instance into which the XAML should be loaded. + /// + /// The loaded object. + public object Load(Type type, object rootInstance = null) + { + Contract.Requires(type != null); + + // HACK: Currently Visual Studio is forcing us to change the extension of xaml files + // in certain situations, so we try to load .xaml and if that's not found we try .xaml. + // Ideally we'd be able to use .xaml everywhere + var assetLocator = AvaloniaLocator.Current.GetService(); + + if (assetLocator == null) + { + throw new InvalidOperationException( + "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); + } + + foreach (var uri in GetUrisFor(type)) + { + if (assetLocator.Exists(uri)) + { + using (var stream = assetLocator.Open(uri)) + { + var initialize = rootInstance as ISupportInitialize; + initialize?.BeginInit(); + try + { + return Load(stream, rootInstance, uri); + } + finally + { + initialize?.EndInit(); + } + } + } + } + + throw new FileNotFoundException("Unable to find view for " + type.FullName); + } + + /// + /// Loads XAML from a URI. + /// + /// The URI of the XAML file. + /// + /// A base URI to use if is relative. + /// + /// + /// The optional instance into which the XAML should be loaded. + /// + /// The loaded object. + public object Load(Uri uri, Uri baseUri = null, object rootInstance = null) + { + Contract.Requires(uri != null); + + var assetLocator = AvaloniaLocator.Current.GetService(); + + if (assetLocator == null) + { + throw new InvalidOperationException( + "Could not create IAssetLoader : maybe Application.RegisterServices() wasn't called?"); + } + + using (var stream = assetLocator.Open(uri, baseUri)) + { + return Load(stream, rootInstance, uri); + } + } + + /// + /// Loads XAML from a string. + /// + /// The string containing the XAML. + /// + /// The optional instance into which the XAML should be loaded. + /// + /// The loaded object. + public object Load(string xaml, object rootInstance = null) + { + Contract.Requires(xaml != null); + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml))) + { + return Load(stream, rootInstance); + } + } + + /// + /// Loads XAML from a stream. + /// + /// The stream containing the XAML. + /// + /// The optional instance into which the XAML should be loaded. + /// + /// The URI of the XAML + /// The loaded object. + public object Load(Stream stream, object rootInstance = null, Uri uri = null) + { + var readerSettings = new XamlXmlReaderSettings() + { + BaseUri = uri, + LocalAssembly = rootInstance?.GetType().GetTypeInfo().Assembly + }; + + var reader = new XamlXmlReader(stream, _context, readerSettings); + + object result = LoadFromReader( + reader, + AvaloniaXamlContext.For(readerSettings, rootInstance)); + + var topLevel = result as TopLevel; + + if (topLevel != null) + { + DelayedBinding.ApplyBindings(topLevel); + } + + return result; + } + + internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null) + { + var writer = AvaloniaXamlObjectWriter.Create( + reader.SchemaContext, + context); + + XamlServices.Transform(reader, writer); + + return writer.Result; + } + + internal static object LoadFromReader(XamlReader reader) + { + //return XamlServices.Load(reader); + return LoadFromReader(reader, null); + } + + /// + /// Gets the URI for a type. + /// + /// The type. + /// The URI. + private static IEnumerable GetUrisFor(Type type) + { + var asm = type.GetTypeInfo().Assembly.GetName().Name; + var typeName = type.FullName; + yield return new Uri("resm:" + typeName + ".xaml?assembly=" + asm); + yield return new Uri("resm:" + typeName + ".paml?assembly=" + asm); + } + } +} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaAttachableXamlMember.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaAttachableXamlMember.cs deleted file mode 100644 index be8aa8eb9f..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaAttachableXamlMember.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) The Avalonia Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using Avalonia.Markup.Xaml.Data; -using OmniXaml; -using OmniXaml.Typing; -using System.Reflection; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaAttachableXamlMember : AttachableMember - { - public AvaloniaAttachableXamlMember(string name, - XamlType owner, - MethodInfo getter, - MethodInfo setter, - ITypeRepository xamlTypeRepository, - ITypeFeatureProvider featureProvider) - : base(name, getter, setter, xamlTypeRepository, featureProvider) - { - } - - public override string ToString() - { - return "Avalonia Attachable XAML Member " + base.ToString(); - } - - protected override IMemberValuePlugin LookupXamlMemberValueConnector() - { - return new AvaloniaMemberValuePlugin(this); - } - } -} \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaContentPropertyProvider.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaContentPropertyProvider.cs deleted file mode 100644 index 164d948e10..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaContentPropertyProvider.cs +++ /dev/null @@ -1,58 +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; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Glass; -using OmniXaml; -using OmniXaml.Builder; -using Avalonia.Metadata; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaContentPropertyProvider : IContentPropertyProvider - { - private readonly Dictionary _values = new Dictionary(); - - public string GetContentPropertyName(Type type) - { - string result; - - if (!_values.TryGetValue(type, out result)) - { - result = LookupContentProperty(type); - _values[type] = result; - } - - return result; - } - - private string LookupContentProperty(Type type) - { - var result = (from member in type.GetRuntimeProperties() - let att = member.GetCustomAttribute() - where att != null - select member).FirstOrDefault(); - - return result?.Name; - } - - void IAdd.Add(ContentPropertyDefinition item) - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaLifeCycleListener.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaLifeCycleListener.cs deleted file mode 100644 index db5a19b484..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaLifeCycleListener.cs +++ /dev/null @@ -1,31 +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; -using OmniXaml; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaLifeCycleListener : IInstanceLifeCycleListener - { - public void OnAfterProperties(object instance) - { - } - - public void OnAssociatedToParent(object instance) - { - } - - public void OnBegin(object instance) - { - var isi = instance as ISupportInitialize; - isi?.BeginInit(); - } - - public void OnEnd(object instance) - { - var isi = instance as ISupportInitialize; - isi?.EndInit(); - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaMemberValuePlugin.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaMemberValuePlugin.cs deleted file mode 100644 index 0494e9d1fe..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaMemberValuePlugin.cs +++ /dev/null @@ -1,25 +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; -using OmniXaml.TypeConversion; -using OmniXaml.Typing; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaMemberValuePlugin : MemberValuePlugin - { - private readonly MutableMember _xamlMember; - - public AvaloniaMemberValuePlugin(MutableMember xamlMember) - : base(xamlMember) - { - _xamlMember = xamlMember; - } - - public override void SetValue(object instance, object value, IValueContext valueContext) - { - PropertyAccessor.SetValue(instance, _xamlMember, value, valueContext); - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaNamespaceRegistry.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaNamespaceRegistry.cs deleted file mode 100644 index b591654621..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaNamespaceRegistry.cs +++ /dev/null @@ -1,181 +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; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using OmniXaml.Builder; -using OmniXaml.Typing; -using Avalonia.Controls; -using Avalonia.Markup.Xaml.Templates; -using Avalonia.Media; -using Avalonia.Metadata; -using Avalonia.Platform; -using Avalonia.Styling; -using Glass.Core; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaNamespaceRegistry : INamespaceRegistry - { - private const string ClrNamespace = "clr-namespace:"; - private const string AvaloniaNs = "https://github.com/avaloniaui"; - - private static readonly IEnumerable ForcedAssemblies = new[] - { - typeof(AvaloniaObject).GetTypeInfo().Assembly, - typeof(Control).GetTypeInfo().Assembly, - typeof(Style).GetTypeInfo().Assembly, - typeof(DataTemplate).GetTypeInfo().Assembly, - typeof(SolidColorBrush).GetTypeInfo().Assembly, - typeof(IValueConverter).GetTypeInfo().Assembly, - }; - - private List _clrNamespaces = new List(); - private List _namespaces = new List(); - private Dictionary _prefixes = new Dictionary(); - private List _scanned = new List(); - - public AvaloniaNamespaceRegistry() - { - ScanAssemblies(ForcedAssemblies); - ScanNewAssemblies(); - RegisterPrefix(new PrefixRegistration(string.Empty, AvaloniaNs)); - } - - public IEnumerable RegisteredPrefixes => - _prefixes.Select(x => new PrefixRegistration(x.Key, x.Value)); - - public void AddNamespace(XamlNamespace xamlNamespace) - { - _namespaces.Add(xamlNamespace); - } - - public Namespace GetNamespace(string name) - { - Namespace result; - - if (!IsClrNamespace(name)) - { - ScanNewAssemblies(); - result = _namespaces.FirstOrDefault(x => x.Name == name); - - if (result == null) - { - result = _namespaces.FirstOrDefault(x => x.Name == name); - } - } - else - { - var nsAndAssembly = ParseClrNameSpace(name); - - result = _clrNamespaces.FirstOrDefault(x => - x.Name == nsAndAssembly.Item1 && - x.Assembly.GetName().Name == nsAndAssembly.Item2); - - if (result == null) - { - var clr = CreateClrNamespace(name); - _clrNamespaces.Add(clr); - result = clr; - } - } - - return result; - } - - public Namespace GetNamespaceByPrefix(string prefix) - { - string uri; - - if (_prefixes.TryGetValue(prefix, out uri)) - { - return GetNamespace(uri); - } - - return null; - } - - public void RegisterPrefix(PrefixRegistration prefixRegistration) - { - _prefixes[prefixRegistration.Prefix] = prefixRegistration.Ns; - } - - private static bool IsClrNamespace(string ns) - { - return ns.StartsWith(ClrNamespace); - } - - private static ClrNamespace CreateClrNamespace(string formattedClrString) - { - var nsAndAssembly = ParseClrNameSpace(formattedClrString); - var assembly = GetAssembly(nsAndAssembly.Item2); - - return new ClrNamespace(assembly, nsAndAssembly.Item1); - } - - private static Tuple ParseClrNameSpace(string clrNamespace) - { - var startOfNamespace = clrNamespace.IndexOf(":", StringComparison.Ordinal) + 1; - var endOfNamespace = clrNamespace.IndexOf(";", startOfNamespace, StringComparison.Ordinal); - - if (endOfNamespace < 0) - { - endOfNamespace = clrNamespace.Length - startOfNamespace; - } - - var ns = clrNamespace.Substring(startOfNamespace, endOfNamespace - startOfNamespace); - - var remainingPartStart = startOfNamespace + ns.Length + 1; - var remainingPartLenght = clrNamespace.Length - remainingPartStart; - var assemblyPart = clrNamespace.Substring(remainingPartStart, remainingPartLenght); - - return Tuple.Create(ns, assemblyPart.Dicotomize('=').Item2); - } - - private static Assembly GetAssembly(string assemblyName) - { - return Assembly.Load(new AssemblyName(assemblyName)); - } - - private void ScanAssemblies(IEnumerable assemblies) - { - foreach (var assembly in assemblies) - { - var namespaces = assembly.GetCustomAttributes() - .Select(x => new { x.XmlNamespace, x.ClrNamespace }) - .GroupBy(x => x.XmlNamespace); - - foreach (var nsa in namespaces) - { - var xamlNamespace = _namespaces.FirstOrDefault(x => x.Name == nsa.Key); - - if (xamlNamespace == null) - { - xamlNamespace = new XamlNamespace(nsa.Key); - _namespaces.Add(xamlNamespace); - } - - var clrNamespaces = nsa.Select(x => x.ClrNamespace); - xamlNamespace.Addresses.Add(new ConfiguredAssemblyWithNamespaces(assembly, clrNamespaces)); - } - - _scanned.Add(assembly); - } - } - - private void ScanNewAssemblies() - { - IEnumerable assemblies = AvaloniaLocator.Current - .GetService() - ?.GetLoadedAssemblies(); - - if (assemblies != null) - { - assemblies = assemblies.Except(_scanned); - ScanAssemblies(assemblies); - } - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs b/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs deleted file mode 100644 index 1f5d259fc3..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/Context/AvaloniaObjectAssembler.cs +++ /dev/null @@ -1,79 +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; -using OmniXaml; -using OmniXaml.ObjectAssembler; -using OmniXaml.ObjectAssembler.Commands; -using OmniXaml.TypeConversion; -using Avalonia.Markup.Xaml.Templates; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace Avalonia.Markup.Xaml.Context -{ - public class AvaloniaObjectAssembler : IObjectAssembler - { - private readonly TemplateHostingObjectAssembler objectAssembler; - private readonly ObjectAssembler assembler; - - public AvaloniaObjectAssembler( - IRuntimeTypeSource typeSource, - ITopDownValueContext topDownValueContext, - Settings settings = null) - { - var mapping = new DeferredLoaderMapping(); - mapping.Map