diff --git a/Avalonia.sln b/Avalonia.sln index 9cf93e8a84..54f6f5e7e7 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -105,9 +105,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}" EndProject @@ -141,7 +141,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props build\NetCore.props = build\NetCore.props - build\NetFX.props = build\NetFX.props build\ReactiveUI.props = build\ReactiveUI.props build\Rx.props = build\Rx.props build\SampleApp.props = build\SampleApp.props diff --git a/build.cake b/build.cake index 1c796e8594..bf3ae41b58 100644 --- a/build.cake +++ b/build.cake @@ -10,7 +10,8 @@ // TOOLS /////////////////////////////////////////////////////////////////////////////// -#tool "nuget:?package=xunit.runner.console&version=2.3.0-beta5-build3769" +#tool "nuget:?package=xunit.runner.console&version=2.3.1" +#tool "nuget:?package=JetBrains.dotMemoryUnit&version=3.0.20171219.105559" /////////////////////////////////////////////////////////////////////////////// // USINGS @@ -34,20 +35,31 @@ using NuGet; // PARAMETERS ////////////////////////////////////////////////////////////////////// -Parameters parameters = new Parameters(Context); -Packages packages = new Packages(Context, parameters); +class AvaloniaBuildData +{ + public AvaloniaBuildData(Parameters parameters, Packages packages) + { + Parameters = parameters; + Packages = packages; + } + + public Parameters Parameters { get; } + public Packages Packages { get; } +} /////////////////////////////////////////////////////////////////////////////// // SETUP /////////////////////////////////////////////////////////////////////////////// -Setup(context => +Setup(context => { - Information("Building version {0} of Avalonia ({1}, {2}, {3}) using version {4} of Cake.", + var parameters = new Parameters(context); + var buildContext = new AvaloniaBuildData(parameters, new Packages(context, parameters)); + + Information("Building version {0} of Avalonia ({1}, {2}) using version {3} of Cake.", parameters.Version, parameters.Platform, parameters.Configuration, - parameters.Target, typeof(ICakeContext).Assembly.GetName().Version.ToString()); if (parameters.IsRunningOnAppVeyor) @@ -55,8 +67,7 @@ Setup(context => Information("Repository Name: " + BuildSystem.AppVeyor.Environment.Repository.Name); Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch); } - - Information("Target: " + parameters.Target); + Information("Target:" + context.TargetTask.Name); Information("Platform: " + parameters.Platform); Information("Configuration: " + parameters.Configuration); Information("IsLocalBuild: " + parameters.IsLocalBuild); @@ -70,13 +81,15 @@ Setup(context => Information("IsReleasable: " + parameters.IsReleasable); Information("IsMyGetRelease: " + parameters.IsMyGetRelease); Information("IsNuGetRelease: " + parameters.IsNuGetRelease); + + return buildContext; }); /////////////////////////////////////////////////////////////////////////////// // TEARDOWN /////////////////////////////////////////////////////////////////////////////// -Teardown(context => +Teardown((context, buildContext) => { Information("Finished running tasks."); }); @@ -86,19 +99,19 @@ Teardown(context => /////////////////////////////////////////////////////////////////////////////// Task("Clean") - .Does(() => + .Does(data => { - CleanDirectories(parameters.BuildDirs); - CleanDirectory(parameters.ArtifactsDir); - CleanDirectory(parameters.NugetRoot); - CleanDirectory(parameters.ZipRoot); - CleanDirectory(parameters.BinRoot); + CleanDirectories(data.Parameters.BuildDirs); + CleanDirectory(data.Parameters.ArtifactsDir); + CleanDirectory(data.Parameters.NugetRoot); + CleanDirectory(data.Parameters.ZipRoot); + CleanDirectory(data.Parameters.BinRoot); }); Task("Restore-NuGet-Packages") .IsDependentOn("Clean") - .WithCriteria(parameters.IsRunningOnWindows) - .Does(() => + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .Does(data => { var maxRetryCount = 5; var toolTimeout = 2d; @@ -115,13 +128,13 @@ Task("Restore-NuGet-Packages") toolTimeout+=0.5; }}) .Execute(()=> { - NuGetRestore(parameters.MSBuildSolution, new NuGetRestoreSettings { + NuGetRestore(data.Parameters.MSBuildSolution, new NuGetRestoreSettings { ToolTimeout = TimeSpan.FromMinutes(toolTimeout) }); }); }); -void DotNetCoreBuild() +void DotNetCoreBuild(Parameters parameters) { var settings = new DotNetCoreBuildSettings { @@ -137,23 +150,24 @@ void DotNetCoreBuild() Task("Build") .IsDependentOn("Restore-NuGet-Packages") - .Does(() => + .Does(data => { - if(parameters.IsRunningOnWindows) + if(data.Parameters.IsRunningOnWindows) { - MSBuild(parameters.MSBuildSolution, settings => { - settings.SetConfiguration(parameters.Configuration); + MSBuild(data.Parameters.MSBuildSolution, settings => { + settings.SetConfiguration(data.Parameters.Configuration); settings.SetVerbosity(Verbosity.Minimal); - settings.WithProperty("Platform", "\"" + parameters.Platform + "\""); + settings.WithProperty("Platform", "\"" + data.Parameters.Platform + "\""); settings.WithProperty("UseRoslynPathHack", "true"); settings.UseToolVersion(MSBuildToolVersion.VS2017); settings.WithProperty("Windows", "True"); settings.SetNodeReuse(false); + settings.SetMaxCpuCount(0); }); } else { - DotNetCoreBuild(); + DotNetCoreBuild(data.Parameters); } }); @@ -182,68 +196,94 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) Task("Run-Unit-Tests") .IsDependentOn("Build") - .IsDependentOn("Run-Designer-Tests") - .IsDependentOn("Run-Render-Tests") - .WithCriteria(() => !parameters.SkipTests) - .Does(() => { - RunCoreTest("./tests/Avalonia.Base.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Controls.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Input.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Layout.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Markup.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Styling.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Visuals.UnitTests", parameters, false); - if (parameters.IsRunningOnWindows) + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .Does(data => { + RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Input.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Layout.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); + if (data.Parameters.IsRunningOnWindows) { - RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", parameters, true); + RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); } }); Task("Run-Designer-Tests") .IsDependentOn("Build") - .WithCriteria(() => !parameters.SkipTests) - .Does(() => { - RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", parameters, false); + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .Does(data => { + RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); }); Task("Run-Render-Tests") .IsDependentOn("Build") - .WithCriteria(() => !parameters.SkipTests && parameters.IsRunningOnWindows) - .Does(() => { - RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true); - RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true); + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .Does(data => { + RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); + RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); }); -Task("Copy-Files") - .IsDependentOn("Run-Unit-Tests") +Task("Run-Leak-Tests") + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .IsDependentOn("Build") .Does(() => + { + var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); + var leakTestsExitCode = StartProcess(dotMemoryUnit, new ProcessSettings + { + Arguments = new ProcessArgumentBuilder() + .Append(Context.Tools.Resolve("xunit.console.x86.exe").FullPath) + .Append("--propagate-exit-code") + .Append("--") + .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"), + Timeout = 120000 + }); + + if (leakTestsExitCode != 0) + { + throw new Exception("Leak Tests failed"); + } + }); + +Task("Run-Tests") + .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Render-Tests") + .IsDependentOn("Run-Designer-Tests") + .IsDependentOn("Run-Leak-Tests"); + +Task("Copy-Files") + .IsDependentOn("Run-Tests") + .Does(data => { - CopyFiles(packages.BinFiles, parameters.BinRoot); + CopyFiles(data.Packages.BinFiles, data.Parameters.BinRoot); }); Task("Zip-Files") .IsDependentOn("Copy-Files") - .Does(() => + .Does(data => { - Zip(parameters.BinRoot, parameters.ZipCoreArtifacts); - - Zip(parameters.ZipSourceControlCatalogDesktopDirs, - parameters.ZipTargetControlCatalogDesktopDirs, - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); + Zip(data.Parameters.BinRoot, data.Parameters.ZipCoreArtifacts); + + Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, + data.Parameters.ZipTargetControlCatalogDesktopDirs, + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); }); Task("Create-NuGet-Packages") - .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Tests") .IsDependentOn("Inspect") - .Does(() => + .Does(data => { - foreach(var nuspec in packages.NuspecNuGetSettings) + foreach(var nuspec in data.Packages.NuspecNuGetSettings) { NuGetPack(nuspec); } @@ -251,12 +291,12 @@ Task("Create-NuGet-Packages") Task("Publish-MyGet") .IsDependentOn("Create-NuGet-Packages") - .WithCriteria(() => !parameters.IsLocalBuild) - .WithCriteria(() => !parameters.IsPullRequest) - .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsMasterBranch) - .WithCriteria(() => parameters.IsMyGetRelease) - .Does(() => + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsMasterBranch) + .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) + .Does(data => { var apiKey = EnvironmentVariable("MYGET_API_KEY"); if(string.IsNullOrEmpty(apiKey)) @@ -270,7 +310,7 @@ Task("Publish-MyGet") throw new InvalidOperationException("Could not resolve MyGet API url."); } - foreach(var nupkg in packages.NugetPackages) + foreach(var nupkg in data.Packages.NugetPackages) { NuGetPush(nupkg, new NuGetPushSettings { Source = apiUrl, @@ -285,11 +325,11 @@ Task("Publish-MyGet") Task("Publish-NuGet") .IsDependentOn("Create-NuGet-Packages") - .WithCriteria(() => !parameters.IsLocalBuild) - .WithCriteria(() => !parameters.IsPullRequest) - .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsNuGetRelease) - .Does(() => + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) + .Does(data => { var apiKey = EnvironmentVariable("NUGET_API_KEY"); if(string.IsNullOrEmpty(apiKey)) @@ -303,7 +343,7 @@ Task("Publish-NuGet") throw new InvalidOperationException("Could not resolve NuGet API url."); } - foreach(var nupkg in packages.NugetPackages) + foreach(var nupkg in data.Packages.NugetPackages) { NuGetPush(nupkg, new NuGetPushSettings { ApiKey = apiKey, @@ -316,48 +356,8 @@ 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 toolXunitConsoleX86 = Context.Tools.Resolve("xunit.console.x86.exe").FullPath; - var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo - { - FileName="tests\\Avalonia.LeakTests\\toolproject\\bin\\dotMemoryUnit.exe", - Arguments="-targetExecutable=\"" + toolXunitConsoleX86 + "\" -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) + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages") .Does(() => { @@ -366,8 +366,13 @@ Task("Inspect") "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; Information("Running code inspections"); - StartProcess(Context.Tools.Resolve("inspectcode.exe"), - new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"), + new ProcessSettings + { + Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln", + RedirectStandardOutput = true + }); + Information("Analyzing report"); var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); var failBuild = false; @@ -395,23 +400,23 @@ Task("Inspect") Task("Package") .IsDependentOn("Create-NuGet-Packages"); -Task("Default").Does(() => -{ - if(parameters.IsRunningOnWindows) - RunTarget("Package"); - else - RunTarget("Run-Unit-Tests"); -}); Task("AppVeyor") .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") .IsDependentOn("Publish-NuGet"); Task("Travis") - .IsDependentOn("Run-Unit-Tests"); + .IsDependentOn("Run-Tests"); /////////////////////////////////////////////////////////////////////////////// // EXECUTE /////////////////////////////////////////////////////////////////////////////// -RunTarget(parameters.Target); +var target = Context.Argument("target", "Default"); + +if (target == "Default") +{ + target = Context.IsRunningOnWindows() ? "Package" : "Run-Tests"; +} + +RunTarget(target); diff --git a/build/XUnit.props b/build/XUnit.props index 15412d19e7..079565d184 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -9,21 +9,6 @@ + - - - - - true - - - - - true - - - diff --git a/cake.config b/cake.config new file mode 100644 index 0000000000..8089cd4084 --- /dev/null +++ b/cake.config @@ -0,0 +1,15 @@ +; This is the default configuration file for Cake. +; This file was downloaded from https://github.com/cake-build/resources + +[Nuget] +Source=https://api.nuget.org/v3/index.json +UseInProcessClient=true +LoadDependencies=false + +[Paths] +Tools=./tools +Addins=./tools/Addins +Modules=./tools/Modules + +[Settings] +SkipVerification=false diff --git a/parameters.cake b/parameters.cake index 759846c658..ffd472cbd4 100644 --- a/parameters.cake +++ b/parameters.cake @@ -1,6 +1,5 @@ public class Parameters { - public string Target { get; private set; } public string Platform { get; private set; } public string Configuration { get; private set; } public bool SkipTests { get; private set; } @@ -43,7 +42,6 @@ public class Parameters var buildSystem = context.BuildSystem(); // ARGUMENTS - Target = context.Argument("target", "Default"); Platform = context.Argument("platform", "Any CPU"); Configuration = context.Argument("configuration", "Release"); SkipTests = context.HasArgument("skip-tests"); diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index cee3331ba8..96f0e76fd8 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -28,8 +28,6 @@ namespace ControlCatalog.Android { global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello; - global::Avalonia.Android.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; - global::Splat.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; } public partial class Attribute @@ -96,14 +94,11 @@ namespace ControlCatalog.Android public partial class String { - // aapt resource value: 0x7f040002 - public const int ApplicationName = 2130968578; - // aapt resource value: 0x7f040001 - public const int Hello = 2130968577; + public const int ApplicationName = 2130968577; // aapt resource value: 0x7f040000 - public const int library_name = 2130968576; + public const int Hello = 2130968576; static String() { diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index ffa0de0a36..19c31a3af1 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -88,7 +88,6 @@ namespace Direct3DInteropSample context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearRenderTargetView(renderView, Color.White); - var time = 50; // Update WorldViewProj Matrix var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * Matrix.RotationZ((float) _model.RotationZ) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs index 78f744cea0..041d91043a 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs @@ -110,7 +110,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform { //Not supported } - + public void SetTopmost(bool value) + { + //Not supported + } } } \ No newline at end of file diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs index 0f90472bd0..71822a6f47 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs @@ -126,8 +126,6 @@ namespace Avalonia.Android.Platform.Specific.Helpers return e.Action != MotionEventActions.Up; } - private Paint _paint; - public void Dispose() { HandleEvents = false; diff --git a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs index e66c2800d3..80cbbc51ec 100644 --- a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs @@ -40,14 +40,11 @@ namespace Avalonia.Android public partial class String { - // aapt resource value: 0x7f020002 - public static int ApplicationName = 2130837506; - // aapt resource value: 0x7f020001 - public static int Hello = 2130837505; + public static int ApplicationName = 2130837505; // aapt resource value: 0x7f020000 - public static int library_name = 2130837504; + public static int Hello = 2130837504; static String() { diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 91327cf941..e171dd6162 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -28,8 +28,6 @@ namespace Avalonia.AndroidTestApplication { global::Avalonia.Android.Resource.String.ApplicationName = global::Avalonia.AndroidTestApplication.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::Avalonia.AndroidTestApplication.Resource.String.Hello; - global::Avalonia.Android.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; - global::Splat.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; } public partial class Attribute @@ -64,14 +62,11 @@ namespace Avalonia.AndroidTestApplication public partial class String { - // aapt resource value: 0x7f030002 - public const int ApplicationName = 2130903042; - // aapt resource value: 0x7f030001 - public const int Hello = 2130903041; + public const int ApplicationName = 2130903041; // aapt resource value: 0x7f030000 - public const int library_name = 2130903040; + public const int Hello = 2130903040; static String() { diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Animation/AnimatorKeyFrame.cs index 875bf761c4..02457cb9aa 100644 --- a/src/Avalonia.Animation/AnimatorKeyFrame.cs +++ b/src/Avalonia.Animation/AnimatorKeyFrame.cs @@ -9,7 +9,7 @@ namespace Avalonia.Animation { /// /// Defines a KeyFrame that is used for - /// objects. + /// objects. /// public class AnimatorKeyFrame { diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index ae70cacdba..ac7e97a4b1 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -11,6 +11,7 @@ namespace Avalonia.Data.Core { internal abstract class ExpressionNode : ISubject { + private static readonly object CacheInvalid = new object(); protected static readonly WeakReference UnsetReference = new WeakReference(AvaloniaProperty.UnsetValue); @@ -18,6 +19,8 @@ namespace Avalonia.Data.Core private IDisposable _valueSubscription; private IObserver _observer; + protected WeakReference LastValue { get; private set; } + public abstract string Description { get; } public ExpressionNode Next { get; set; } @@ -61,6 +64,7 @@ namespace Avalonia.Data.Core { _valueSubscription?.Dispose(); _valueSubscription = null; + LastValue = null; nextSubscription?.Dispose(); _observer = null; }); @@ -120,6 +124,7 @@ namespace Avalonia.Data.Core if (notification == null) { + LastValue = new WeakReference(value); if (Next != null) { Next.Target = new WeakReference(value); @@ -131,6 +136,7 @@ namespace Avalonia.Data.Core } else { + LastValue = new WeakReference(notification.Value); if (Next != null) { Next.Target = new WeakReference(notification.Value); diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index 7719f93a02..14bc09f5b7 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs @@ -154,7 +154,7 @@ namespace Avalonia.Data.Core /// public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue) { - if (Leaf is ISettableNode settable) + if (Leaf is SettableNode settable) { var node = _node; while (node != null) @@ -188,7 +188,7 @@ namespace Avalonia.Data.Core /// Gets the type of the expression result or null if the expression could not be /// evaluated. /// - public Type ResultType => (Leaf as ISettableNode)?.PropertyType; + public Type ResultType => (Leaf as SettableNode)?.PropertyType; /// /// Gets the leaf node. diff --git a/src/Avalonia.Base/Data/Core/ISettableNode.cs b/src/Avalonia.Base/Data/Core/ISettableNode.cs deleted file mode 100644 index 7788407833..0000000000 --- a/src/Avalonia.Base/Data/Core/ISettableNode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Avalonia.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Avalonia.Data.Core -{ - interface ISettableNode - { - bool SetTargetValue(object value, BindingPriority priority); - Type PropertyType { get; } - } -} diff --git a/src/Avalonia.Base/Data/Core/IndexerNode.cs b/src/Avalonia.Base/Data/Core/IndexerNode.cs index 47e82fa2d3..633d3558ee 100644 --- a/src/Avalonia.Base/Data/Core/IndexerNode.cs +++ b/src/Avalonia.Base/Data/Core/IndexerNode.cs @@ -15,7 +15,7 @@ using Avalonia.Data; namespace Avalonia.Data.Core { - internal class IndexerNode : ExpressionNode, ISettableNode + internal class IndexerNode : SettableNode { public IndexerNode(IList arguments) { @@ -52,7 +52,7 @@ namespace Avalonia.Data.Core return Observable.Merge(inputs).StartWith(GetValue(target)); } - public bool SetTargetValue(object value, BindingPriority priority) + protected override bool SetTargetValueCore(object value, BindingPriority priority) { var typeInfo = Target.Target.GetType().GetTypeInfo(); var list = Target.Target as IList; @@ -154,7 +154,7 @@ namespace Avalonia.Data.Core public IList Arguments { get; } - public Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType; + public override Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType; private object GetValue(object target) { diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index 4dbff4602f..9d657b3144 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -10,7 +10,7 @@ using Avalonia.Data.Core.Plugins; namespace Avalonia.Data.Core { - internal class PropertyAccessorNode : ExpressionNode, ISettableNode + internal class PropertyAccessorNode : SettableNode { private readonly bool _enableValidation; private IPropertyAccessor _accessor; @@ -23,13 +23,17 @@ namespace Avalonia.Data.Core public override string Description => PropertyName; public string PropertyName { get; } - public Type PropertyType => _accessor?.PropertyType; + public override Type PropertyType => _accessor?.PropertyType; - public bool SetTargetValue(object value, BindingPriority priority) + protected override bool SetTargetValueCore(object value, BindingPriority priority) { if (_accessor != null) { - try { return _accessor.SetValue(value, priority); } catch { } + try + { + return _accessor.SetValue(value, priority); + } + catch { } } return false; @@ -56,7 +60,10 @@ namespace Avalonia.Data.Core () => { _accessor = accessor; - return Disposable.Create(() => _accessor = null); + return Disposable.Create(() => + { + _accessor = null; + }); }, _ => accessor); } diff --git a/src/Avalonia.Base/Data/Core/SettableNode.cs b/src/Avalonia.Base/Data/Core/SettableNode.cs new file mode 100644 index 0000000000..092cdbe48f --- /dev/null +++ b/src/Avalonia.Base/Data/Core/SettableNode.cs @@ -0,0 +1,38 @@ +using Avalonia.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Data.Core +{ + internal abstract class SettableNode : ExpressionNode + { + public bool SetTargetValue(object value, BindingPriority priority) + { + if (ShouldNotSet(value)) + { + return true; + } + return SetTargetValueCore(value, priority); + } + + private bool ShouldNotSet(object value) + { + if (PropertyType == null) + { + return false; + } + if (PropertyType.IsValueType) + { + return LastValue?.Target != null && LastValue.Target.Equals(value); + } + return LastValue != null && Object.ReferenceEquals(LastValue?.Target, value); + } + + protected abstract bool SetTargetValueCore(object value, BindingPriority priority); + + public abstract Type PropertyType { get; } + } +} diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 7af3deef34..51b690ece9 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -209,16 +209,7 @@ namespace Avalonia.Controls public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); - private bool CheckSetup { get; set; } = true; - - /// - /// Set this AppBuilder to ignore the setup check. Used for testing purposes. - /// - internal TAppBuilder IgnoreSetupCheck() - { - CheckSetup = false; - return Self; - } + protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() { diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 4f7ac82df7..9ba68f584e 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -72,6 +72,11 @@ namespace Avalonia.Platform /// void SetMinMaxSize(Size minSize, Size maxSize); + /// + /// Sets whether this window appears on top of all other windows + /// + void SetTopmost(bool value); + /// /// Gets platform specific display information /// diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index e438843078..1d5a187a73 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -115,7 +115,9 @@ namespace Avalonia.Controls.Presenters var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count); Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl)); +#pragma warning disable 4014 MoveToPage(-1, SelectedIndex); +#pragma warning restore 4014 } break; @@ -126,6 +128,7 @@ namespace Avalonia.Controls.Presenters generator.Clear(); Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl)); +#pragma warning disable 4014 var newIndex = SelectedIndex; if(SelectedIndex < 0) @@ -141,6 +144,7 @@ namespace Avalonia.Controls.Presenters } MoveToPage(-1, newIndex); +#pragma warning restore 4014 } break; } diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 656f3890cd..005717d681 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -70,6 +70,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty StaysOpenProperty = AvaloniaProperty.Register(nameof(StaysOpen), true); + /// + /// Defines the property. + /// + public static readonly StyledProperty TopmostProperty = + AvaloniaProperty.Register(nameof(Topmost)); + private bool _isOpen; private PopupRoot _popupRoot; private TopLevel _topLevel; @@ -84,6 +90,7 @@ namespace Avalonia.Controls.Primitives IsHitTestVisibleProperty.OverrideDefaultValue(false); ChildProperty.Changed.AddClassHandler(x => x.ChildChanged); IsOpenProperty.Changed.AddClassHandler(x => x.IsOpenChanged); + TopmostProperty.Changed.AddClassHandler((p, e) => p.PopupRoot.Topmost = (bool)e.NewValue); } /// @@ -194,6 +201,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(StaysOpenProperty, value); } } + /// + /// Gets or sets whether this popup appears on top of all other windows + /// + public bool Topmost + { + get { return GetValue(TopmostProperty); } + set { SetValue(TopmostProperty, value); } + } + /// /// Gets the root of the popup window. /// diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index 0057b15150..3ddcb06303 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -6,9 +6,21 @@ using System.Reactive; using System.Reactive.Linq; using Avalonia.Data; using Avalonia.Interactivity; +using Avalonia.Input; namespace Avalonia.Controls.Primitives { + public class ScrollEventArgs : EventArgs + { + public ScrollEventArgs(ScrollEventType eventType, double newValue) + { + ScrollEventType = eventType; + NewValue = newValue; + } + public double NewValue { get; private set; } + public ScrollEventType ScrollEventType { get; private set; } + } + /// /// A scrollbar control. /// @@ -44,6 +56,9 @@ namespace Avalonia.Controls.Primitives { PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical"); PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); + + Thumb.DragDeltaEvent.AddClassHandler(o => o.OnThumbDragDelta, RoutingStrategies.Bubble); + Thumb.DragCompletedEvent.AddClassHandler(o => o.OnThumbDragComplete, RoutingStrategies.Bubble); } /// @@ -88,6 +103,8 @@ namespace Avalonia.Controls.Primitives set { SetValue(OrientationProperty, value); } } + public event EventHandler Scroll; + /// /// Calculates whether the scrollbar should be visible. /// @@ -140,6 +157,8 @@ namespace Avalonia.Controls.Primitives _pageUpButton = e.NameScope.Find