diff --git a/.gitignore b/.gitignore
index 6ee6057d5f..2b2c9c3d0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -195,3 +195,4 @@ Logs/
ModuleCache.noindex/
Build/Intermediates.noindex/
info.plist
+build-intermediate
diff --git a/.gitmodules b/.gitmodules
index 2d2a9ac497..22c56307b0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"]
path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
url = https://github.com/AvaloniaUI/Portable.Xaml.git
+[submodule "nukebuild/Numerge"]
+ path = nukebuild/Numerge
+ url = https://github.com/kekekeks/Numerge.git
diff --git a/.nuke b/.nuke
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b0c0c807cb..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: csharp
-os:
- - linux
-dist: trusty
-osx_image: xcode8.3
-env:
- global:
- - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
- - DOTNET_CLI_TELEMETRY_OPTOUT=1
-mono:
- - 5.2.0
-dotnet: 2.1.200
-script:
- - sudo apt-get update
- - sudo apt-get install castxml
- - ./build.sh --target "Travis" --configuration "Release"
-notifications:
- email: false
- webhooks:
- urls:
- - https://webhooks.gitter.im/e/98f653320ef2b7506c05
- on_success: change
- on_failure: always
- on_start: never
diff --git a/Avalonia.sln b/Avalonia.sln
index 7fa5f7736a..e006cadb5a 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -147,6 +147,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Splat.props = build\Splat.props
build\System.Memory.props = build\System.Memory.props
build\XUnit.props = build\XUnit.props
+ build\BuildTargets.targets = build\BuildTargets.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@@ -188,6 +189,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks", "src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj", "{BF28998D-072C-439A-AFBB-2FE5021241E0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -213,6 +218,10 @@ Global
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1687,6 +1696,30 @@ Global
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.Build.0 = Release|Any CPU
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings
index d61208c358..1361172fff 100644
--- a/Avalonia.sln.DotSettings
+++ b/Avalonia.sln.DotSettings
@@ -35,4 +35,5 @@
<Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" />
- <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" />
\ No newline at end of file
+ <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" />
+ True
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 50476c81f1..1f26df9bbc 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,5 +1,5 @@
- $(MSBuildThisFileDirectory)artifacts/nuget
+ $(MSBuildThisFileDirectory)build-intermediate/nuget
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 484fb4586f..0000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-os: Visual Studio 2017
-skip_branch_with_pr: true
-configuration:
-- Release
-environment:
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- NUGET_API_URL: https://www.nuget.org/api/v2/package
- MYGET_API_KEY:
- secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK
- MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package
-init:
-- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")}
-before_build:
-- git submodule update --init
-build_script:
-- ps: .\build.ps1 -Target "AppVeyor" -Configuration "$env:configuration"
-
-test: off
-artifacts:
- - path: artifacts\nuget\*.nupkg
- - path: artifacts\zip\*.zip
- - path: artifacts\inspectcode.xml
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index f6929f8dee..8c5380e65e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -11,19 +11,18 @@ jobs:
sudo apt-get install castxml
- task: CmdLine@2
- displayName: 'Install Cake'
+ displayName: 'Install Nuke'
inputs:
script: |
- dotnet tool install -g Cake.Tool --version 0.30.0
-
+ dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2
- displayName: 'Run Cake'
+ displayName: 'Run Nuke'
inputs:
script: |
export PATH="$PATH:$HOME/.dotnet/tools"
dotnet --info
printenv
- dotnet cake build.cake -target="Azure-Linux" -configuration="Release"
+ nuke --target CiAzureLinux --configuration=Release
- task: PublishTestResults@2
inputs:
@@ -55,13 +54,13 @@ jobs:
script: brew install castxml
- task: CmdLine@2
- displayName: 'Install Cake'
+ displayName: 'Install Nuke'
inputs:
script: |
- dotnet tool install -g Cake.Tool --version 0.30.0
+ dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2
- displayName: 'Run Cake'
+ displayName: 'Run Nuke'
inputs:
script: |
export COREHOST_TRACE=0
@@ -72,7 +71,7 @@ jobs:
export PATH="$PATH:$HOME/.dotnet/tools"
dotnet --info
printenv
- dotnet cake build.cake -target="Azure-OSX" -configuration="Release"
+ nuke --target CiAzureOSX --configuration Release
- task: PublishTestResults@2
inputs:
@@ -97,17 +96,17 @@ jobs:
vmImage: 'vs2017-win2016'
steps:
- task: CmdLine@2
- displayName: 'Install Cake'
+ displayName: 'Install Nuke'
inputs:
script: |
- dotnet tool install -g Cake.Tool --version 0.30.0
+ dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2
- displayName: 'Run Cake'
+ displayName: 'Run Nuke'
inputs:
script: |
set PATH=%PATH%;%USERPROFILE%\.dotnet\tools
- dotnet cake build.cake -target="Azure-Windows" -configuration="Release"
+ nuke --target CiAzureWindows --configuration Release
- task: PublishTestResults@2
inputs:
diff --git a/build.cake b/build.cake
deleted file mode 100644
index f10a12c4e6..0000000000
--- a/build.cake
+++ /dev/null
@@ -1,312 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// TOOLS
-///////////////////////////////////////////////////////////////////////////////
-
-#tool "nuget:?package=NuGet.CommandLine&version=4.7.1"
-#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.3"
-#tool "nuget:?package=xunit.runner.console&version=2.3.1"
-#tool "nuget:?package=JetBrains.dotMemoryUnit&version=3.0.20171219.105559"
-
-///////////////////////////////////////////////////////////////////////////////
-// USINGS
-///////////////////////////////////////////////////////////////////////////////
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-
-///////////////////////////////////////////////////////////////////////////////
-// SCRIPTS
-///////////////////////////////////////////////////////////////////////////////
-
-#load "./parameters.cake"
-
-///////////////////////////////////////////////////////////////////////////////
-// SETUP
-///////////////////////////////////////////////////////////////////////////////
-
-Setup(context =>
-{
- var parameters = new Parameters(context);
-
- Information("Building version {0} of Avalonia ({1}) using version {2} of Cake.",
- parameters.Version,
- parameters.Configuration,
- typeof(ICakeContext).Assembly.GetName().Version.ToString());
-
- if (parameters.IsRunningOnAppVeyor)
- {
- Information("Repository Name: " + BuildSystem.AppVeyor.Environment.Repository.Name);
- Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch);
- }
- Information("Target:" + context.TargetTask.Name);
- Information("Configuration: " + parameters.Configuration);
- Information("IsLocalBuild: " + parameters.IsLocalBuild);
- Information("IsRunningOnUnix: " + parameters.IsRunningOnUnix);
- Information("IsRunningOnWindows: " + parameters.IsRunningOnWindows);
- Information("IsRunningOnAppVeyor: " + parameters.IsRunningOnAppVeyor);
- Information("IsRunnongOnAzure:" + parameters.IsRunningOnAzure);
- Information("IsPullRequest: " + parameters.IsPullRequest);
- Information("IsMainRepo: " + parameters.IsMainRepo);
- Information("IsMasterBranch: " + parameters.IsMasterBranch);
- Information("IsReleaseBranch: " + parameters.IsReleaseBranch);
- Information("IsTagged: " + parameters.IsTagged);
- Information("IsReleasable: " + parameters.IsReleasable);
- Information("IsMyGetRelease: " + parameters.IsMyGetRelease);
- Information("IsNuGetRelease: " + parameters.IsNuGetRelease);
-
- return parameters;
-});
-
-///////////////////////////////////////////////////////////////////////////////
-// TEARDOWN
-///////////////////////////////////////////////////////////////////////////////
-
-Teardown((context, buildContext) =>
-{
- Information("Finished running tasks.");
-});
-
-///////////////////////////////////////////////////////////////////////////////
-// TASKS
-///////////////////////////////////////////////////////////////////////////////
-
-Task("Clean-Impl")
- .Does(data =>
-{
- CleanDirectories(data.BuildDirs);
- CleanDirectory(data.ArtifactsDir);
- CleanDirectory(data.NugetRoot);
- CleanDirectory(data.ZipRoot);
- CleanDirectory(data.TestResultsRoot);
-});
-
-void DotNetCoreBuild(Parameters parameters)
-{
- var settings = new DotNetCoreBuildSettings
- {
- Configuration = parameters.Configuration,
- MSBuildSettings = new DotNetCoreMSBuildSettings
- {
- Properties =
- {
- { "PackageVersion", new [] { parameters.Version } }
- }
- }
- };
-
- DotNetCoreBuild(parameters.MSBuildSolution, settings);
-}
-
-Task("Build-Impl")
- .Does(data =>
-{
- if(data.IsRunningOnWindows)
- {
- MSBuild(data.MSBuildSolution, settings => {
- settings.SetConfiguration(data.Configuration);
- settings.SetVerbosity(Verbosity.Minimal);
- settings.WithProperty("iOSRoslynPathHackRequired", "true");
- settings.WithProperty("PackageVersion", data.Version);
- settings.UseToolVersion(MSBuildToolVersion.VS2017);
- settings.WithRestore();
- });
- }
- else
- {
- DotNetCoreBuild(data);
- }
-});
-
-void RunCoreTest(string project, Parameters parameters, bool coreOnly = false)
-{
- if(!project.EndsWith(".csproj"))
- project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
- Information("Running tests from " + project);
- var frameworks = new List(){"netcoreapp2.0"};
- foreach(var fw in frameworks)
- {
- if(!fw.StartsWith("netcoreapp") && coreOnly)
- continue;
- Information("Running for " + fw);
-
- var settings = new DotNetCoreTestSettings {
- Configuration = parameters.Configuration,
- Framework = fw,
- NoBuild = true,
- NoRestore = true
- };
-
- if (parameters.PublishTestResults)
- {
- settings.Logger = "trx";
- settings.ResultsDirectory = parameters.TestResultsRoot;
- }
-
- DotNetCoreTest(project, settings);
- }
-}
-
-Task("Run-Unit-Tests-Impl")
- .WithCriteria((context, data) => !data.SkipTests)
- .Does(data =>
-{
- RunCoreTest("./tests/Avalonia.Base.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Controls.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Input.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Layout.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Markup.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Styling.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.Skia.UnitTests", data, false);
- RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", data, false);
- if (data.IsRunningOnWindows)
- {
- RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data, false);
- }
-});
-
-Task("Run-Designer-Tests-Impl")
- .WithCriteria((context, data) => !data.SkipTests)
- .Does(data =>
-{
- RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data, false);
-});
-
-Task("Run-Render-Tests-Impl")
- .WithCriteria((context, data) => !data.SkipTests)
- .WithCriteria((context, data) => data.IsRunningOnWindows)
- .Does(data =>
-{
- RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data, true);
- RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data, true);
-});
-
-Task("Run-Leak-Tests-Impl")
- .WithCriteria((context, data) => !data.SkipTests)
- .WithCriteria((context, data) => data.IsRunningOnWindows)
- .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("Zip-Files-Impl")
- .Does(data =>
-{
- Zip(data.BinRoot, data.ZipCoreArtifacts);
-
- Zip(data.NugetRoot, data.ZipNuGetArtifacts);
-
- Zip(data.ZipSourceControlCatalogDesktopDirs,
- data.ZipTargetControlCatalogDesktopDirs,
- GetFiles(data.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") +
- GetFiles(data.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") +
- GetFiles(data.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") +
- GetFiles(data.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") +
- GetFiles(data.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe"));
-});
-
-void DotNetCorePack(Parameters parameters)
-{
- var settings = new DotNetCorePackSettings
- {
- Configuration = parameters.Configuration,
- MSBuildSettings = new DotNetCoreMSBuildSettings
- {
- Properties =
- {
- { "PackageVersion", new [] { parameters.Version } }
- }
- }
- };
-
- DotNetCorePack(parameters.MSBuildSolution, settings);
-}
-
-Task("Create-NuGet-Packages-Impl")
- .Does(data =>
-{
- if(data.IsRunningOnWindows)
- {
- MSBuild(data.MSBuildSolution, settings => {
- settings.SetConfiguration(data.Configuration);
- settings.SetVerbosity(Verbosity.Minimal);
- settings.WithProperty("iOSRoslynPathHackRequired", "true");
- settings.WithProperty("PackageVersion", data.Version);
- settings.UseToolVersion(MSBuildToolVersion.VS2017);
- settings.WithRestore();
- settings.WithTarget("Pack");
- });
- }
- else
- {
- DotNetCorePack(data);
- }
-});
-
-///////////////////////////////////////////////////////////////////////////////
-// TARGETS
-///////////////////////////////////////////////////////////////////////////////
-
-Task("Build")
- .IsDependentOn("Clean-Impl")
- .IsDependentOn("Build-Impl");
-
-Task("Run-Tests")
- .IsDependentOn("Build")
- .IsDependentOn("Run-Unit-Tests-Impl")
- .IsDependentOn("Run-Render-Tests-Impl")
- .IsDependentOn("Run-Designer-Tests-Impl")
- .IsDependentOn("Run-Leak-Tests-Impl");
-
-Task("Package")
- .IsDependentOn("Run-Tests")
- .IsDependentOn("Create-NuGet-Packages-Impl");
-
-Task("AppVeyor")
- .IsDependentOn("Package")
- .IsDependentOn("Zip-Files-Impl");
-
-Task("Travis")
- .IsDependentOn("Run-Tests");
-
-Task("Azure-Linux")
- .IsDependentOn("Run-Tests");
-
-Task("Azure-OSX")
- .IsDependentOn("Package")
- .IsDependentOn("Zip-Files-Impl");
-
-Task("Azure-Windows")
- .IsDependentOn("Package")
- .IsDependentOn("Zip-Files-Impl");
-
-///////////////////////////////////////////////////////////////////////////////
-// EXECUTE
-///////////////////////////////////////////////////////////////////////////////
-
-var target = Context.Argument("target", "Default");
-
-if (target == "Default")
-{
- target = Context.IsRunningOnWindows() ? "Package" : "Run-Tests";
-}
-
-RunTarget(target);
diff --git a/build.ps1 b/build.ps1
index 46696db2b2..57e2f80075 100644
--- a/build.ps1
+++ b/build.ps1
@@ -1,201 +1,69 @@
-##########################################################################
-# This is the Cake bootstrapper script for PowerShell.
-# This file was downloaded from https://github.com/cake-build/resources
-# Feel free to change this file to fit your needs.
-##########################################################################
-
-<#
-
-.SYNOPSIS
-This is a Powershell script to bootstrap a Cake build.
-
-.DESCRIPTION
-This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
-and execute your Cake build script with the parameters you provide.
-
-.PARAMETER Script
-The build script to execute.
-.PARAMETER Target
-The build script target to run.
-.PARAMETER Platform
-The build platform to use.
-.PARAMETER Configuration
-The build configuration to use.
-.PARAMETER Verbosity
-Specifies the amount of information to be displayed.
-.PARAMETER Experimental
-Tells Cake to use the latest Roslyn release.
-.PARAMETER WhatIf
-Performs a dry run of the build script.
-No tasks will be executed.
-.PARAMETER Mono
-Tells Cake to use the Mono scripting engine.
-.PARAMETER SkipToolPackageRestore
-Skips restoring of packages.
-.PARAMETER SkipTests
-Skips unit tests
-.PARAMETER ScriptArgs
-Remaining arguments are added here.
-
-.LINK
-http://cakebuild.net
-
-#>
-
[CmdletBinding()]
Param(
- [string]$Script = "build.cake",
- [string]$Target = "Default",
- [ValidateSet("Any CPU", "x86", "x64", "NetCoreOnly", "iPhone")]
- [string]$Platform = "Any CPU",
- [ValidateSet("Release", "Debug")]
- [string]$Configuration = "Release",
- [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
- [string]$Verbosity = "Verbose",
- [switch]$Experimental,
- [Alias("DryRun","Noop")]
- [switch]$WhatIf,
- [switch]$Mono,
- [switch]$SkipToolPackageRestore,
+ #[switch]$CustomParam,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
- [string[]]$ScriptArgs
+ [string[]]$BuildArguments
)
-[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
-function MD5HashFile([string] $filePath)
-{
- if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
- {
- return $null
- }
-
- [System.IO.Stream] $file = $null;
- [System.Security.Cryptography.MD5] $md5 = $null;
- try
- {
- $md5 = [System.Security.Cryptography.MD5]::Create()
- $file = [System.IO.File]::OpenRead($filePath)
- return [System.BitConverter]::ToString($md5.ComputeHash($file))
- }
- finally
- {
- if ($file -ne $null)
- {
- $file.Dispose()
- }
- }
-}
-
-Write-Host "Preparing to run build script..."
-
-if(!$PSScriptRoot){
- $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
-}
+Write-Output "Windows PowerShell $($Host.Version)"
-$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
-$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
-$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
-$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
-$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
-$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
+Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 }
+$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
-# Should we use mono?
-$UseMono = "";
-if($Mono.IsPresent) {
- Write-Verbose -Message "Using the Mono based scripting engine."
- $UseMono = "-mono"
-}
+###########################################################################
+# CONFIGURATION
+###########################################################################
-# Should we use the new Roslyn?
-$UseExperimental = "";
-if($Experimental.IsPresent -and !($Mono.IsPresent)) {
- Write-Verbose -Message "Using experimental version of Roslyn."
- $UseExperimental = "-experimental"
-}
+$BuildProjectFile = "$PSScriptRoot\nukebuild\_build.csproj"
+$TempDirectory = "$PSScriptRoot\\.tmp"
-# Is this a dry run?
-$UseDryRun = "";
-if($WhatIf.IsPresent) {
- $UseDryRun = "-dryrun"
-}
+$DotNetGlobalFile = "$PSScriptRoot\\global.json"
+$DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1"
+$DotNetChannel = "Current"
-# Is this a dry run?
-$UseSkipTests = "";
-if($SkipTests.IsPresent) {
- $UseSkipTests = "-skip-tests"
-}
+$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
+$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
+$env:NUGET_XMLDOC_MODE = "skip"
-# Make sure tools folder exists
-if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
- Write-Verbose -Message "Creating tools directory..."
- New-Item -Path $TOOLS_DIR -Type directory | out-null
-}
+###########################################################################
+# EXECUTION
+###########################################################################
-# Make sure that packages.config exist.
-if (!(Test-Path $PACKAGES_CONFIG)) {
- Write-Verbose -Message "Downloading packages.config..."
- try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
- Throw "Could not download packages.config."
- }
+function ExecSafe([scriptblock] $cmd) {
+ & $cmd
+ if ($LASTEXITCODE) { exit $LASTEXITCODE }
}
-# Try find NuGet.exe in path if not exists
-if (!(Test-Path $NUGET_EXE)) {
- Write-Verbose -Message "Trying to find nuget.exe in PATH..."
- $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
- $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
- if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
- Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
- $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
+# If global.json exists, load expected version
+if (Test-Path $DotNetGlobalFile) {
+ $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
+ if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
+ $DotNetVersion = $DotNetGlobal.sdk.version
}
}
-# Try download NuGet.exe if not exists
-if (!(Test-Path $NUGET_EXE)) {
- Write-Verbose -Message "Downloading NuGet.exe..."
- try {
- (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
- } catch {
- Throw "Could not download NuGet.exe."
- }
+# If dotnet is installed locally, and expected version is not set or installation matches the expected version
+if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and `
+ (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) {
+ $env:DOTNET_EXE = (Get-Command "dotnet").Path
}
-
-# Save nuget.exe path to environment to be available to child processed
-$ENV:NUGET_EXE = $NUGET_EXE
-
-# Restore tools from NuGet?
-if(-Not $SkipToolPackageRestore.IsPresent) {
- Push-Location
- Set-Location $TOOLS_DIR
-
- # Check for changes in packages.config and remove installed tools if true.
- [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
- if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
- ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
- Write-Verbose -Message "Missing or changed package.config hash..."
- Remove-Item * -Recurse -Exclude packages.config,nuget.exe
+else {
+ $DotNetDirectory = "$TempDirectory\dotnet-win"
+ $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
+
+ # Download install script
+ $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
+ md -force $TempDirectory > $null
+ (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
+
+ # Install by channel or version
+ if (!(Test-Path variable:DotNetVersion)) {
+ ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
+ } else {
+ ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}
-
- Write-Verbose -Message "Restoring tools from NuGet..."
- $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
-
- if ($LASTEXITCODE -ne 0) {
- Throw "An error occured while restoring NuGet tools."
- }
- else
- {
- $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
- }
- Write-Verbose -Message ($NuGetOutput | out-string)
- Pop-Location
}
-# Make sure that Cake has been installed.
-if (!(Test-Path $CAKE_EXE)) {
- Throw "Could not find Cake.exe at $CAKE_EXE"
-}
+Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
-# Start Cake
-Write-Host "Running build script..."
-Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -platform=`"$Platform`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseSkipTests $UseMono $UseDryRun $UseExperimental $ScriptArgs"
-exit $LASTEXITCODE
\ No newline at end of file
+ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -- $BuildArguments }
diff --git a/build.sh b/build.sh
index 206a55d171..40b1c225a6 100755
--- a/build.sh
+++ b/build.sh
@@ -1,105 +1,72 @@
#!/usr/bin/env bash
-##########################################################################
-# This is the Cake bootstrapper script for Linux and OS X.
-# This file was downloaded from https://github.com/cake-build/resources
-# Feel free to change this file to fit your needs.
-##########################################################################
+echo $(bash --version 2>&1 | head -n 1)
-# Define directories.
-SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-TOOLS_DIR=$SCRIPT_DIR/tools
-NUGET_EXE=$TOOLS_DIR/nuget.exe
-CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
-PACKAGES_CONFIG=$TOOLS_DIR/packages.config
-PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
-
-# Define md5sum or md5 depending on Linux/OSX
-MD5_EXE=
-if [[ "$(uname -s)" == "Darwin" ]]; then
- MD5_EXE="md5 -r"
-else
- MD5_EXE="md5sum"
-fi
-
-# Define default arguments.
-SCRIPT="build.cake"
-TARGET="Default"
-CONFIGURATION="Release"
-PLATFORM="Any CPU"
-VERBOSITY="verbose"
-DRYRUN=
-SKIP_TESTS=
-SHOW_VERSION=false
-SCRIPT_ARGUMENTS=()
-
-# Parse arguments.
+#CUSTOMPARAM=0
+BUILD_ARGUMENTS=()
for i in "$@"; do
- case $1 in
- -s|--script) SCRIPT="$2"; shift ;;
- -t|--target) TARGET="$2"; shift ;;
- -p|--platform) PLATFORM="$2"; shift ;;
- -c|--configuration) CONFIGURATION="$2"; shift ;;
- --skip-tests) SKIP_TESTS="-skip-tests"; shift ;;
- -v|--verbosity) VERBOSITY="$2"; shift ;;
- -d|--dryrun) DRYRUN="-dryrun" ;;
- --version) SHOW_VERSION=true ;;
- --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
- *) SCRIPT_ARGUMENTS+=("$1") ;;
+ case $(echo $1 | awk '{print tolower($0)}') in
+ # -custom-param) CUSTOMPARAM=1;;
+ *) BUILD_ARGUMENTS+=("$1") ;;
esac
shift
done
-# Make sure the tools folder exist.
-if [ ! -d "$TOOLS_DIR" ]; then
- mkdir "$TOOLS_DIR"
-fi
+set -eo pipefail
+SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
-# Make sure that packages.config exist.
-if [ ! -f "$TOOLS_DIR/packages.config" ]; then
- echo "Downloading packages.config..."
- curl -Lsfo "$TOOLS_DIR/packages.config" http://cakebuild.net/download/bootstrapper/packages
- if [ $? -ne 0 ]; then
- echo "An error occured while downloading packages.config."
- exit 1
- fi
-fi
+###########################################################################
+# CONFIGURATION
+###########################################################################
-# Download NuGet if it does not exist.
-if [ ! -f "$NUGET_EXE" ]; then
- echo "Downloading NuGet..."
- curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
- if [ $? -ne 0 ]; then
- echo "An error occured while downloading nuget.exe."
- exit 1
- fi
-fi
+BUILD_PROJECT_FILE="$SCRIPT_DIR/nukebuild/_build.csproj"
+TEMP_DIRECTORY="$SCRIPT_DIR//.tmp"
-# Restore tools from NuGet.
-pushd "$TOOLS_DIR" >/dev/null
-if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then
- find . -type d ! -name . | xargs rm -rf
-fi
+DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
+DOTNET_INSTALL_URL="https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.sh"
+DOTNET_CHANNEL="Current"
-mono "$NUGET_EXE" install -ExcludeVersion
-if [ $? -ne 0 ]; then
- echo "Could not restore NuGet packages."
- exit 1
-fi
+export DOTNET_CLI_TELEMETRY_OPTOUT=1
+export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+export NUGET_XMLDOC_MODE="skip"
-$MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5
+###########################################################################
+# EXECUTION
+###########################################################################
-popd >/dev/null
+function FirstJsonValue {
+ perl -nle 'print $1 if m{"'$1'": "([^"\-]+)",?}' <<< ${@:2}
+}
-# Make sure that Cake has been installed.
-if [ ! -f "$CAKE_EXE" ]; then
- echo "Could not find Cake.exe at '$CAKE_EXE'."
- exit 1
+# If global.json exists, load expected version
+if [ -f "$DOTNET_GLOBAL_FILE" ]; then
+ DOTNET_VERSION=$(FirstJsonValue "version" $(cat "$DOTNET_GLOBAL_FILE"))
+ if [ "$DOTNET_VERSION" == "" ]; then
+ unset DOTNET_VERSION
+ fi
fi
-# Start Cake
-if $SHOW_VERSION; then
- exec mono "$CAKE_EXE" -version
+# If dotnet is installed locally, and expected version is not set or installation matches the expected version
+if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") ]]; then
+ export DOTNET_EXE="$(command -v dotnet)"
else
- exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -platform="$PLATFORM" -configuration="$CONFIGURATION" -target=$TARGET $DRYRUN $SKIP_TESTS "${SCRIPT_ARGUMENTS[@]}"
+ DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
+ export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
+
+ # Download install script
+ DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
+ mkdir -p "$TEMP_DIRECTORY"
+ curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
+ chmod +x "$DOTNET_INSTALL_FILE"
+
+ # Install by channel or version
+ if [ -z ${DOTNET_VERSION+x} ]; then
+ "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
+ else
+ "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
+ fi
fi
+
+echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
+
+"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" -- ${BUILD_ARGUMENTS[@]}
diff --git a/build/BuildTargets.targets b/build/BuildTargets.targets
new file mode 100644
index 0000000000..08ec039ec7
--- /dev/null
+++ b/build/BuildTargets.targets
@@ -0,0 +1,8 @@
+
+
+ $(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll
+ true
+
+
+
+
diff --git a/build/ReferenceCoreLibraries.props b/build/ReferenceCoreLibraries.props
index bd9d6ad843..cffc2e0324 100644
--- a/build/ReferenceCoreLibraries.props
+++ b/build/ReferenceCoreLibraries.props
@@ -1,5 +1,4 @@
-
diff --git a/cake.config b/cake.config
deleted file mode 100644
index 8089cd4084..0000000000
--- a/cake.config
+++ /dev/null
@@ -1,15 +0,0 @@
-; 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/native/Avalonia.Native/src/OSX/gl.mm b/native/Avalonia.Native/src/OSX/gl.mm
index a62098a074..083adc927d 100644
--- a/native/Avalonia.Native/src/OSX/gl.mm
+++ b/native/Avalonia.Native/src/OSX/gl.mm
@@ -208,9 +208,8 @@ public:
virtual ~AvnGlRenderingSession()
{
- glFlush();
[_context flushBuffer];
- [_context setView:nil];
+ [NSOpenGLContext clearCurrentContext];
CGLUnlockContext([_context CGLContextObj]);
[_view unlockFocus];
}
@@ -241,9 +240,8 @@ public:
auto gl = _context;
CGLLockContext([_context CGLContextObj]);
[gl setView: _view];
+ [gl update];
[gl makeCurrentContext];
- auto frame = [_view frame];
-
*ret = new AvnGlRenderingSession(_window, _view, gl);
return S_OK;
}
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
new file mode 100644
index 0000000000..61944c4dbc
--- /dev/null
+++ b/nukebuild/Build.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Nuke.Common;
+using Nuke.Common.Git;
+using Nuke.Common.ProjectModel;
+using Nuke.Common.Tooling;
+using Nuke.Common.Tools.DotNet;
+using Nuke.Common.Tools.MSBuild;
+using Nuke.Common.Utilities;
+using static Nuke.Common.EnvironmentInfo;
+using static Nuke.Common.IO.FileSystemTasks;
+using static Nuke.Common.IO.PathConstruction;
+using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
+using static Nuke.Common.Tools.DotNet.DotNetTasks;
+using static Nuke.Common.Tools.Xunit.XunitTasks;
+
+
+/*
+ Before editing this file, install support plugin for your IDE,
+ running and debugging a particular target (optionally without deps) would be way easier
+ ReSharper/Rider - https://plugins.jetbrains.com/plugin/10803-nuke-support
+ VSCode - https://marketplace.visualstudio.com/items?itemName=nuke.support
+
+ */
+
+partial class Build : NukeBuild
+{
+ BuildParameters Parameters { get; set; }
+ protected override void OnBuildInitialized()
+ {
+ Parameters = new BuildParameters(this);
+ Information("Building version {0} of Avalonia ({1}) using version {2} of Nuke.",
+ Parameters.Version,
+ Parameters.Configuration,
+ typeof(NukeBuild).Assembly.GetName().Version.ToString());
+
+ if (Parameters.IsLocalBuild)
+ {
+ Information("Repository Name: " + Parameters.RepositoryName);
+ Information("Repository Branch: " + Parameters.RepositoryBranch);
+ }
+ Information("Configuration: " + Parameters.Configuration);
+ Information("IsLocalBuild: " + Parameters.IsLocalBuild);
+ Information("IsRunningOnUnix: " + Parameters.IsRunningOnUnix);
+ Information("IsRunningOnWindows: " + Parameters.IsRunningOnWindows);
+ Information("IsRunningOnAzure:" + Parameters.IsRunningOnAzure);
+ Information("IsPullRequest: " + Parameters.IsPullRequest);
+ Information("IsMainRepo: " + Parameters.IsMainRepo);
+ Information("IsMasterBranch: " + Parameters.IsMasterBranch);
+ Information("IsReleaseBranch: " + Parameters.IsReleaseBranch);
+ Information("IsReleasable: " + Parameters.IsReleasable);
+ Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
+ Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
+ }
+
+ Target Clean => _ => _.Executes(() =>
+ {
+ DeleteDirectories(Parameters.BuildDirs);
+ EnsureCleanDirectories(Parameters.BuildDirs);
+ EnsureCleanDirectory(Parameters.ArtifactsDir);
+ EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
+ EnsureCleanDirectory(Parameters.NugetRoot);
+ EnsureCleanDirectory(Parameters.ZipRoot);
+ EnsureCleanDirectory(Parameters.TestResultsRoot);
+ });
+
+ Target Compile => _ => _
+ .DependsOn(Clean)
+ .Executes(() =>
+ {
+
+ if (Parameters.IsRunningOnWindows)
+ MSBuild(Parameters.MSBuildSolution, c => c
+ .SetArgumentConfigurator(a => a.Add("/r"))
+ .SetConfiguration(Parameters.Configuration)
+ .SetVerbosity(MSBuildVerbosity.Minimal)
+ .AddProperty("PackageVersion", Parameters.Version)
+ .AddProperty("iOSRoslynPathHackRequired", "true")
+ .SetToolsVersion(MSBuildToolsVersion._15_0)
+ .AddTargets("Build")
+ );
+
+ else
+ DotNetBuild(Parameters.MSBuildSolution, c => c
+ .AddProperty("PackageVersion", Parameters.Version)
+ .SetConfiguration(Parameters.Configuration)
+ );
+ });
+
+ void RunCoreTest(string project, bool coreOnly = false)
+ {
+ if(!project.EndsWith(".csproj"))
+ project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
+ Information("Running tests from " + project);
+ var frameworks = new List(){"netcoreapp2.0"};
+ foreach(var fw in frameworks)
+ {
+ if(!fw.StartsWith("netcoreapp") && coreOnly)
+ continue;
+ Information("Running for " + fw);
+ DotNetTest(c =>
+ {
+ c = c
+ .SetProjectFile(project)
+ .SetConfiguration(Parameters.Configuration)
+ .SetFramework(fw)
+ .EnableNoBuild()
+ .EnableNoRestore();
+ // NOTE: I can see that we could maybe add another extension method "Switch" or "If" to make this more convenient
+ if (Parameters.PublishTestResults)
+ c = c.SetLogger("trx").SetResultsDirectory(Parameters.TestResultsRoot);
+ return c;
+ });
+ }
+ }
+
+ Target RunCoreLibsTests => _ => _
+ .OnlyWhen(() => !Parameters.SkipTests)
+ .DependsOn(Compile)
+ .Executes(() =>
+ {
+ RunCoreTest("./tests/Avalonia.Base.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Controls.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Input.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Layout.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Markup.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Styling.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Visuals.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.Skia.UnitTests", false);
+ RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", false);
+ });
+
+ Target RunRenderTests => _ => _
+ .OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
+ .DependsOn(Compile)
+ .Executes(() =>
+ {
+ RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true);
+ RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true);
+ });
+
+ Target RunDesignerTests => _ => _
+ .OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
+ .DependsOn(Compile)
+ .Executes(() =>
+ {
+ RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", false);
+ });
+
+ [PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit;
+
+ Target RunLeakTests => _ => _
+ .OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
+ .DependsOn(Compile)
+ .Executes(() =>
+ {
+ var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll";
+ DotMemoryUnit(
+ $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}",
+ timeout: 120_000);
+ });
+
+ Target ZipFiles => _ => _
+ .After(CreateNugetPackages, Compile, RunCoreLibsTests, Package)
+ .Executes(() =>
+ {
+ var data = Parameters;
+ Zip(data.ZipCoreArtifacts, data.BinRoot);
+ Zip(data.ZipNuGetArtifacts, data.NugetRoot);
+ Zip(data.ZipTargetControlCatalogDesktopDir,
+ GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dll").Concat(
+ GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.config")).Concat(
+ GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.so")).Concat(
+ GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dylib")).Concat(
+ GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe")));
+ });
+
+ Target CreateIntermediateNugetPackages => _ => _
+ .DependsOn(Compile)
+ .After(RunTests)
+ .Executes(() =>
+ {
+ if (Parameters.IsRunningOnWindows)
+
+ MSBuild(Parameters.MSBuildSolution, c => c
+ .SetConfiguration(Parameters.Configuration)
+ .SetVerbosity(MSBuildVerbosity.Minimal)
+ .AddProperty("PackageVersion", Parameters.Version)
+ .AddProperty("iOSRoslynPathHackRequired", "true")
+ .SetToolsVersion(MSBuildToolsVersion._15_0)
+ .AddTargets("Pack"));
+ else
+ DotNetPack(Parameters.MSBuildSolution, c =>
+ c.SetConfiguration(Parameters.Configuration)
+ .AddProperty("PackageVersion", Parameters.Version));
+ });
+
+ Target CreateNugetPackages => _ => _
+ .DependsOn(CreateIntermediateNugetPackages)
+ .Executes(() =>
+ {
+ var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
+ EnsureCleanDirectory(Parameters.NugetRoot);
+ if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
+ new NumergeNukeLogger()))
+ throw new Exception("Package merge failed");
+ });
+
+ Target RunTests => _ => _
+ .DependsOn(RunCoreLibsTests)
+ .DependsOn(RunRenderTests)
+ .DependsOn(RunDesignerTests)
+ .DependsOn(RunLeakTests);
+
+ Target Package => _ => _
+ .DependsOn(RunTests)
+ .DependsOn(CreateNugetPackages);
+
+ Target CiAzureLinux => _ => _
+ .DependsOn(RunTests);
+
+ Target CiAzureOSX => _ => _
+ .DependsOn(Package)
+ .DependsOn(ZipFiles);
+
+ Target CiAzureWindows => _ => _
+ .DependsOn(Package)
+ .DependsOn(ZipFiles);
+
+
+ public static int Main() =>
+ RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+ ? Execute(x => x.Package)
+ : Execute(x => x.RunTests);
+
+}
diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs
new file mode 100644
index 0000000000..5f8ec7267e
--- /dev/null
+++ b/nukebuild/BuildParameters.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Xml.Linq;
+using Nuke.Common;
+using Nuke.Common.BuildServers;
+using Nuke.Common.Execution;
+using Nuke.Common.IO;
+using static Nuke.Common.IO.FileSystemTasks;
+using static Nuke.Common.IO.PathConstruction;
+using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
+
+public partial class Build
+{
+ [Parameter("configuration")]
+ public string NukeArgConfiguration { get; set; }
+
+ [Parameter("skip-tests")]
+ public bool NukeArgSkipTests { get; set; }
+
+ [Parameter("force-nuget-version")]
+ public string NukeArgForceNugetVersion { get; set; }
+
+ public class BuildParameters
+ {
+ public string Configuration { get; }
+ public bool SkipTests { get; }
+ public string MainRepo { get; }
+ public string MasterBranch { get; }
+ public string RepositoryName { get; }
+ public string RepositoryBranch { get; }
+ public string ReleaseConfiguration { get; }
+ public string ReleaseBranchPrefix { get; }
+ public string MSBuildSolution { get; }
+ public bool IsLocalBuild { get; }
+ public bool IsRunningOnUnix { get; }
+ public bool IsRunningOnWindows { get; }
+ public bool IsRunningOnAzure { get; }
+ public bool IsPullRequest { get; }
+ public bool IsMainRepo { get; }
+ public bool IsMasterBranch { get; }
+ public bool IsReleaseBranch { get; }
+ public bool IsReleasable { get; }
+ public bool IsMyGetRelease { get; }
+ public bool IsNuGetRelease { get; }
+ public bool PublishTestResults { get; }
+ public string Version { get; }
+ public AbsolutePath ArtifactsDir { get; }
+ public AbsolutePath NugetIntermediateRoot { get; }
+ public AbsolutePath NugetRoot { get; }
+ public AbsolutePath ZipRoot { get; }
+ public AbsolutePath BinRoot { get; }
+ public AbsolutePath TestResultsRoot { get; }
+ public string DirSuffix { get; }
+ public List BuildDirs { get; }
+ public string FileZipSuffix { get; }
+ public AbsolutePath ZipCoreArtifacts { get; }
+ public AbsolutePath ZipNuGetArtifacts { get; }
+ public AbsolutePath ZipSourceControlCatalogDesktopDir { get; }
+ public AbsolutePath ZipTargetControlCatalogDesktopDir { get; }
+
+
+ public BuildParameters(Build b)
+ {
+ // ARGUMENTS
+ Configuration = b.NukeArgConfiguration ?? "Release";
+ SkipTests = b.NukeArgSkipTests;
+
+ // CONFIGURATION
+ MainRepo = "https://github.com/AvaloniaUI/Avalonia";
+ MasterBranch = "refs/heads/master";
+ ReleaseBranchPrefix = "refs/heads/release/";
+ ReleaseConfiguration = "Release";
+ MSBuildSolution = RootDirectory / "dirs.proj";
+
+ // PARAMETERS
+ IsLocalBuild = Host == HostType.Console;
+ IsRunningOnUnix = Environment.OSVersion.Platform == PlatformID.Unix ||
+ Environment.OSVersion.Platform == PlatformID.MacOSX;
+ IsRunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ IsRunningOnAzure = Host == HostType.TeamServices ||
+ Environment.GetEnvironmentVariable("LOGNAME") == "vsts";
+
+ if (IsRunningOnAzure)
+ {
+ RepositoryName = TeamServices.Instance.RepositoryUri;
+ RepositoryBranch = TeamServices.Instance.SourceBranch;
+ IsPullRequest = TeamServices.Instance.PullRequestId.HasValue;
+ IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, TeamServices.Instance.RepositoryUri);
+ }
+ IsMainRepo =
+ StringComparer.OrdinalIgnoreCase.Equals(MainRepo,
+ RepositoryName);
+ IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch,
+ RepositoryBranch);
+ IsReleaseBranch = RepositoryBranch?.StartsWith(ReleaseBranchPrefix, StringComparison.OrdinalIgnoreCase) ==
+ true;
+
+ IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
+ IsMyGetRelease = IsReleasable;
+ IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
+
+ // VERSION
+ Version = b.NukeArgForceNugetVersion ?? GetVersion();
+
+ if (IsRunningOnAzure)
+ {
+ if (!IsNuGetRelease)
+ {
+ // Use AssemblyVersion with Build as version
+ Version += "-build" + Environment.GetEnvironmentVariable("BUILD_BUILDID") + "-beta";
+ }
+
+ PublishTestResults = true;
+ }
+
+ // DIRECTORIES
+ ArtifactsDir = RootDirectory / "artifacts";
+ NugetRoot = ArtifactsDir / "nuget";
+ NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget";
+ ZipRoot = ArtifactsDir / "zip";
+ BinRoot = ArtifactsDir / "bin";
+ TestResultsRoot = ArtifactsDir / "test-results";
+ BuildDirs = GlobDirectories(RootDirectory, "**bin").Concat(GlobDirectories(RootDirectory, "**obj")).ToList();
+ DirSuffix = Configuration;
+ FileZipSuffix = Version + ".zip";
+ ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix);
+ ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix);
+ ZipSourceControlCatalogDesktopDir =
+ RootDirectory / ("samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461");
+ ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix);
+ }
+
+ string GetVersion()
+ {
+ var xdoc = XDocument.Load(RootDirectory / "build/SharedVersion.props");
+ return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
+ }
+ }
+
+}
diff --git a/nukebuild/Numerge b/nukebuild/Numerge
new file mode 160000
index 0000000000..4464343aef
--- /dev/null
+++ b/nukebuild/Numerge
@@ -0,0 +1 @@
+Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8
diff --git a/nukebuild/Shims.cs b/nukebuild/Shims.cs
new file mode 100644
index 0000000000..461d617643
--- /dev/null
+++ b/nukebuild/Shims.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using Nuke.Common;
+using Nuke.Common.IO;
+using Numerge;
+
+public partial class Build
+{
+ static void Information(string info)
+ {
+ Logger.Info(info);
+ }
+
+ static void Information(string info, params object[] args)
+ {
+ Logger.Info(info, args);
+ }
+
+ private void Zip(PathConstruction.AbsolutePath target, params string[] paths) => Zip(target, paths.AsEnumerable());
+
+ private void Zip(PathConstruction.AbsolutePath target, IEnumerable paths)
+ {
+ var targetPath = target.ToString();
+ bool finished = false, atLeastOneFileAdded = false;
+ try
+ {
+ using (var targetStream = File.Create(targetPath))
+ using(var archive = new System.IO.Compression.ZipArchive(targetStream, ZipArchiveMode.Create))
+ {
+ void AddFile(string path, string relativePath)
+ {
+ var e = archive.CreateEntry(relativePath.Replace("\\", "/"), CompressionLevel.Optimal);
+ using (var entryStream = e.Open())
+ using (var fileStream = File.OpenRead(path))
+ fileStream.CopyTo(entryStream);
+ atLeastOneFileAdded = true;
+ }
+
+ foreach (var path in paths)
+ {
+ if (Directory.Exists(path))
+ {
+ var dirInfo = new DirectoryInfo(path);
+ var rootPath = Path.GetDirectoryName(dirInfo.FullName);
+ foreach(var fsEntry in dirInfo.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
+ {
+ if (fsEntry is FileInfo)
+ {
+ var relPath = Path.GetRelativePath(rootPath, fsEntry.FullName);
+ AddFile(fsEntry.FullName, relPath);
+ }
+ }
+ }
+ else if(File.Exists(path))
+ {
+ var name = Path.GetFileName(path);
+ AddFile(path, name);
+ }
+ }
+ }
+
+ finished = true;
+ }
+ finally
+ {
+ try
+ {
+ if (!finished || !atLeastOneFileAdded)
+ File.Delete(targetPath);
+ }
+ catch
+ {
+ //Ignore
+ }
+ }
+ }
+
+ class NumergeNukeLogger : INumergeLogger
+ {
+ public void Log(NumergeLogLevel level, string message)
+ {
+ if(level == NumergeLogLevel.Error)
+ Logger.Error(message);
+ else if (level == NumergeLogLevel.Warning)
+ Logger.Warn(message);
+ else
+ Logger.Info(message);
+ }
+ }
+}
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
new file mode 100644
index 0000000000..e02acff007
--- /dev/null
+++ b/nukebuild/_build.csproj
@@ -0,0 +1,37 @@
+
+
+
+ Exe
+ netcoreapp2.0
+ false
+
+ False
+ CS0649;CS0169
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nukebuild/_build.csproj.DotSettings b/nukebuild/_build.csproj.DotSettings
new file mode 100644
index 0000000000..9aac7d8e8d
--- /dev/null
+++ b/nukebuild/_build.csproj.DotSettings
@@ -0,0 +1,24 @@
+
+ False
+ Implicit
+ Implicit
+ ExpressionBody
+ 0
+ NEXT_LINE
+ True
+ False
+ 120
+ IF_OWNER_IS_SINGLE_LINE
+ WRAP_IF_LONG
+ False
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
diff --git a/nukebuild/numerge.config b/nukebuild/numerge.config
new file mode 100644
index 0000000000..e4e15d693d
--- /dev/null
+++ b/nukebuild/numerge.config
@@ -0,0 +1,23 @@
+{
+ "Packages":
+ [
+ {
+ "Id": "Avalonia",
+ "MergeAll": true,
+ "Exclude": ["Avalonia.Remote.Protocol"],
+ "IncomingIncludeAssetsOverride": "",
+ "Merge": [
+ {
+ "Id": "Avalonia.Build.Tasks",
+ "IgnoreMissingFrameworkBinaries": true,
+ "DoNotMergeDependencies": true
+ },
+ {
+ "Id": "Avalonia.DesktopRuntime",
+ "IgnoreMissingFrameworkBinaries": true,
+ "IgnoreMissingFrameworkDependencies": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index dee42fb795..489cb228aa 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -1,10 +1,12 @@
-
+
netstandard2.0;net461;netcoreapp2.0
-
+
+
+
@@ -27,14 +29,18 @@
false
None
- <_PackageFiles Include="Avalonia.props">
- build/Avalonia.props
- false
- None
-
-
+
+
+ true
+ build\
+
+
+ true
+ build\
+
+
diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props
index 8234dc4fde..6f21971d3d 100644
--- a/packages/Avalonia/Avalonia.props
+++ b/packages/Avalonia/Avalonia.props
@@ -2,5 +2,8 @@
$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll
$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe
+ $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll
+ false
+
diff --git a/packages/Avalonia/Avalonia.targets b/packages/Avalonia/Avalonia.targets
new file mode 100644
index 0000000000..50306f2cdc
--- /dev/null
+++ b/packages/Avalonia/Avalonia.targets
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/Avalonia/AvaloniaBuildTasks.props b/packages/Avalonia/AvaloniaBuildTasks.props
new file mode 100644
index 0000000000..30bafa37ee
--- /dev/null
+++ b/packages/Avalonia/AvaloniaBuildTasks.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
new file mode 100644
index 0000000000..10f971cc4c
--- /dev/null
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -0,0 +1,43 @@
+
+
+ <_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)
+ <_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false
+
+
+
+
+
+
+
+ $(IntermediateOutputPath)/Avalonia/resources
+
+
+
+ !AvaloniaResources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index 9d113cf2ef..9280125323 100644
--- a/readme.md
+++ b/readme.md
@@ -2,9 +2,9 @@
# Avalonia
-| Gitter Chat | Build Status (Win, Linux, OSX) | Appveyor Build Status | Open Collective |
-|---|---|---|---|
-| [](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) | [](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [](#backers) [](#sponsors) |
+| Gitter Chat | Build Status (Win, Linux, OSX) | Open Collective |
+|---|---|---|
+| [](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) | [](#backers) [](#sponsors) |
## About
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 1f53dedc14..57c8b700df 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -23,6 +23,7 @@ namespace ControlCatalog.NetCore
break;
}
}
+
if (args.Contains("--fbdev"))
AppBuilder.Configure().InitializeWithLinuxFramebuffer(tl =>
{
@@ -30,7 +31,12 @@ namespace ControlCatalog.NetCore
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
else
- BuildAvaloniaApp().Start();
+ BuildAvaloniaApp().Start(AppMain, args);
+ }
+
+ static void AppMain(Application app, string[] args)
+ {
+ app.Run(new MainWindow());
}
///
@@ -46,4 +52,4 @@ namespace ControlCatalog.NetCore
Console.ReadKey(true);
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index 19a22bb6ed..67220e6dd0 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -1,7 +1,9 @@
-
+
-
-
+
+
-
+
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index d0a746f87d..463a8a8db6 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -6,10 +6,11 @@
%(Filename)
-
+
Designer
-
-
+
+
+
@@ -24,4 +25,5 @@
+
diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml
index b2f6497caa..cb6016b324 100644
--- a/samples/ControlCatalog/DecoratedWindow.xaml
+++ b/samples/ControlCatalog/DecoratedWindow.xaml
@@ -1,7 +1,8 @@
-
+ xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window">
@@ -18,7 +19,13 @@
- Hello world!
+
+ Hello world!
+
+ Decorated
+
+ CanResize
+
@@ -30,4 +37,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 7c2ae441d0..0f7f2e80a8 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -1,6 +1,7 @@
diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml
index 7029273a84..4bae60b2a6 100644
--- a/samples/ControlCatalog/MainWindow.xaml
+++ b/samples/ControlCatalog/MainWindow.xaml
@@ -1,6 +1,8 @@
+ Icon="/Assets/test_icon.ico"
+ xmlns:local="clr-namespace:ControlCatalog"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.MainWindow">
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
index 28bdc7ac71..0ca3567970 100644
--- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
@@ -1,4 +1,6 @@
-
+
AutoCompleteBox
A control into which the user can input text
diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml
index 09f591d59d..c30056d5e5 100644
--- a/samples/ControlCatalog/Pages/BorderPage.xaml
+++ b/samples/ControlCatalog/Pages/BorderPage.xaml
@@ -1,4 +1,6 @@
-
+
Border
A control which decorates a child with a border and background
diff --git a/samples/ControlCatalog/Pages/ButtonPage.xaml b/samples/ControlCatalog/Pages/ButtonPage.xaml
index a4690e32e1..39d89590c2 100644
--- a/samples/ControlCatalog/Pages/ButtonPage.xaml
+++ b/samples/ControlCatalog/Pages/ButtonPage.xaml
@@ -1,5 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.Pages.ButtonPage">
Button
A button control
diff --git a/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml b/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
index fba15f6e77..c3f9f65dd9 100644
--- a/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
+++ b/samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
@@ -1,5 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.Pages.ButtonSpinnerPage">
ButtonSpinner
diff --git a/samples/ControlCatalog/Pages/CalendarPage.xaml b/samples/ControlCatalog/Pages/CalendarPage.xaml
index c47fd766fb..dbd0dba37b 100644
--- a/samples/ControlCatalog/Pages/CalendarPage.xaml
+++ b/samples/ControlCatalog/Pages/CalendarPage.xaml
@@ -1,5 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.Pages.CalendarPage">
Calendar
A calendar control for selecting dates
diff --git a/samples/ControlCatalog/Pages/CanvasPage.xaml b/samples/ControlCatalog/Pages/CanvasPage.xaml
index d6c138a4f7..d154e717a4 100644
--- a/samples/ControlCatalog/Pages/CanvasPage.xaml
+++ b/samples/ControlCatalog/Pages/CanvasPage.xaml
@@ -1,4 +1,6 @@
-
+
Canvas
A panel which lays out its children by explicit coordinates
diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml b/samples/ControlCatalog/Pages/CarouselPage.xaml
index cf9b13c00c..b08ff565e3 100644
--- a/samples/ControlCatalog/Pages/CarouselPage.xaml
+++ b/samples/ControlCatalog/Pages/CarouselPage.xaml
@@ -1,4 +1,6 @@
-
+
Carousel
An items control that displays its items as pages that fill the control.
@@ -11,9 +13,9 @@
-
-
-
+
+
+
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/SliderPage.xaml b/samples/ControlCatalog/Pages/SliderPage.xaml
index 6db71b5fcc..58f7b881fe 100644
--- a/samples/ControlCatalog/Pages/SliderPage.xaml
+++ b/samples/ControlCatalog/Pages/SliderPage.xaml
@@ -1,4 +1,6 @@
-
+
Slider
A control that lets the user select from a range of values by moving a Thumb control along a Track.
@@ -18,4 +20,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml b/samples/ControlCatalog/Pages/TabControlPage.xaml
index 5b10e7d790..01ddc0ddca 100644
--- a/samples/ControlCatalog/Pages/TabControlPage.xaml
+++ b/samples/ControlCatalog/Pages/TabControlPage.xaml
@@ -1,4 +1,7 @@
-
+
-
-
-
-
-
+
This is the first page in the TabControl.
-
+
-
-
-
-
-
+
This is the second page in the TabControl.
-
+
-
-
-
-
-
+
You should not see this.
@@ -82,10 +61,7 @@
+ Text="{Binding Header}">
diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs
index 808d90a49c..a38a3ab4cb 100644
--- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs
@@ -26,13 +26,13 @@ namespace ControlCatalog.Pages
{
Header = "Arch",
Text = "This is the first templated tab page.",
- Image = LoadBitmap("resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg?assembly=ControlCatalog"),
+ Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"),
},
new TabItemViewModel
{
Header = "Leaf",
Text = "This is the second templated tab page.",
- Image = LoadBitmap("resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg?assembly=ControlCatalog"),
+ Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"),
},
new TabItemViewModel
{
diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml
index 5f94e15cec..0c0a4d705b 100644
--- a/samples/ControlCatalog/Pages/TextBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml
@@ -1,4 +1,6 @@
-
+
TextBox
A control into which the user can input text
@@ -33,11 +35,20 @@
Text="Multiline TextBox with no TextWrapping.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
+ resm fonts
+
+
+ res fonts
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml
index ad832b9b82..cbe1e3059c 100644
--- a/samples/ControlCatalog/Pages/ToolTipPage.xaml
+++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml
@@ -1,4 +1,6 @@
-
+
ToolTip
@@ -38,4 +40,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/TreeViewPage.xaml b/samples/ControlCatalog/Pages/TreeViewPage.xaml
index 1ab49dbb30..f8f3cd5848 100644
--- a/samples/ControlCatalog/Pages/TreeViewPage.xaml
+++ b/samples/ControlCatalog/Pages/TreeViewPage.xaml
@@ -1,4 +1,6 @@
-
+
TreeView
Displays a hierachical tree of data.
@@ -16,4 +18,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml
index 89a82c4791..e78cf2bc22 100644
--- a/samples/ControlCatalog/Pages/ViewboxPage.xaml
+++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml
@@ -1,5 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.Pages.ViewboxPage">
F1 M 16.6309,18.6563C 17.1309,
diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml
index 3ec8e43b07..f97a3064e7 100644
--- a/samples/ControlCatalog/SideBar.xaml
+++ b/samples/ControlCatalog/SideBar.xaml
@@ -1,5 +1,6 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ x:Class="ControlCatalog.SideBar">
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
+
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj
index 3b6d1c1b33..73c619942e 100644
--- a/src/Avalonia.Animation/Avalonia.Animation.csproj
+++ b/src/Avalonia.Animation/Avalonia.Animation.csproj
@@ -1,7 +1,6 @@
netstandard2.0
- false
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index a72d20a57c..cbcf7a7386 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -4,8 +4,10 @@
Avalonia.Base
Avalonia
True
- false
+
+
+
diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs
index dda2cbc2d5..691fa2fc12 100644
--- a/src/Avalonia.Base/Platform/IAssetLoader.cs
+++ b/src/Avalonia.Base/Platform/IAssetLoader.cs
@@ -66,6 +66,6 @@ namespace Avalonia.Platform
///
/// The URI.
/// All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset
- IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri uri);
+ IEnumerable GetAssets(Uri uri, Uri baseUri);
}
}
diff --git a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs
new file mode 100644
index 0000000000..22e5c952bf
--- /dev/null
+++ b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+
+// ReSharper disable AssignNullToNotNullAttribute
+
+namespace Avalonia.Utilities
+{
+ #if !BUILDTASK
+ public
+ #endif
+ static class AvaloniaResourcesIndexReaderWriter
+ {
+ private const int LastKnownVersion = 1;
+ public static List Read(Stream stream)
+ {
+ var ver = new BinaryReader(stream).ReadInt32();
+ if (ver > LastKnownVersion)
+ throw new Exception("Resources index format version is not known");
+ var index = (AvaloniaResourcesIndex)
+ new DataContractSerializer(typeof(AvaloniaResourcesIndex)).ReadObject(stream);
+ return index.Entries;
+ }
+
+ public static void Write(Stream stream, List entries)
+ {
+ new BinaryWriter(stream).Write(LastKnownVersion);
+ new DataContractSerializer(typeof(AvaloniaResourcesIndex)).WriteObject(stream,
+ new AvaloniaResourcesIndex()
+ {
+ Entries = entries
+ });
+ }
+ }
+
+ [DataContract]
+#if !BUILDTASK
+ public
+#endif
+ class AvaloniaResourcesIndex
+ {
+ [DataMember]
+ public List Entries { get; set; } = new List();
+ }
+
+ [DataContract]
+#if !BUILDTASK
+ public
+#endif
+ class AvaloniaResourcesIndexEntry
+ {
+ [DataMember]
+ public string Path { get; set; }
+
+ [DataMember]
+ public int Offset { get; set; }
+
+ [DataMember]
+ public int Size { get; set; }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
new file mode 100644
index 0000000000..ce767aaa87
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netstandard2.0
+ tools
+ $(DefineConstants);BUILDTASK
+
+
+
+
+ Shared/AvaloniaResourcesIndex.cs
+
+
+ Shared/AvaloniaResourceXamlInfo.cs
+
+
+
+
diff --git a/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
new file mode 100644
index 0000000000..3b8a390881
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
@@ -0,0 +1,9 @@
+namespace Avalonia.Build.Tasks
+{
+ public enum BuildEngineErrorCode
+ {
+ InvalidXAML = 1,
+ DuplicateXClass = 2,
+ LegacyResmScheme = 3,
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/Extensions.cs b/src/Avalonia.Build.Tasks/Extensions.cs
new file mode 100644
index 0000000000..faecee0f37
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/Extensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.Build.Framework;
+
+namespace Avalonia.Build.Tasks
+{
+ public static class Extensions
+ {
+ static string FormatErrorCode(BuildEngineErrorCode code) => $"AVLN:{(int)code:0000}";
+
+ public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message)
+ {
+ engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message,
+ "", "Avalonia"));
+ }
+
+ public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message)
+ {
+ engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message,
+ "", "Avalonia"));
+ }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
new file mode 100644
index 0000000000..98ebb3e7d1
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using Avalonia.Markup.Xaml.PortableXaml;
+using Avalonia.Utilities;
+using Microsoft.Build.Framework;
+using SPath=System.IO.Path;
+namespace Avalonia.Build.Tasks
+{
+ public class GenerateAvaloniaResourcesTask : ITask
+ {
+ [Required]
+ public ITaskItem[] Resources { get; set; }
+ [Required]
+ public string Root { get; set; }
+ [Required]
+ public string Output { get; set; }
+ [Required]
+ public ITaskItem[] EmbeddedResources { get; set; }
+
+ class Source
+ {
+ public string Path { get; set; }
+ public int Size { get; set; }
+ private byte[] _data;
+ private string _sourcePath;
+
+ public Source(string file, string root)
+ {
+ file = SPath.GetFullPath(file);
+ root = SPath.GetFullPath(root);
+ var fileUri = new Uri(file, UriKind.Absolute);
+ var rootUri = new Uri(root, UriKind.Absolute);
+ rootUri = new Uri(rootUri.ToString().TrimEnd('/') + '/');
+ Path = '/' + rootUri.MakeRelativeUri(fileUri).ToString().TrimStart('/');
+ _sourcePath = file;
+ Size = (int)new FileInfo(_sourcePath).Length;
+ }
+
+ public string SystemPath => _sourcePath ?? Path;
+
+ public Source(string path, byte[] data)
+ {
+ Path = path;
+ _data = data;
+ Size = data.Length;
+ }
+
+ public Stream Open()
+ {
+ if (_data != null)
+ return new MemoryStream(_data, false);
+ return File.OpenRead(_sourcePath);
+ }
+
+ public string ReadAsString()
+ {
+ if (_data != null)
+ return Encoding.UTF8.GetString(_data);
+ return File.ReadAllText(_sourcePath);
+ }
+ }
+
+ List BuildResourceSources() => Resources.Select(r => new Source(r.ItemSpec, Root)).ToList();
+
+ private void Pack(Stream output, List sources)
+ {
+ var offsets = new Dictionary();
+ var coffset = 0;
+ foreach (var s in sources)
+ {
+ offsets[s] = coffset;
+ coffset += s.Size;
+ }
+ var index = sources.Select(s => new AvaloniaResourcesIndexEntry
+ {
+ Path = s.Path,
+ Size = s.Size,
+ Offset = offsets[s]
+ }).ToList();
+ var ms = new MemoryStream();
+ AvaloniaResourcesIndexReaderWriter.Write(ms, index);
+ new BinaryWriter(output).Write((int)ms.Length);
+ ms.Position = 0;
+ ms.CopyTo(output);
+ foreach (var s in sources)
+ {
+ using(var input = s.Open())
+ input.CopyTo(output);
+ }
+ }
+
+ private bool PreProcessXamlFiles(List sources)
+ {
+ var typeToXamlIndex = new Dictionary();
+
+ foreach (var s in sources.ToList())
+ {
+ if (s.Path.ToLowerInvariant().EndsWith(".xaml") || s.Path.ToLowerInvariant().EndsWith(".paml"))
+ {
+ XamlFileInfo info;
+ try
+ {
+ info = XamlFileInfo.Parse(s.ReadAsString());
+ }
+ catch(Exception e)
+ {
+ BuildEngine.LogError(BuildEngineErrorCode.InvalidXAML, s.SystemPath, "File doesn't contain valid XAML: " + e);
+ return false;
+ }
+
+ if (info.XClass != null)
+ {
+ if (typeToXamlIndex.ContainsKey(info.XClass))
+ {
+
+ BuildEngine.LogError(BuildEngineErrorCode.DuplicateXClass, s.SystemPath,
+ $"Duplicate x:Class directive, {info.XClass} is already used in {typeToXamlIndex[info.XClass]}");
+ return false;
+ }
+ typeToXamlIndex[info.XClass] = s.Path;
+ }
+ }
+ }
+
+ var xamlInfo = new AvaloniaResourceXamlInfo
+ {
+ ClassToResourcePathIndex = typeToXamlIndex
+ };
+ var ms = new MemoryStream();
+ new DataContractSerializer(typeof(AvaloniaResourceXamlInfo)).WriteObject(ms, xamlInfo);
+ sources.Add(new Source("/!AvaloniaResourceXamlInfo", ms.ToArray()));
+ return true;
+ }
+
+ public bool Execute()
+ {
+ foreach(var r in EmbeddedResources.Where(r=>r.ItemSpec.EndsWith(".xaml")||r.ItemSpec.EndsWith(".paml")))
+ BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec,
+ "XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
+ var resources = BuildResourceSources();
+
+ if (!PreProcessXamlFiles(resources))
+ return false;
+ var dir = Path.GetDirectoryName(Output);
+ Directory.CreateDirectory(dir);
+ using (var file = File.Create(Output))
+ Pack(file, resources);
+ return true;
+ }
+
+ public IBuildEngine BuildEngine { get; set; }
+ public ITaskHost HostObject { get; set; }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/XamlFileInfo.cs b/src/Avalonia.Build.Tasks/XamlFileInfo.cs
new file mode 100644
index 0000000000..d3169f9454
--- /dev/null
+++ b/src/Avalonia.Build.Tasks/XamlFileInfo.cs
@@ -0,0 +1,20 @@
+using System.Xml.Linq;
+
+namespace Avalonia.Build.Tasks
+{
+ public class XamlFileInfo
+ {
+ public string XClass { get; set; }
+
+ public static XamlFileInfo Parse(string data)
+ {
+ var xdoc = XDocument.Parse(data);
+ var xclass = xdoc.Root.Attribute(XName.Get("Class", "http://schemas.microsoft.com/winfx/2006/xaml"));
+ return new XamlFileInfo
+ {
+ XClass = xclass?.Value
+ };
+ }
+ }
+
+}
diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index 376714b20b..c5dd072d8a 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -145,6 +145,15 @@ namespace Avalonia.Controls
Instance.Run(mainWindow);
}
+ public delegate void AppMainDelegate(Application app, string[] args);
+
+ public void Start(AppMainDelegate main, string[] args)
+ {
+ Setup();
+ BeforeStartCallback(Self);
+ main(Instance, args);
+ }
+
///
/// Sets up the platform-specific services for the application, but does not run it.
///
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index fddd4403e9..32331d29ab 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -1,7 +1,6 @@
netstandard2.0
- false
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index 1f3fcbafb3..d485924885 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -253,10 +253,8 @@ namespace Avalonia.Controls
IsPressed = false;
e.Handled = true;
- var hittest = this.GetVisualsAt(e.GetPosition(this));
-
if (ClickMode == ClickMode.Release &&
- hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this))
+ this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c)))
{
OnClick();
}
diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs
index 8596d06d2c..93b33e0589 100644
--- a/src/Avalonia.Controls/DropDown.cs
+++ b/src/Avalonia.Controls/DropDown.cs
@@ -2,9 +2,12 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Linq;
using Avalonia.Controls.Generators;
+using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
+using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Media;
@@ -12,12 +15,17 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls
{
-
///
/// A drop-down list control.
///
public class DropDown : SelectingItemsControl
{
+ ///
+ /// The default value for the property.
+ ///
+ private static readonly FuncTemplate DefaultPanel =
+ new FuncTemplate(() => new VirtualizingStackPanel());
+
///
/// Defines the property.
///
@@ -39,6 +47,12 @@ namespace Avalonia.Controls
public static readonly DirectProperty SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty VirtualizationModeProperty =
+ ItemsPresenter.VirtualizationModeProperty.AddOwner();
+
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@@ -48,6 +62,7 @@ namespace Avalonia.Controls
///
static DropDown()
{
+ ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
FocusableProperty.OverrideDefaultValue(true);
SelectedItemProperty.Changed.AddClassHandler(x => x.SelectedItemChanged);
KeyDownEvent.AddClassHandler(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
@@ -80,6 +95,15 @@ namespace Avalonia.Controls
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
}
+ ///
+ /// Gets or sets the virtualization mode for the items.
+ ///
+ public ItemVirtualizationMode VirtualizationMode
+ {
+ get { return GetValue(VirtualizationModeProperty); }
+ set { SetValue(VirtualizationModeProperty, value); }
+ }
+
///
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
@@ -138,6 +162,16 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+ else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 &&
+ (e.Key == Key.Up || e.Key == Key.Down))
+ {
+ var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
+ if (firstChild != null)
+ {
+ FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional);
+ e.Handled = true;
+ }
+ }
}
///
@@ -159,6 +193,7 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+
base.OnPointerPressed(e);
}
@@ -168,28 +203,65 @@ namespace Avalonia.Controls
if (_popup != null)
{
_popup.Opened -= PopupOpened;
+ _popup.Closed -= PopupClosed;
}
_popup = e.NameScope.Get("PART_Popup");
_popup.Opened += PopupOpened;
+ _popup.Closed += PopupClosed;
+
+ base.OnTemplateApplied(e);
}
- private void PopupOpened(object sender, EventArgs e)
+ internal void ItemFocused(DropDownItem dropDownItem)
{
- var selectedIndex = SelectedIndex;
+ if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid)
+ {
+ dropDownItem.BringIntoView();
+ }
+ }
- if (selectedIndex != -1)
+ private void PopupClosed(object sender, EventArgs e)
+ {
+ if (CanFocus(this))
{
- var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
- container?.Focus();
+ Focus();
}
}
+ private void PopupOpened(object sender, EventArgs e)
+ {
+ TryFocusSelectedItem();
+ }
+
private void SelectedItemChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdateSelectionBoxItem(e.NewValue);
+ TryFocusSelectedItem();
+ }
+
+ private void TryFocusSelectedItem()
+ {
+ var selectedIndex = SelectedIndex;
+ if (IsDropDownOpen && selectedIndex != -1)
+ {
+ var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
+
+ if(container == null && SelectedItems.Count > 0)
+ {
+ ScrollIntoView(SelectedItems[0]);
+ container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
+ }
+
+ if (container != null && CanFocus(container))
+ {
+ container.Focus();
+ }
+ }
}
+ private bool CanFocus(IControl control) => control.Focusable && control.IsEnabledCore && control.IsVisible;
+
private void UpdateSelectionBoxItem(object item)
{
var contentControl = item as IContentControl;
@@ -219,7 +291,8 @@ namespace Avalonia.Controls
}
else
{
- SelectionBoxItem = item;
+ var selector = MemberSelector;
+ SelectionBoxItem = selector != null ? selector.Select(item) : item;
}
}
diff --git a/src/Avalonia.Controls/DropDownItem.cs b/src/Avalonia.Controls/DropDownItem.cs
index fb465e93ec..1e22ededf6 100644
--- a/src/Avalonia.Controls/DropDownItem.cs
+++ b/src/Avalonia.Controls/DropDownItem.cs
@@ -2,43 +2,19 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using System.Reactive.Linq;
namespace Avalonia.Controls
{
///
/// A selectable item in a .
///
- public class DropDownItem : ContentControl, ISelectable
+ public class DropDownItem : ListBoxItem
{
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty IsSelectedProperty =
- AvaloniaProperty.Register(nameof(IsSelected));
-
- ///
- /// Initializes static members of the class.
- ///
- static DropDownItem()
- {
- FocusableProperty.OverrideDefaultValue(true);
- }
-
public DropDownItem()
{
- this.GetObservable(DropDownItem.IsFocusedProperty).Subscribe(focused =>
- {
- PseudoClasses.Set(":selected", focused);
- });
- }
-
- ///
- /// Gets or sets the selection state of the item.
- ///
- public bool IsSelected
- {
- get { return GetValue(IsSelectedProperty); }
- set { SetValue(IsSelectedProperty, value); }
+ this.GetObservable(DropDownItem.IsFocusedProperty).Where(focused => focused)
+ .Subscribe(_ => (Parent as DropDown)?.ItemFocused(this));
}
}
}
diff --git a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
index 088f9e30ea..a6a64e570b 100644
--- a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
@@ -21,6 +21,8 @@ namespace Avalonia.Controls.Generators
tabItem.ParentTabControl = Owner;
+ tabItem[~TabControl.TabStripPlacementProperty] = Owner[~TabControl.TabStripPlacementProperty];
+
if (tabItem.HeaderTemplate == null)
{
tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty];
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index ccbdc71b1d..c40ddc37ad 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -431,9 +431,12 @@ namespace Avalonia.Controls.Primitives
{
if (i.ContainerControl != null && i.Item != null)
{
- MarkContainerSelected(
- i.ContainerControl,
- SelectedItems.Contains(i.Item));
+ var ms = MemberSelector;
+ bool selected = ms == null ?
+ SelectedItems.Contains(i.Item) :
+ SelectedItems.OfType