Browse Source

Merge branch 'master' into x11

pull/2011/head
danwalmsley 8 years ago
committed by GitHub
parent
commit
3fc1f9e88f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/Avalonia.Native.v3.ncrunchproject
  2. 0
      .nuke
  3. 24
      .travis.yml
  4. 33
      Avalonia.sln
  5. 3
      Avalonia.sln.DotSettings
  6. 23
      appveyor.yml
  7. 25
      azure-pipelines.yml
  8. 312
      build.cake
  9. 226
      build.ps1
  10. 139
      build.sh
  11. 8
      build/BuildTargets.targets
  12. 15
      cake.config
  13. 2
      native/Avalonia.Native/inc/avalonia-native.h
  14. 9
      native/Avalonia.Native/src/OSX/common.h
  15. 24
      native/Avalonia.Native/src/OSX/main.mm
  16. 2
      native/Avalonia.Native/src/OSX/window.h
  17. 125
      native/Avalonia.Native/src/OSX/window.mm
  18. 231
      nukebuild/Build.cs
  19. 140
      nukebuild/BuildParameters.cs
  20. 79
      nukebuild/Shims.cs
  21. 35
      nukebuild/_build.csproj
  22. 24
      nukebuild/_build.csproj.DotSettings
  23. 22
      packages/Avalonia/Avalonia.csproj
  24. 3
      packages/Avalonia/Avalonia.props
  25. 3
      packages/Avalonia/Avalonia.targets
  26. 3
      packages/Avalonia/AvaloniaBuildTasks.props
  27. 43
      packages/Avalonia/AvaloniaBuildTasks.targets
  28. 6
      readme.md
  29. 2
      samples/ControlCatalog.Desktop/Program.cs
  30. 10
      samples/ControlCatalog.NetCore/Program.cs
  31. 10
      samples/ControlCatalog/App.xaml
  32. 8
      samples/ControlCatalog/ControlCatalog.csproj
  33. 17
      samples/ControlCatalog/DecoratedWindow.xaml
  34. 69
      samples/ControlCatalog/MainView.xaml
  35. 17
      samples/ControlCatalog/MainView.xaml.cs
  36. 8
      samples/ControlCatalog/MainWindow.xaml
  37. 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  38. 4
      samples/ControlCatalog/Pages/BorderPage.xaml
  39. 3
      samples/ControlCatalog/Pages/ButtonPage.xaml
  40. 3
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
  41. 3
      samples/ControlCatalog/Pages/CalendarPage.xaml
  42. 4
      samples/ControlCatalog/Pages/CanvasPage.xaml
  43. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  44. 3
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  45. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  46. 3
      samples/ControlCatalog/Pages/DatePickerPage.xaml
  47. 9
      samples/ControlCatalog/Pages/DialogsPage.xaml
  48. 14
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  49. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  50. 4
      samples/ControlCatalog/Pages/DropDownPage.xaml
  51. 4
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  52. 14
      samples/ControlCatalog/Pages/ImagePage.xaml
  53. 5
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml
  54. 4
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  55. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  56. 2
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  57. 19
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  58. 6
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  59. 5
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  60. 6
      samples/ControlCatalog/Pages/SliderPage.xaml
  61. 100
      samples/ControlCatalog/Pages/TabControlPage.xaml
  62. 80
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  63. 15
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  64. 6
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  65. 6
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  66. 66
      samples/ControlCatalog/Pages/ViewboxPage.xaml
  67. 18
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  68. 113
      samples/ControlCatalog/SideBar.xaml
  69. 5
      samples/RenderDemo/MainWindow.xaml
  70. 113
      samples/RenderDemo/SideBar.xaml
  71. 1
      scripts/ReplaceNugetCache.sh
  72. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  73. 12
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  74. 2
      src/Avalonia.Base/Platform/IAssetLoader.cs
  75. 64
      src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs
  76. 19
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  77. 9
      src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs
  78. 21
      src/Avalonia.Build.Tasks/Extensions.cs
  79. 159
      src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
  80. 20
      src/Avalonia.Build.Tasks/XamlFileInfo.cs
  81. 9
      src/Avalonia.Controls/AppBuilderBase.cs
  82. 2
      src/Avalonia.Controls/ColumnDefinition.cs
  83. 87
      src/Avalonia.Controls/DropDown.cs
  84. 32
      src/Avalonia.Controls/DropDownItem.cs
  85. 59
      src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
  86. 154
      src/Avalonia.Controls/Grid.cs
  87. 73
      src/Avalonia.Controls/GridSplitter.cs
  88. 2
      src/Avalonia.Controls/ItemsControl.cs
  89. 41
      src/Avalonia.Controls/LayoutTransformControl.cs
  90. 5
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  91. 21
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  92. 6
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  93. 9
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  94. 19
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  95. 31
      src/Avalonia.Controls/SystemDialog.cs
  96. 164
      src/Avalonia.Controls/TabControl.cs
  97. 67
      src/Avalonia.Controls/TabItem.cs
  98. 6
      src/Avalonia.Controls/Templates/TemplateExtensions.cs
  99. 1
      src/Avalonia.Controls/TextBox.cs
  100. 23
      src/Avalonia.Controls/Utils/GridLayout.cs

5
.ncrunch/Avalonia.Native.v3.ncrunchproject

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

0
.nuke

24
.travis.yml

@ -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

33
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}"
@ -192,6 +193,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "src\Aval
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
@ -217,6 +222,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
@ -1715,6 +1724,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

3
Avalonia.sln.DotSettings

@ -35,4 +35,5 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

23
appveyor.yml

@ -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

25
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:

312
build.cake

@ -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<Parameters>(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<Parameters>((context, buildContext) =>
{
Information("Finished running tasks.");
});
///////////////////////////////////////////////////////////////////////////////
// TASKS
///////////////////////////////////////////////////////////////////////////////
Task("Clean-Impl")
.Does<Parameters>(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<Parameters>(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<string>(){"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<Parameters>((context, data) => !data.SkipTests)
.Does<Parameters>(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<Parameters>((context, data) => !data.SkipTests)
.Does<Parameters>(data =>
{
RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data, false);
});
Task("Run-Render-Tests-Impl")
.WithCriteria<Parameters>((context, data) => !data.SkipTests)
.WithCriteria<Parameters>((context, data) => data.IsRunningOnWindows)
.Does<Parameters>(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<Parameters>((context, data) => !data.SkipTests)
.WithCriteria<Parameters>((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<Parameters>(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<Parameters>(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);

226
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
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -- $BuildArguments }

139
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[@]}

8
build/BuildTargets.targets

@ -0,0 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>true</AvaloniaUseExternalMSBuild>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.props"/>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.targets"/>
</Project>

15
cake.config

@ -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

2
native/Avalonia.Native/inc/avalonia-native.h

@ -213,7 +213,7 @@ AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
{
virtual HRESULT ShowDialog (IUnknown**ppv) = 0;
virtual HRESULT ShowDialog (IAvnWindow* parent) = 0;
virtual HRESULT SetCanResize(bool value) = 0;
virtual HRESULT SetHasDecorations(bool value) = 0;
virtual HRESULT SetTitle (void* utf8Title) = 0;

9
native/Avalonia.Native/src/OSX/common.h

@ -31,4 +31,13 @@ extern NSSize ToNSSize (AvnSize s);
#define NSDebugLog(...) (void)0
#endif
template<typename T> inline T* objc_cast(id from) {
if(from == nil)
return nil;
if ([from isKindOfClass:[T class]]) {
return static_cast<T*>(from);
}
return nil;
}
#endif

24
native/Avalonia.Native/src/OSX/main.mm

@ -32,27 +32,23 @@ public:
- (void) do;
@end
@implementation ThreadingInitializer
pthread_mutex_t mutex;
pthread_cond_t cond;
{
int _fds[2];
}
- (void) runOnce
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
char buf[]={0};
write(_fds[1], buf, 1);
}
- (void) do
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pipe(_fds);
[[[NSThread alloc] initWithTarget:self selector:@selector(runOnce) object:nil] start];
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
char buf[1];
read(_fds[0], buf, 1);
close(_fds[0]);
close(_fds[1]);
}

2
native/Avalonia.Native/src/OSX/window.h

@ -18,6 +18,8 @@ class WindowBaseImpl;
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(void) setCanBecomeKeyAndMain;
-(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents;
@end
struct INSWindowHolder

125
native/Avalonia.Native/src/OSX/window.mm

@ -109,6 +109,7 @@ public:
if(Window != nullptr)
{
[Window orderOut:Window];
[Window restoreParentWindow];
}
return S_OK;
@ -392,30 +393,6 @@ protected:
}
};
class ModalDisposable : public ComUnknownObject
{
NSModalSession _session;
AvnWindow* _window;
void Dispose ()
{
[_window orderOut:_window];
[NSApp endModalSession:_session];
}
public:
ModalDisposable(AvnWindow* window, NSModalSession session)
{
_session = session;
_window = window;
}
virtual ~ModalDisposable()
{
Dispose();
}
};
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{
private:
@ -444,32 +421,27 @@ private:
{
@autoreleasepool
{
if([Window parentWindow] != nil)
[[Window parentWindow] removeChildWindow:Window];
WindowBaseImpl::Show();
return SetWindowState(_lastWindowState);
}
}
virtual HRESULT ShowDialog (IUnknown**ppv) override
virtual HRESULT ShowDialog (IAvnWindow* parent) override
{
@autoreleasepool
{
if(ppv == nullptr)
{
if(parent == nullptr)
return E_POINTER;
}
auto session = [NSApp beginModalSessionForWindow:Window];
auto disposable = new ModalDisposable(Window, session);
*ppv = disposable;
SetPosition(lastPositionSet);
UpdateStyle();
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
auto cparent = dynamic_cast<WindowImpl*>(parent);
if(cparent == nullptr)
return E_INVALIDARG;
[Window pollModalSession:session];
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
WindowBaseImpl::Show();
return S_OK;
}
@ -843,8 +815,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[super viewDidChangeBackingProperties];
}
- (bool) ignoreUserInput
{
auto parentWindow = objc_cast<AvnWindow>([self window]);
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
return TRUE;
return FALSE;
}
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
{
if([self ignoreUserInput])
return;
[self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
auto avnPoint = [self toAvnPoint:localPoint];
@ -952,7 +935,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
{
{
if([self ignoreUserInput])
return;
auto key = s_KeyMap[[event keyCode]];
auto timestamp = [event timestamp] * 1000;
@ -1125,13 +1110,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
ComPtr<WindowBaseImpl> parent = _parent;
_parent = NULL;
[self restoreParentWindow];
parent->BaseEvents->Closed();
[parent->View onClosed];
[self setContentView: nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self setContentView: nil];
});
}
}
-(BOOL)canBecomeKeyWindow
{
return _canBecomeKeyAndMain;
@ -1142,12 +1129,62 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return _canBecomeKeyAndMain;
}
-(bool) activateAppropriateChild: (bool)activating
{
for(NSWindow* uch in [self childWindows])
{
auto ch = objc_cast<AvnWindow>(uch);
if(ch == nil)
continue;
[ch activateAppropriateChild:false];
return FALSE;
}
if(!activating)
[self makeKeyAndOrderFront:self];
return TRUE;
}
-(bool)shouldTryToHandleEvents
{
for(NSWindow* uch in [self childWindows])
{
auto ch = objc_cast<AvnWindow>(uch);
if(ch == nil)
continue;
return FALSE;
}
return TRUE;
}
-(void)makeKeyWindow
{
if([self activateAppropriateChild: true])
{
[super makeKeyWindow];
}
}
-(void)becomeKeyWindow
{
_parent->BaseEvents->Activated();
[super becomeKeyWindow];
if([self activateAppropriateChild: true])
{
_parent->BaseEvents->Activated();
[super becomeKeyWindow];
}
}
-(void) restoreParentWindow;
{
auto parent = objc_cast<AvnWindow>([self parentWindow]);
if(parent != nil)
{
[parent removeChildWindow:self];
[parent activateAppropriateChild: false];
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
{
return true;

231
nukebuild/Build.cs

@ -0,0 +1,231 @@
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.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<string>(){"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 CreateNugetPackages => _ => _
.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 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<Build>(x => x.Package)
: Execute<Build>(x => x.RunTests);
}

140
nukebuild/BuildParameters.cs

@ -0,0 +1,140 @@
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 NugetRoot { get; }
public AbsolutePath ZipRoot { get; }
public AbsolutePath BinRoot { get; }
public AbsolutePath TestResultsRoot { get; }
public string DirSuffix { get; }
public List<string> 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";
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);
}
private static string GetVersion()
{
var xdoc = XDocument.Load("./build/SharedVersion.props");
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
}
}
}

79
nukebuild/Shims.cs

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Nuke.Common;
using Nuke.Common.IO;
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<string> 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
}
}
}
}

35
nukebuild/_build.csproj

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace></RootNamespace>
<IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="0.12.3" />
<PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
</ItemGroup>
<ItemGroup>
<NukeMetadata Include="**\*.json" Exclude="bin\**;obj\**" />
<NukeExternalFiles Include="**\*.*.ext" Exclude="bin\**;obj\**" />
<None Remove="*.csproj.DotSettings;*.ref.*.txt" />
<!-- Common build related files -->
<None Include="..\build.ps1" />
<None Include="..\build.sh" />
<None Include="..\.nuke" />
<None Include="..\global.json" Condition="Exists('..\global.json')" />
<None Include="..\nuget.config" Condition="Exists('..\nuget.config')" />
<None Include="..\Jenkinsfile" Condition="Exists('..\Jenkinsfile')" />
<None Include="..\appveyor.yml" Condition="Exists('..\appveyor.yml')" />
<None Include="..\.travis.yml" Condition="Exists('..\.travis.yml')" />
<None Include="..\GitVersion.yml" Condition="Exists('..\GitVersion.yml')" />
</ItemGroup>
</Project>

24
nukebuild/_build.csproj.DotSettings

@ -0,0 +1,24 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/ImplicitNullability/EnableFields/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_INTERNAL_MODIFIER/@EntryValue">Implicit</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_PRIVATE_MODIFIER/@EntryValue">Implicit</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">0</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">False</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_ATTRIBUTE_LENGTH_FOR_SAME_LINE/@EntryValue">120</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">IF_OWNER_IS_SINGLE_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">WRAP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

22
packages/Avalonia/Avalonia.csproj

@ -5,6 +5,8 @@
<ItemGroup>
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" EmbedReference="false" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
</ItemGroup>
<PropertyGroup>
@ -27,14 +29,22 @@
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
<_PackageFiles Include="Avalonia.props">
<PackagePath>build/Avalonia.props</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
</ItemGroup>
</Target>
<ItemGroup>
<Content Include="*.props">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="*.targets">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="../../src/Avalonia.Build.Tasks/bin/$(Configuration)/netstandard2.0/Avalonia.Build.Tasks.dll">
<Pack>true</Pack>
<PackagePath>tools\</PackagePath>
</Content>
</ItemGroup>
<Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\CoreLibraries.props" />

3
packages/Avalonia/Avalonia.props

@ -2,5 +2,8 @@
<PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
</Project>

3
packages/Avalonia/Avalonia.targets

@ -0,0 +1,3 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.targets"/>
</Project>

3
packages/Avalonia/AvaloniaBuildTasks.props

@ -0,0 +1,3 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

43
packages/Avalonia/AvaloniaBuildTasks.targets

@ -0,0 +1,43 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)</_AvaloniaUseExternalMSBuild>
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
</PropertyGroup>
<UsingTask TaskName="GenerateAvaloniaResourcesTask"
AssemblyFile="$(AvaloniaBuildTasksLocation)"
/>
<Target Name="AddAvaloniaResources" BeforeTargets="ResolveReferences">
<PropertyGroup>
<AvaloniaResourcesTemporaryFilePath Condition="'$(AvaloniaResourcesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/resources</AvaloniaResourcesTemporaryFilePath>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="$(AvaloniaResourcesTemporaryFilePath)">
<LogicalName>!AvaloniaResources</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
<Target Name="GenerateAvaloniaResources"
BeforeTargets="CoreCompile;CoreResGen"
Inputs="@(AvaloniaResource);$(MSBuildAllProjects)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences">
<GenerateAvaloniaResourcesTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
EmbeddedResources="@(EmbeddedResources)"/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration)"/>
</Target>
<ItemGroup>
<UpToDateCheckInput Include="@(AvaloniaResource)" />
</ItemGroup>
</Project>

6
readme.md

@ -2,9 +2,9 @@
# Avalonia
| Gitter Chat | Build Status (Win, Linux, OSX) | Appveyor Build Status | Open Collective |
|---|---|---|---|
| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) |
| Gitter Chat | Build Status (Win, Linux, OSX) | Open Collective |
|---|---|---|
| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) | [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) |
## About

2
samples/ControlCatalog.Desktop/Program.cs

@ -22,7 +22,7 @@ namespace ControlCatalog
/// This method is needed for IDE previewer infrastructure
/// </summary>
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>().LogToDebug().UsePlatformDetect();
=> AppBuilder.Configure<App>().LogToDebug().UsePlatformDetect().UseReactiveUI();
private static void ConfigureAssetAssembly(AppBuilder builder)
{

10
samples/ControlCatalog.NetCore/Program.cs

@ -23,6 +23,7 @@ namespace ControlCatalog.NetCore
break;
}
}
if (args.Contains("--fbdev"))
AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{
@ -30,7 +31,12 @@ namespace ControlCatalog.NetCore
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
});
else
BuildAvaloniaApp().Start<MainWindow>();
BuildAvaloniaApp().Start(AppMain, args);
}
static void AppMain(Application app, string[] args)
{
app.Run(new MainWindow());
}
/// <summary>
@ -46,4 +52,4 @@ namespace ControlCatalog.NetCore
Console.ReadKey(true);
}
}
}
}

10
samples/ControlCatalog/App.xaml

@ -1,7 +1,9 @@
<Application xmlns="https://github.com/avaloniaui">
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/>
@ -12,6 +14,6 @@
<Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/>
</Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/>
<StyleInclude Source="/SideBar.xaml"/>
</Application.Styles>
</Application>

8
samples/ControlCatalog/ControlCatalog.csproj

@ -6,10 +6,11 @@
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<EmbeddedResource Include="**\*.xaml">
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Assets\*" />
</AvaloniaResource>
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
@ -24,4 +25,5 @@
</ItemGroup>
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

17
samples/ControlCatalog/DecoratedWindow.xaml

@ -1,7 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False">
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">
@ -18,7 +19,13 @@
</StackPanel>
</Grid>
<Border Background="White" Margin="5">
<TextBlock>Hello world!</TextBlock>
<StackPanel>
<TextBlock>Hello world!</TextBlock>
<CheckBox IsChecked="{Binding ElementName=Window, Path=HasSystemDecorations}">Decorated</CheckBox>
<CheckBox IsChecked="{Binding ElementName=Window, Path=CanResize}">CanResize</CheckBox>
</StackPanel>
</Border>
</DockPanel>
<Border Name="TopLeft" Background="Red"/>
@ -30,4 +37,4 @@
<Border Name="Bottom" Background="Blue" Grid.Row="2" Grid.Column="1" />
<Border Name="Left" Background="Blue" Grid.Row="1" />
</Grid>
</Window>
</Window>

69
samples/ControlCatalog/MainView.xaml

@ -1,33 +1,42 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TabControl Classes="sidebar" Name="Sidebar">
<TabControl.PageTransition>
<CrossFade Duration="0.25"/>
</TabControl.PageTransition>
<TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem>
<TabItem Header="Border"><pages:BorderPage/></TabItem>
<TabItem Header="Button"><pages:ButtonPage/></TabItem>
<TabItem Header="ButtonSpinner"><pages:ButtonSpinnerPage/></TabItem>
<TabItem Header="Calendar"><pages:CalendarPage/></TabItem>
<TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
<TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
<TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
<TabItem Header="ContextMenu"><pages:ContextMenuPage/></TabItem>
<TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"><pages:ImagePage/></TabItem>
<TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
<TabItem Header="ListBox"><pages:ListBoxPage/></TabItem>
<TabItem Header="Menu"><pages:MenuPage/></TabItem>
<TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
<TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
</TabControl>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView"
Background="{DynamicResource ThemeBackgroundBrush}"
Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}">
<Grid>
<DropDown x:Name="Themes" SelectedIndex="0" Width="100" Margin="8" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<DropDownItem>Light</DropDownItem>
<DropDownItem>Dark</DropDownItem>
</DropDown>
<TabControl Classes="sidebar" Name="Sidebar">
<TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem>
<TabItem Header="Border"><pages:BorderPage/></TabItem>
<TabItem Header="Button"><pages:ButtonPage/></TabItem>
<TabItem Header="ButtonSpinner"><pages:ButtonSpinnerPage/></TabItem>
<TabItem Header="Calendar"><pages:CalendarPage/></TabItem>
<TabItem Header="Canvas"><pages:CanvasPage/></TabItem>
<TabItem Header="Carousel"><pages:CarouselPage/></TabItem>
<TabItem Header="CheckBox"><pages:CheckBoxPage/></TabItem>
<TabItem Header="ContextMenu"><pages:ContextMenuPage/></TabItem>
<TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"><pages:ImagePage/></TabItem>
<TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
<TabItem Header="ListBox"><pages:ListBoxPage/></TabItem>
<TabItem Header="Menu"><pages:MenuPage/></TabItem>
<TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
<TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
<TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
<TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>
</TabControl>
</Grid>
</UserControl>

17
samples/ControlCatalog/MainView.xaml.cs

@ -2,6 +2,7 @@ using System.Collections;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Platform;
using ControlCatalog.Pages;
@ -27,6 +28,22 @@ namespace ControlCatalog
});
}
var light = AvaloniaXamlLoader.Parse<StyleInclude>(@"<StyleInclude xmlns='https://github.com/avaloniaui' Source='resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default'/>");
var dark = AvaloniaXamlLoader.Parse<StyleInclude>(@"<StyleInclude xmlns='https://github.com/avaloniaui' Source='resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default'/>");
var themes = this.Find<DropDown>("Themes");
themes.SelectionChanged += (sender, e) =>
{
switch (themes.SelectedIndex)
{
case 0:
Styles[0] = light;
break;
case 1:
Styles[0] = dark;
break;
}
};
Styles.Add(light);
}
private void InitializeComponent()

8
samples/ControlCatalog/MainWindow.xaml

@ -1,6 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog"
xmlns:local="clr-namespace:ControlCatalog">
Icon="/Assets/test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainWindow">
<local:MainView/>
</Window>
</Window>

4
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.AutoCompleteBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">AutoCompleteBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock>

4
samples/ControlCatalog/Pages/BorderPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.BorderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Border</TextBlock>
<TextBlock Classes="h2">A control which decorates a child with a border and background</TextBlock>

3
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Button</TextBlock>
<TextBlock Classes="h2">A button control</TextBlock>

3
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ButtonSpinnerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ButtonSpinner</TextBlock>

3
samples/ControlCatalog/Pages/CalendarPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CalendarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Calendar</TextBlock>
<TextBlock Classes="h2">A calendar control for selecting dates</TextBlock>

4
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CanvasPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Canvas</TextBlock>
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>

10
samples/ControlCatalog/Pages/CarouselPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CarouselPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Carousel</TextBlock>
<TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock>
@ -11,9 +13,9 @@
<Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Vertical" />
</Carousel.PageTransition>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg"/>
<Image Source="/Assets/delicate-arch-896885_640.jpg"/>
<Image Source="/Assets/hirsch-899118_640.jpg"/>
<Image Source="/Assets/maple-leaf-888807_640.jpg"/>
</Carousel>
<Button Name="right" VerticalAlignment="Center" Padding="20">
<Path Data="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" Fill="Black"/>

3
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CheckBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</TextBlock>

6
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContextMenuPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Context Menu</TextBlock>
<TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock>
@ -19,7 +21,7 @@
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">

3
samples/ControlCatalog/Pages/DatePickerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DatePickerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>

9
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -1,12 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DialogsPage">
<StackPanel Orientation="Vertical" Spacing="4" Margin="4">
<Button Name="OpenFile">Open File</Button>
<Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button>
<StackPanel Orientation="Horizontal">
<CheckBox Name="IsModal" IsChecked="True"/>
<TextBlock>Modal to window</TextBlock>
</StackPanel>
<Button Name="DecoratedWindow">Decorated window</Button>
<Button Name="Dialog">Dialog</Button>
</StackPanel>
</UserControl>

14
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -31,12 +31,18 @@ namespace ControlCatalog.Pages
}.ShowAsync(GetWindow());
};
this.FindControl<Button>("DecoratedWindow").Click += delegate
{
new DecoratedWindow().Show();
};
{
new DecoratedWindow().ShowDialog(GetWindow());
};
this.FindControl<Button>("Dialog").Click += delegate
{
new MainWindow().ShowDialog(GetWindow());
};
}
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked.Value ? (Window)this.VisualRoot : null;
Window GetWindow() => (Window)this.VisualRoot;
private void InitializeComponent()
{

4
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DragAndDropPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Drag+Drop</TextBlock>
<TextBlock Classes="h2">Example of Drag+Drop capabilities</TextBlock>

4
samples/ControlCatalog/Pages/DropDownPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DropDownPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DropDown</TextBlock>
<TextBlock Classes="h2">A drop-down list.</TextBlock>

4
samples/ControlCatalog/Pages/ExpanderPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ExpanderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Expander</TextBlock>
<TextBlock Classes="h2">Expands to show nested content</TextBlock>

14
samples/ControlCatalog/Pages/ImagePage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ImagePage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Image</TextBlock>
<TextBlock Classes="h2">Displays an image</TextBlock>
@ -9,28 +11,28 @@
Spacing="16">
<StackPanel Orientation="Vertical">
<TextBlock>No Stretch</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="None"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Fill</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="Fill"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>Uniform</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="Uniform"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock>UniformToFill</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"
<Image Source="/Assets/delicate-arch-896885_640.jpg"
Width="100" Height="200"
Stretch="UniformToFill"/>
</StackPanel>
@ -40,4 +42,4 @@
<Image Name="Icon" Width="100" Height="200" Stretch="None" />
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

5
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.LayoutTransformControlPage">
<DockPanel>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto" Margin="16" DockPanel.Dock="Top">
<TextBlock Grid.Column="0" Grid.Row="0">Rotation</TextBlock>
@ -23,4 +24,4 @@
</LayoutTransformControl>
</Grid>
</DockPanel>
</UserControl>
</UserControl>

4
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ListBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ListBox</TextBlock>
<TextBlock Classes="h2">Hosts a collection of ListBoxItem.</TextBlock>

6
samples/ControlCatalog/Pages/MenuPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.MenuPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Menu</TextBlock>
<TextBlock Classes="h2">A window menu</TextBlock>
@ -19,7 +21,7 @@
</MenuItem>
<MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/>
<Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Menu Item with _Checkbox">

2
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@ -83,7 +83,7 @@ namespace ControlCatalog.Pages
public async Task Open()
{
var dialog = new OpenFileDialog();
var result = await dialog.ShowAsync();
var result = await dialog.ShowAsync(App.Current.MainWindow);
if (result != null)
{

19
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -1,12 +1,13 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.NumericUpDownPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock>
<TextBlock Margin="2" Classes="h2" TextWrapping="Wrap">Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.</TextBlock>
<TextBlock Margin="2,5,2,2" FontSize="14" FontWeight="Bold">Features:</TextBlock>
<Grid Margin="2" ColumnDefinitions="Auto,Auto,Auto,Auto" RowDefinitions="Auto,Auto">
<Grid Grid.Row="0" Grid.Column="0" ColumnDefinitions="Auto, Auto" RowDefinitions="35,35,35,35,35">
<Grid Grid.Row="0" Grid.Column="0" ColumnDefinitions="Auto, Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">ShowButtonSpinner:</TextBlock>
<CheckBox Grid.Row="0" Grid.Column="1" IsChecked="{Binding #upDown.ShowButtonSpinner}" VerticalAlignment="Center" Margin="2"/>
@ -20,7 +21,7 @@
<CheckBox Grid.Row="3" Grid.Column="1" IsChecked="{Binding #upDown.ClipValueToMinMax}" VerticalAlignment="Center" Margin="2"/>
</Grid>
<Grid Grid.Row="0" Grid.Column="1" Margin="10,2,2,2" ColumnDefinitions="Auto, 120" RowDefinitions="35,35,35,35,35">
<Grid Grid.Row="0" Grid.Column="1" Margin="10,2,2,2" ColumnDefinitions="Auto, 120" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">FormatString:</TextBlock>
<DropDown Grid.Row="0" Grid.Column="1" Items="{Binding Formats}" SelectedItem="{Binding SelectedFormat}"
VerticalAlignment="Center" Margin="2">
@ -49,22 +50,22 @@
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Margin="2">Text:</TextBlock>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding #upDown.Text}" VerticalAlignment="Center" Margin="2" />
</Grid>
<Grid Grid.Row="0" Grid.Column="2" Margin="10,2,2,2" RowDefinitions="35,35,35,35,35" ColumnDefinitions="Auto, 120">
<Grid Grid.Row="0" Grid.Column="2" Margin="10,2,2,2" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, 120">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Minimum:</TextBlock>
<NumericUpDown Grid.Row="0" Grid.Column="1" Value="{Binding #upDown.Minimum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/>
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" Width="70" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Maximum:</TextBlock>
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding #upDown.Maximum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/>
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" Width="70" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Increment:</TextBlock>
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding #upDown.Increment}" VerticalAlignment="Center"
Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/>
Margin="2" Width="70" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock>
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
Height="25" Margin="2" Width="70" HorizontalAlignment="Center"/>
Margin="2" Width="70" HorizontalAlignment="Center"/>
</Grid>
</Grid>
@ -72,7 +73,7 @@
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10">
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock>
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Height="25" Width="100"
CultureInfo="en-US" VerticalAlignment="Center" Width="100"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>

6
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock>
@ -21,4 +23,4 @@
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

5
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.RadioButtonPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
@ -37,4 +38,4 @@
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/SliderPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.SliderPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Slider</TextBlock>
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
@ -18,4 +20,4 @@
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

100
samples/ControlCatalog/Pages/TabControlPage.xaml

@ -0,0 +1,100 @@
<UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.TabControlPage"
xmlns="https://github.com/avaloniaui">
<DockPanel>
<TextBlock
DockPanel.Dock="Top"
Classes="h1"
Text="TabControl"
Margin="4">
</TextBlock>
<TextBlock
DockPanel.Dock="Top"
Classes="h2"
Text="A tab control that displays a tab strip along with the content of the selected tab"
Margin="4">
</TextBlock>
<Grid
ColumnDefinitions="*,*"
RowDefinitions="*,100">
<DockPanel
Grid.Column="0"
Margin="4">
<TextBlock
DockPanel.Dock="Top"
Classes="h1"
Text="From Inline TabItems">
</TextBlock>
<TabControl
Margin="0 16"
TabStripPlacement="{Binding TabPlacement}">
<TabItem Header="Arch">
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock>This is the first page in the TabControl.</TextBlock>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Width="300"/>
</StackPanel>
</TabItem>
<TabItem Header="Leaf">
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock>This is the second page in the TabControl.</TextBlock>
<Image Source="/Assets/maple-leaf-888807_640.jpg" Width="300"/>
</StackPanel>
</TabItem>
<TabItem Header="Disabled" IsEnabled="False">
<TextBlock>You should not see this.</TextBlock>
</TabItem>
</TabControl>
</DockPanel>
<DockPanel
Grid.Column="1"
Margin="4">
<TextBlock
DockPanel.Dock="Top"
Classes="h1"
Text="From DataTemplate">
</TextBlock>
<TabControl
Items="{Binding Tabs}"
Margin="0 16"
TabStripPlacement="{Binding TabPlacement}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Header}">
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Text="{Binding Text}"/>
<Image Source="{Binding Image}" Width="300"/>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.Styles>
<Style Selector="TabItem">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
</Style>
</TabControl.Styles>
</TabControl>
</DockPanel>
<StackPanel
Grid.Row="1"
Grid.ColumnSpan="2"
Orientation="Horizontal"
Spacing="8"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Tab Placement:</TextBlock>
<DropDown SelectedIndex="{Binding TabPlacement, Mode=TwoWay}">
<DropDownItem>Left</DropDownItem>
<DropDownItem>Bottom</DropDownItem>
<DropDownItem>Right</DropDownItem>
<DropDownItem>Top</DropDownItem>
</DropDown>
</StackPanel>
</Grid>
</DockPanel>
</UserControl>

80
samples/ControlCatalog/Pages/TabControlPage.xaml.cs

@ -0,0 +1,80 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using ReactiveUI;
namespace ControlCatalog.Pages
{
using System.Collections.Generic;
public class TabControlPage : UserControl
{
public TabControlPage()
{
InitializeComponent();
DataContext = new PageViewModel
{
Tabs = new[]
{
new TabItemViewModel
{
Header = "Arch",
Text = "This is the first templated tab page.",
Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"),
},
new TabItemViewModel
{
Header = "Leaf",
Text = "This is the second templated tab page.",
Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"),
},
new TabItemViewModel
{
Header = "Disabled",
Text = "You should not see this.",
IsEnabled = false,
},
},
TabPlacement = Dock.Top,
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private IBitmap LoadBitmap(string uri)
{
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
return new Bitmap(assets.Open(new Uri(uri)));
}
private class PageViewModel : ReactiveObject
{
private Dock _tabPlacement;
public TabItemViewModel[] Tabs { get; set; }
public Dock TabPlacement
{
get { return _tabPlacement; }
set { this.RaiseAndSetIfChanged(ref _tabPlacement, value); }
}
}
private class TabItemViewModel
{
public string Header { get; set; }
public string Text { get; set; }
public IBitmap Image { get; set; }
public bool IsEnabled { get; set; } = true;
}
}
}

15
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.TextBoxPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TextBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock>
@ -33,11 +35,20 @@
Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;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." />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">resm fonts</TextBlock>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-*.ttf?assembly=ControlCatalog#Source Sans Pro"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">res fonts</TextBlock>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-Italic.ttf#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ToolTipPage">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Classes="h1">ToolTip</TextBlock>
@ -38,4 +40,4 @@
</Border>
</Grid>
</StackPanel>
</UserControl>
</UserControl>

6
samples/ControlCatalog/Pages/TreeViewPage.xaml

@ -1,4 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.TreeViewPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TreeView</TextBlock>
<TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock>
@ -16,4 +18,4 @@
</TreeView>
</StackPanel>
</StackPanel>
</UserControl>
</UserControl>

66
samples/ControlCatalog/Pages/ViewboxPage.xaml

@ -0,0 +1,66 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ViewboxPage">
<UserControl.Resources>
<StreamGeometry x:Key="Acorn">
F1 M 16.6309,18.6563C 17.1309,
8.15625 29.8809,14.1563 29.8809,
14.1563C 30.8809,11.1563 34.1308,
11.4063 34.1308,11.4063C 33.5,12
34.6309,13.1563 34.6309,13.1563C
32.1309,13.1562 31.1309,14.9062
31.1309,14.9062C 41.1309,23.9062
32.6309,27.9063 32.6309,27.9062C
24.6309,24.9063 21.1309,22.1562
16.6309,18.6563 Z M 16.6309,19.9063C
21.6309,24.1563 25.1309,26.1562
31.6309,28.6562C 31.6309,28.6562
26.3809,39.1562 18.3809,36.1563C
18.3809,36.1563 18,38 16.3809,36.9063C
15,36 16.3809,34.9063 16.3809,34.9063C
16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z
</StreamGeometry>
</UserControl.Resources>
<Grid RowDefinitions="Auto,*">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Viewbox</TextBlock>
<TextBlock Classes="h2">A control used to scale single child.</TextBlock>
</StackPanel>
<Grid ColumnDefinitions="Auto,*,*"
RowDefinitions="*,*,*,*"
Grid.Row="1" Margin="48"
MaxWidth="400">
<TextBlock Grid.Row="0" VerticalAlignment="Center">None</TextBlock>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Fill</TextBlock>
<TextBlock Grid.Row="2" VerticalAlignment="Center">Uniform</TextBlock>
<TextBlock Grid.Row="3" VerticalAlignment="Center">UniformToFill</TextBlock>
<Viewbox Grid.Row="0" Grid.Column="1" Stretch="None">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="1" Stretch="Fill">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="2" Grid.Column="1" Stretch="Uniform">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="3" Grid.Column="1" Stretch="UniformToFill">
<TextBlock>Hello World!</TextBlock>
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2" Stretch="None">
<Path Fill="Blue" Data="{StaticResource Acorn}"/>
</Viewbox>
<Viewbox Grid.Row="1" Grid.Column="2" Stretch="Fill">
<Path Fill="Blue" Data="{StaticResource Acorn}"/>
</Viewbox>
<Viewbox Grid.Row="2" Grid.Column="2" Stretch="Uniform">
<Path Fill="Blue" Data="{StaticResource Acorn}"/>
</Viewbox>
<Viewbox Grid.Row="3" Grid.Column="2" Stretch="UniformToFill">
<Path Fill="Blue" Data="{StaticResource Acorn}"/>
</Viewbox>
</Grid>
</Grid>
</UserControl>

18
samples/ControlCatalog/Pages/ViewboxPage.xaml.cs

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ViewboxPage : UserControl
{
public ViewboxPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

113
samples/ControlCatalog/SideBar.xaml

@ -1,52 +1,67 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Style Selector="TabControl.sidebar">
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<ScrollViewer MinWidth="190" Background="{DynamicResource ThemeAccentBrush}" DockPanel.Dock="Left">
<TabStrip Name="PART_TabStrip"
MemberSelector="{x:Static TabControl.HeaderSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}">
<TabStrip.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</TabStrip.ItemsPanel>
</TabStrip>
</ScrollViewer>
<Carousel Name="PART_Content"
Margin="8 0 0 0"
MemberSelector="{x:Static TabControl.ContentSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding SelectedIndex}"
PageTransition="{TemplateBinding PageTransition}"
Grid.Row="1"/>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.SideBar">
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
MemberSelector="{TemplateBinding MemberSelector}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_Content"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar TabStripItem">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar TabStripItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar TabStripItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style>
</Styles>

5
samples/RenderDemo/MainWindow.xaml

@ -24,9 +24,6 @@
</MenuItem>
</Menu>
<TabControl Classes="sidebar">
<TabControl.PageTransition>
<CrossFade Duration="0.25"/>
</TabControl.PageTransition>
<TabItem Header="Animations">
<pages:AnimationsPage/>
</TabItem>
@ -38,4 +35,4 @@
</TabItem>
</TabControl>
</DockPanel>
</Window>
</Window>

113
samples/RenderDemo/SideBar.xaml

@ -1,53 +1,66 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:a="clr-namespace:Avalonia.Media.Animations;assembly=Avalonia.Media.Animations">
<Style Selector="TabControl.sidebar">
<Setter Property="Template">
<ControlTemplate>
<DockPanel>
<ScrollViewer MinWidth="190" Background="{DynamicResource ThemeAccentBrush}" DockPanel.Dock="Left">
<TabStrip Name="PART_TabStrip"
MemberSelector="{x:Static TabControl.HeaderSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}">
<TabStrip.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</TabStrip.ItemsPanel>
</TabStrip>
</ScrollViewer>
<Carousel Name="PART_Content"
Margin="8 0 0 0"
MemberSelector="{x:Static TabControl.ContentSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding SelectedIndex}"
PageTransition="{TemplateBinding PageTransition}"
Grid.Row="1"/>
</DockPanel>
</ControlTemplate>
</Setter>
</Style>
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}"
MemberSelector="{TemplateBinding MemberSelector}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_Content"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar TabStripItem">
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar TabStripItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar TabStripItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.5"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style>
</Styles>

1
scripts/ReplaceNugetCache.sh

@ -4,5 +4,6 @@
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.native/$1/lib/netstandard2.0/

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

@ -6,6 +6,9 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/>
</ItemGroup>
<Import Project="..\..\build\Base.props" />
<Import Project="..\..\build\Binding.props" />
<Import Project="..\..\build\Rx.props" />

12
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -88,18 +88,21 @@ namespace Avalonia.Data.Core
_subscriber(value);
}
protected void ValueChanged(object value)
protected void ValueChanged(object value) => ValueChanged(value, true);
private void ValueChanged(object value, bool notify)
{
var notification = value as BindingNotification;
if (notification == null)
{
LastValue = new WeakReference(value);
if (Next != null)
{
Next.Target = new WeakReference(value);
Next.Target = LastValue;
}
else
else if (notify)
{
_subscriber(value);
}
@ -110,7 +113,7 @@ namespace Avalonia.Data.Core
if (Next != null)
{
Next.Target = new WeakReference(notification.Value);
Next.Target = LastValue;
}
if (Next == null || notification.Error != null)
@ -136,6 +139,7 @@ namespace Avalonia.Data.Core
}
else
{
ValueChanged(AvaloniaProperty.UnsetValue, notify:false);
_listening = false;
}
}

2
src/Avalonia.Base/Platform/IAssetLoader.cs

@ -66,6 +66,6 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
IEnumerable<(string absolutePath, Assembly assembly)> GetAssets(Uri uri);
IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri);
}
}

64
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<AvaloniaResourcesIndexEntry> 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<AvaloniaResourcesIndexEntry> 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<AvaloniaResourcesIndexEntry> Entries { get; set; } = new List<AvaloniaResourcesIndexEntry>();
}
[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; }
}
}

19
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants>
<Packable>false</Packable>
</PropertyGroup>
<ItemGroup>
<Compile Include="../Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs">
<Link>Shared/AvaloniaResourcesIndex.cs</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile>
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" />
</ItemGroup>
</Project>

9
src/Avalonia.Build.Tasks/BuildEngineErrorCode.cs

@ -0,0 +1,9 @@
namespace Avalonia.Build.Tasks
{
public enum BuildEngineErrorCode
{
InvalidXAML = 1,
DuplicateXClass = 2,
LegacyResmScheme = 3,
}
}

21
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"));
}
}
}

159
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<Source> BuildResourceSources() => Resources.Select(r => new Source(r.ItemSpec, Root)).ToList();
private void Pack(Stream output, List<Source> sources)
{
var offsets = new Dictionary<Source, int>();
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<Source> sources)
{
var typeToXamlIndex = new Dictionary<string, string>();
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; }
}
}

20
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
};
}
}
}

9
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);
}
/// <summary>
/// Sets up the platform-specific services for the application, but does not run it.
/// </summary>

2
src/Avalonia.Controls/ColumnDefinition.cs

@ -88,4 +88,4 @@ namespace Avalonia.Controls
set { SetValue(WidthProperty, value); }
}
}
}
}

87
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
{
/// <summary>
/// A drop-down list control.
/// </summary>
public class DropDown : SelectingItemsControl
{
/// <summary>
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
/// </summary>
private static readonly FuncTemplate<IPanel> DefaultPanel =
new FuncTemplate<IPanel>(() => new VirtualizingStackPanel());
/// <summary>
/// Defines the <see cref="IsDropDownOpen"/> property.
/// </summary>
@ -39,6 +47,12 @@ namespace Avalonia.Controls
public static readonly DirectProperty<DropDown, object> SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect<DropDown, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
/// <summary>
/// Defines the <see cref="VirtualizationMode"/> property.
/// </summary>
public static readonly StyledProperty<ItemVirtualizationMode> VirtualizationModeProperty =
ItemsPresenter.VirtualizationModeProperty.AddOwner<DropDown>();
private bool _isDropDownOpen;
private Popup _popup;
private object _selectionBoxItem;
@ -48,6 +62,7 @@ namespace Avalonia.Controls
/// </summary>
static DropDown()
{
ItemsPanelProperty.OverrideDefaultValue<DropDown>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<DropDown>(true);
SelectedItemProperty.Changed.AddClassHandler<DropDown>(x => x.SelectedItemChanged);
KeyDownEvent.AddClassHandler<DropDown>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
@ -80,6 +95,15 @@ namespace Avalonia.Controls
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
}
/// <summary>
/// Gets or sets the virtualization mode for the items.
/// </summary>
public ItemVirtualizationMode VirtualizationMode
{
get { return GetValue(VirtualizationModeProperty); }
set { SetValue(VirtualizationModeProperty, value); }
}
/// <inheritdoc/>
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;
}
}
}
/// <inheritdoc/>
@ -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<Popup>("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;
}
}

32
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
{
/// <summary>
/// A selectable item in a <see cref="DropDown"/>.
/// </summary>
public class DropDownItem : ContentControl, ISelectable
public class DropDownItem : ListBoxItem
{
/// <summary>
/// Defines the <see cref="IsSelected"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsSelectedProperty =
AvaloniaProperty.Register<DropDownItem, bool>(nameof(IsSelected));
/// <summary>
/// Initializes static members of the <see cref="DropDownItem"/> class.
/// </summary>
static DropDownItem()
{
FocusableProperty.OverrideDefaultValue<DropDownItem>(true);
}
public DropDownItem()
{
this.GetObservable(DropDownItem.IsFocusedProperty).Subscribe(focused =>
{
PseudoClasses.Set(":selected", focused);
});
}
/// <summary>
/// Gets or sets the selection state of the item.
/// </summary>
public bool IsSelected
{
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
this.GetObservable(DropDownItem.IsFocusedProperty).Where(focused => focused)
.Subscribe(_ => (Parent as DropDown)?.ItemFocused(this));
}
}
}

59
src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs

@ -0,0 +1,59 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls.Generators
{
public class TabItemContainerGenerator : ItemContainerGenerator<TabItem>
{
public TabItemContainerGenerator(TabControl owner)
: base(owner, ContentControl.ContentProperty, ContentControl.ContentTemplateProperty)
{
Owner = owner;
}
public new TabControl Owner { get; }
protected override IControl CreateContainer(object item)
{
var tabItem = (TabItem)base.CreateContainer(item);
tabItem.ParentTabControl = Owner;
tabItem[~TabControl.TabStripPlacementProperty] = Owner[~TabControl.TabStripPlacementProperty];
if (tabItem.HeaderTemplate == null)
{
tabItem[~HeaderedContentControl.HeaderTemplateProperty] = Owner[~ItemsControl.ItemTemplateProperty];
}
if (tabItem.Header == null)
{
if (item is IHeadered headered)
{
tabItem.Header = headered.Header;
}
else
{
if (!(tabItem.DataContext is IControl))
{
tabItem.Header = tabItem.DataContext;
}
}
}
if (!(tabItem.Content is IControl))
{
tabItem[~ContentControl.ContentTemplateProperty] = Owner[~TabControl.ContentTemplateProperty];
}
if (tabItem.Content == null)
{
tabItem[~ContentControl.ContentProperty] = tabItem[~StyledElement.DataContextProperty];
}
return tabItem;
}
}
}

154
src/Avalonia.Controls/Grid.cs

@ -3,10 +3,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Collections;
using Avalonia.Controls.Utils;
using Avalonia.VisualTree;
using JetBrains.Annotations;
namespace Avalonia.Controls
@ -44,6 +47,24 @@ namespace Avalonia.Controls
public static readonly AttachedProperty<int> RowSpanProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, int>("RowSpan", 1);
public static readonly AttachedProperty<bool> IsSharedSizeScopeProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, bool>("IsSharedSizeScope", false);
protected override void OnMeasureInvalidated()
{
base.OnMeasureInvalidated();
_sharedSizeHost?.InvalidateMeasure(this);
}
private SharedSizeScopeHost _sharedSizeHost;
/// <summary>
/// Defines the SharedSizeScopeHost private property.
/// The ampersands are used to make accessing the property via xaml inconvenient.
/// </summary>
internal static readonly AttachedProperty<SharedSizeScopeHost> s_sharedSizeScopeHostProperty =
AvaloniaProperty.RegisterAttached<Grid, Control, SharedSizeScopeHost>("&&SharedSizeScopeHost");
private ColumnDefinitions _columnDefinitions;
private RowDefinitions _rowDefinitions;
@ -51,6 +72,13 @@ namespace Avalonia.Controls
static Grid()
{
AffectsParentMeasure<Grid>(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty);
IsSharedSizeScopeProperty.Changed.AddClassHandler<Control>(IsSharedSizeScopeChanged);
}
public Grid()
{
this.AttachedToVisualTree += Grid_AttachedToVisualTree;
this.DetachedFromVisualTree += Grid_DetachedFromVisualTree;
}
/// <summary>
@ -77,6 +105,7 @@ namespace Avalonia.Controls
_columnDefinitions = value;
_columnDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure());
_columnDefinitions.CollectionChanged += (_, __) => InvalidateMeasure();
}
}
@ -104,6 +133,7 @@ namespace Avalonia.Controls
_rowDefinitions = value;
_rowDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure());
_rowDefinitions.CollectionChanged += (_, __) => InvalidateMeasure();
}
}
@ -271,6 +301,11 @@ namespace Avalonia.Controls
_rowLayoutCache = rowLayout;
_columnLayoutCache = columnLayout;
if (_sharedSizeHost?.ParticipatesInScope(this) ?? false)
{
_sharedSizeHost.UpdateMeasureStatus(this, rowResult, columnResult);
}
return new Size(columnResult.DesiredLength, rowResult.DesiredLength);
// Measure each child only once.
@ -319,9 +354,21 @@ namespace Avalonia.Controls
var (safeColumns, safeRows) = GetSafeColumnRows();
var columnLayout = _columnLayoutCache;
var rowLayout = _rowLayoutCache;
var rowCache = _rowMeasureCache;
var columnCache = _columnMeasureCache;
if (_sharedSizeHost?.ParticipatesInScope(this) ?? false)
{
(rowCache, columnCache) = _sharedSizeHost.HandleArrange(this, _rowMeasureCache, _columnMeasureCache);
rowCache = rowLayout.Measure(finalSize.Height, rowCache.LeanLengthList);
columnCache = columnLayout.Measure(finalSize.Width, columnCache.LeanLengthList);
}
// Calculate for arrange result.
var columnResult = columnLayout.Arrange(finalSize.Width, _columnMeasureCache);
var rowResult = rowLayout.Arrange(finalSize.Height, _rowMeasureCache);
var columnResult = columnLayout.Arrange(finalSize.Width, columnCache);
var rowResult = rowLayout.Arrange(finalSize.Height, rowCache);
// Arrange the children.
foreach (var child in Children.OfType<Control>())
{
@ -350,6 +397,73 @@ namespace Avalonia.Controls
return finalSize;
}
/// <summary>
/// Tests whether this grid belongs to a shared size scope.
/// </summary>
/// <returns>True if the grid is registered in a shared size scope.</returns>
internal bool HasSharedSizeScope()
{
return _sharedSizeHost != null;
}
/// <summary>
/// Called when the SharedSizeScope for a given grid has changed.
/// Unregisters the grid from it's current scope and finds a new one (if any)
/// </summary>
/// <remarks>
/// This method, while not efficient, correctly handles nested scopes, with any order of scope changes.
/// </remarks>
internal void SharedScopeChanged()
{
_sharedSizeHost?.UnegisterGrid(this);
_sharedSizeHost = null;
var scope = this.GetVisualAncestors().OfType<Control>()
.FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty));
if (scope != null)
{
_sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty);
_sharedSizeHost.RegisterGrid(this);
}
InvalidateMeasure();
}
/// <summary>
/// Callback when a grid is attached to the visual tree. Finds the innermost SharedSizeScope and registers the grid
/// in it.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event arguments.</param>
private void Grid_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
var scope =
new Control[] { this }.Concat(this.GetVisualAncestors().OfType<Control>())
.FirstOrDefault(c => c.GetValue(IsSharedSizeScopeProperty));
if (_sharedSizeHost != null)
throw new AvaloniaInternalException("Shared size scope already present when attaching to visual tree!");
if (scope != null)
{
_sharedSizeHost = scope.GetValue(s_sharedSizeScopeHostProperty);
_sharedSizeHost.RegisterGrid(this);
}
}
/// <summary>
/// Callback when a grid is detached from the visual tree. Unregisters the grid from its SharedSizeScope if any.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event arguments.</param>
private void Grid_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
_sharedSizeHost?.UnegisterGrid(this);
_sharedSizeHost = null;
}
/// <summary>
/// Get the safe column/columnspan and safe row/rowspan.
/// This method ensures that none of the children has a column/row outside the bounds of the definitions.
@ -426,5 +540,41 @@ namespace Avalonia.Controls
return value;
}
/// <summary>
/// Called when the value of <see cref="Grid.IsSharedSizeScopeProperty"/> changes for a control.
/// </summary>
/// <param name="source">The control that triggered the change.</param>
/// <param name="arg2">Change arguments.</param>
private static void IsSharedSizeScopeChanged(Control source, AvaloniaPropertyChangedEventArgs arg2)
{
var shouldDispose = (arg2.OldValue is bool d) && d;
if (shouldDispose)
{
var host = source.GetValue(s_sharedSizeScopeHostProperty) as SharedSizeScopeHost;
if (host == null)
throw new AvaloniaInternalException("SharedScopeHost wasn't set when IsSharedSizeScope was true!");
host.Dispose();
source.ClearValue(s_sharedSizeScopeHostProperty);
}
var shouldAssign = (arg2.NewValue is bool a) && a;
if (shouldAssign)
{
if (source.GetValue(s_sharedSizeScopeHostProperty) != null)
throw new AvaloniaInternalException("SharedScopeHost was already set when IsSharedSizeScope is only now being set to true!");
source.SetValue(s_sharedSizeScopeHostProperty, new SharedSizeScopeHost());
}
// if the scope has changed, notify the descendant grids that they need to update.
if (source.GetVisualRoot() != null && shouldAssign || shouldDispose)
{
var participatingGrids = new[] { source }.Concat(source.GetVisualDescendants()).OfType<Grid>();
foreach (var grid in participatingGrids)
grid.SharedScopeChanged();
}
}
}
}

73
src/Avalonia.Controls/GridSplitter.cs

@ -44,26 +44,52 @@ namespace Avalonia.Controls
protected override void OnDragDelta(VectorEventArgs e)
{
// WPF doesn't change anything when spliter is in the last row/column
// but resizes the splitter row/column when it's the first one.
// this is different, but more internally consistent.
if (_prevDefinition == null || _nextDefinition == null)
return;
var delta = _orientation == Orientation.Vertical ? e.Vector.X : e.Vector.Y;
double max;
double min;
GetDeltaConstraints(out min, out max);
delta = Math.Min(Math.Max(delta, min), max);
foreach (var definition in _definitions)
var prevIsStar = IsStar(_prevDefinition);
var nextIsStar = IsStar(_nextDefinition);
if (prevIsStar && nextIsStar)
{
if (definition == _prevDefinition)
{
SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta);
}
else if (definition == _nextDefinition)
{
SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
else if (IsStar(definition))
foreach (var definition in _definitions)
{
SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars.
if (definition == _prevDefinition)
{
SetLengthInStars(_prevDefinition, GetActualLength(_prevDefinition) + delta);
}
else if (definition == _nextDefinition)
{
SetLengthInStars(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
else if (IsStar(definition))
{
SetLengthInStars(definition, GetActualLength(definition)); // same size but in stars.
}
}
}
else if (prevIsStar)
{
SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
else if (nextIsStar)
{
SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
}
else
{
SetLength(_prevDefinition, GetActualLength(_prevDefinition) + delta);
SetLength(_nextDefinition, GetActualLength(_nextDefinition) - delta);
}
}
private double GetActualLength(DefinitionBase definition)
@ -71,7 +97,7 @@ namespace Avalonia.Controls
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.ActualWidth ?? ((RowDefinition) definition).ActualHeight;
return columnDefinition?.ActualWidth ?? ((RowDefinition)definition).ActualHeight;
}
private double GetMinLength(DefinitionBase definition)
@ -79,7 +105,7 @@ namespace Avalonia.Controls
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MinWidth ?? ((RowDefinition) definition).MinHeight;
return columnDefinition?.MinWidth ?? ((RowDefinition)definition).MinHeight;
}
private double GetMaxLength(DefinitionBase definition)
@ -87,13 +113,13 @@ namespace Avalonia.Controls
if (definition == null)
return 0;
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.MaxWidth ?? ((RowDefinition) definition).MaxHeight;
return columnDefinition?.MaxWidth ?? ((RowDefinition)definition).MaxHeight;
}
private bool IsStar(DefinitionBase definition)
{
var columnDefinition = definition as ColumnDefinition;
return columnDefinition?.Width.IsStar ?? ((RowDefinition) definition).Height.IsStar;
return columnDefinition?.Width.IsStar ?? ((RowDefinition)definition).Height.IsStar;
}
private void SetLengthInStars(DefinitionBase definition, double value)
@ -105,7 +131,20 @@ namespace Avalonia.Controls
}
else
{
((RowDefinition) definition).Height = new GridLength(value, GridUnitType.Star);
((RowDefinition)definition).Height = new GridLength(value, GridUnitType.Star);
}
}
private void SetLength(DefinitionBase definition, double value)
{
var columnDefinition = definition as ColumnDefinition;
if (columnDefinition != null)
{
columnDefinition.Width = new GridLength(value);
}
else
{
((RowDefinition)definition).Height = new GridLength(value);
}
}
@ -160,7 +199,7 @@ namespace Avalonia.Controls
}
if (_grid.Children.OfType<Control>() // Decision based on other controls in the same column
.Where(c => Grid.GetColumn(c) == col)
.Any(c => c.GetType() != typeof (GridSplitter)))
.Any(c => c.GetType() != typeof(GridSplitter)))
{
return Orientation.Horizontal;
}

2
src/Avalonia.Controls/ItemsControl.cs

@ -249,8 +249,6 @@ namespace Avalonia.Controls
if (containerControl != null)
{
((ISetLogicalParent)containerControl).SetParent(this);
containerControl.SetValue(TemplatedParentProperty, null);
containerControl.UpdateChild();
if (containerControl.Child != null)

41
src/Avalonia.Controls/LayoutTransformControl.cs

@ -5,28 +5,30 @@
// http://silverlight.codeplex.com/SourceControl/changeset/view/74775#Release/Silverlight4/Source/Controls.Layout.Toolkit/LayoutTransformer/LayoutTransformer.cs
//
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.VisualTree;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Media;
namespace Avalonia.Controls
{
/// <summary>
/// Control that implements support for transformations as if applied by LayoutTransform.
/// </summary>
public class LayoutTransformControl : ContentControl
public class LayoutTransformControl : Decorator
{
public static readonly AvaloniaProperty<Transform> LayoutTransformProperty =
AvaloniaProperty.Register<LayoutTransformControl, Transform>(nameof(LayoutTransform));
static LayoutTransformControl()
{
ClipToBoundsProperty.OverrideDefaultValue<LayoutTransformControl>(true);
LayoutTransformProperty.Changed
.AddClassHandler<LayoutTransformControl>(x => x.OnLayoutTransformChanged);
ChildProperty.Changed
.AddClassHandler<LayoutTransformControl>(x => x.OnChildChanged);
}
/// <summary>
@ -38,8 +40,7 @@ namespace Avalonia.Controls
set { SetValue(LayoutTransformProperty, value); }
}
public Control TransformRoot => _transformRoot ??
(_transformRoot = this.GetVisualChildren().OfType<Control>().FirstOrDefault());
public IControl TransformRoot => Child;
/// <summary>
/// Provides the behavior for the "Arrange" pass of layout.
@ -132,16 +133,8 @@ namespace Avalonia.Controls
return transformedDesiredSize;
}
/// <summary>
/// Builds the visual tree for the LayoutTransformerControl when a new
/// template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
private void OnChildChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnTemplateApplied(e);
_matrixTransform = new MatrixTransform();
if (null != TransformRoot)
{
TransformRoot.RenderTransform = _matrixTransform;
@ -169,14 +162,14 @@ namespace Avalonia.Controls
/// <summary>
/// RenderTransform/MatrixTransform applied to TransformRoot.
/// </summary>
private MatrixTransform _matrixTransform;
private MatrixTransform _matrixTransform = new MatrixTransform();
/// <summary>
/// Transformation matrix corresponding to _matrixTransform.
/// </summary>
private Matrix _transformation;
private IDisposable _transformChangedEvent = null;
private Control _transformRoot;
/// <summary>
/// Returns true if Size a is smaller than Size b in either dimension.
/// </summary>
@ -215,7 +208,8 @@ namespace Avalonia.Controls
/// </remarks>
private void ApplyLayoutTransform()
{
if (LayoutTransform == null) return;
if (LayoutTransform == null)
return;
// Get the transform matrix and apply it
_transformation = RoundMatrix(LayoutTransform.Value, DecimalsAfterRound);
@ -376,11 +370,8 @@ namespace Avalonia.Controls
{
var newTransform = e.NewValue as Transform;
if (_transformChangedEvent != null)
{
_transformChangedEvent.Dispose();
_transformChangedEvent = null;
}
_transformChangedEvent?.Dispose();
_transformChangedEvent = null;
if (newTransform != null)
{
@ -392,4 +383,4 @@ namespace Avalonia.Controls
ApplyLayoutTransform();
}
}
}
}

5
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -30,10 +30,7 @@ namespace Avalonia.Platform
/// <summary>
/// Shows the window as a dialog.
/// </summary>
/// <returns>
/// An <see cref="IDisposable"/> that should be used to close the window.
/// </returns>
IDisposable ShowDialog();
void ShowDialog(IWindowImpl parent);
/// <summary>
/// Enables or disables system window decorations (title bar, buttons, etc)

21
src/Avalonia.Controls/Primitives/HeaderedContentControl.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Controls.Templates;
namespace Avalonia.Controls.Primitives
{
/// <summary>
@ -12,7 +14,13 @@ namespace Avalonia.Controls.Primitives
/// Defines the <see cref="Header"/> property.
/// </summary>
public static readonly StyledProperty<object> HeaderProperty =
AvaloniaProperty.Register<ContentControl, object>(nameof(Header));
AvaloniaProperty.Register<HeaderedContentControl, object>(nameof(Header));
/// <summary>
/// Defines the <see cref="HeaderTemplate"/> property.
/// </summary>
public static readonly StyledProperty<IDataTemplate> HeaderTemplateProperty =
AvaloniaProperty.Register<HeaderedContentControl, IDataTemplate>(nameof(HeaderTemplate));
/// <summary>
/// Gets or sets the header content.
@ -21,6 +29,15 @@ namespace Avalonia.Controls.Primitives
{
get { return GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
/// <summary>
/// Gets or sets the data template used to display the header content of the control.
/// </summary>
public IDataTemplate HeaderTemplate
{
get { return GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
}
}
}
}

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

@ -84,8 +84,10 @@ namespace Avalonia.Controls.Primitives
if (screen != null)
{
var screenX = Position.X + Bounds.Width - screen.Bounds.X;
var screenY = Position.Y + Bounds.Height - screen.Bounds.Y;
var scaling = VisualRoot.RenderScaling;
var screenX = Position.X + (Bounds.Width * scaling) - screen.Bounds.X;
var screenY = Position.Y + (Bounds.Height * scaling) - screen.Bounds.Y;
if (screenX > screen.Bounds.Width)
{

9
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<object>().Any(v => Equals(ms.Select(v), i.Item));
MarkContainerSelected(i.ContainerControl, selected);
}
}
}

19
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -260,7 +260,7 @@ namespace Avalonia.Controls.Primitives
var child = template.Build(this);
var nameScope = new NameScope();
NameScope.SetNameScope((Control)child, nameScope);
child.SetValue(TemplatedParentProperty, this);
ApplyTemplatedParent(child);
RegisterNames(child, nameScope);
((ISetLogicalParent)child).SetParent(this);
VisualChildren.Add(child);
@ -326,6 +326,23 @@ namespace Avalonia.Controls.Primitives
InvalidateMeasure();
}
/// <summary>
/// Sets the TemplatedParent property for the created template children.
/// </summary>
/// <param name="control">The control.</param>
private void ApplyTemplatedParent(IControl control)
{
control.SetValue(TemplatedParentProperty, this);
foreach (var child in control.LogicalChildren)
{
if (child is IControl c)
{
ApplyTemplatedParent(c);
}
}
}
/// <summary>
/// Registers each control with its name scope.
/// </summary>

31
src/Avalonia.Controls/SystemDialog.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -18,28 +19,40 @@ namespace Avalonia.Controls
public class SaveFileDialog : FileDialog
{
public string DefaultExtension { get; set; }
public string DefaultExtension { get; set; }
public async Task<string> ShowAsync(Window window)
=>
((await AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFileDialogAsync(this, window?.PlatformImpl)) ??
new string[0]).FirstOrDefault();
public async Task<string> ShowAsync(Window parent)
{
if(parent == null)
throw new ArgumentNullException(nameof(parent));
return ((await AvaloniaLocator.Current.GetService<ISystemDialogImpl>()
.ShowFileDialogAsync(this, parent?.PlatformImpl)) ??
new string[0]).FirstOrDefault();
}
}
public class OpenFileDialog : FileDialog
{
public bool AllowMultiple { get; set; }
public Task<string[]> ShowAsync(Window window = null)
=> AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFileDialogAsync(this, window?.PlatformImpl);
public Task<string[]> ShowAsync(Window parent)
{
if(parent == null)
throw new ArgumentNullException(nameof(parent));
return AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFileDialogAsync(this, parent?.PlatformImpl);
}
}
public class OpenFolderDialog : FileSystemDialog
{
public string DefaultDirectory { get; set; }
public Task<string> ShowAsync(Window window = null)
=> AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFolderDialogAsync(this, window?.PlatformImpl);
public Task<string> ShowAsync(Window parent)
{
if(parent == null)
throw new ArgumentNullException(nameof(parent));
return AvaloniaLocator.Current.GetService<ISystemDialogImpl>().ShowFolderDialogAsync(this, parent?.PlatformImpl);
}
}
public abstract class SystemDialog

164
src/Avalonia.Controls/TabControl.cs

@ -1,10 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Animation;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Layout;
namespace Avalonia.Controls
{
@ -14,28 +16,46 @@ namespace Avalonia.Controls
public class TabControl : SelectingItemsControl
{
/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// Defines the <see cref="TabStripPlacement"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition> PageTransitionProperty =
Avalonia.Controls.Carousel.PageTransitionProperty.AddOwner<TabControl>();
public static readonly StyledProperty<Dock> TabStripPlacementProperty =
AvaloniaProperty.Register<TabControl, Dock>(nameof(TabStripPlacement), defaultValue: Dock.Top);
/// <summary>
/// Defines an <see cref="IMemberSelector"/> that selects the content of a <see cref="TabItem"/>.
/// Defines the <see cref="HorizontalContentAlignment"/> property.
/// </summary>
public static readonly IMemberSelector ContentSelector =
new FuncMemberSelector<object, object>(SelectContent);
public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty =
ContentControl.HorizontalContentAlignmentProperty.AddOwner<TabControl>();
/// <summary>
/// Defines an <see cref="IMemberSelector"/> that selects the header of a <see cref="TabItem"/>.
/// Defines the <see cref="VerticalContentAlignment"/> property.
/// </summary>
public static readonly IMemberSelector HeaderSelector =
new FuncMemberSelector<object, object>(SelectHeader);
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<TabControl>();
/// <summary>
/// Defines the <see cref="TabStripPlacement"/> property.
/// Defines the <see cref="ContentTemplate"/> property.
/// </summary>
public static readonly StyledProperty<Dock> TabStripPlacementProperty =
AvaloniaProperty.Register<TabControl, Dock>(nameof(TabStripPlacement), defaultValue: Dock.Top);
public static readonly StyledProperty<IDataTemplate> ContentTemplateProperty =
ContentControl.ContentTemplateProperty.AddOwner<TabControl>();
/// <summary>
/// The selected content property
/// </summary>
public static readonly StyledProperty<object> SelectedContentProperty =
AvaloniaProperty.Register<TabControl, object>(nameof(SelectedContent));
/// <summary>
/// The selected content template property
/// </summary>
public static readonly StyledProperty<IDataTemplate> SelectedContentTemplateProperty =
AvaloniaProperty.Register<TabControl, IDataTemplate>(nameof(SelectedContentTemplate));
/// <summary>
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
/// </summary>
private static readonly FuncTemplate<IPanel> DefaultPanel =
new FuncTemplate<IPanel>(() => new WrapPanel());
/// <summary>
/// Initializes static members of the <see cref="TabControl"/> class.
@ -43,107 +63,107 @@ namespace Avalonia.Controls
static TabControl()
{
SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected);
FocusableProperty.OverrideDefaultValue<TabControl>(false);
ItemsPanelProperty.OverrideDefaultValue<TabControl>(DefaultPanel);
AffectsMeasure<TabControl>(TabStripPlacementProperty);
}
/// <summary>
/// Gets the pages portion of the <see cref="TabControl"/>'s template.
/// Gets or sets the horizontal alignment of the content within the control.
/// </summary>
public IControl Pages
public HorizontalAlignment HorizontalContentAlignment
{
get;
private set;
get { return GetValue(HorizontalContentAlignmentProperty); }
set { SetValue(HorizontalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets the tab strip portion of the <see cref="TabControl"/>'s template.
/// Gets or sets the vertical alignment of the content within the control.
/// </summary>
public IControl TabStrip
public VerticalAlignment VerticalContentAlignment
{
get;
private set;
get { return GetValue(VerticalContentAlignmentProperty); }
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets the transition to use when switching tabs.
/// Gets or sets the tabstrip placement of the TabControl.
/// </summary>
public IPageTransition PageTransition
public Dock TabStripPlacement
{
get { return GetValue(PageTransitionProperty); }
set { SetValue(PageTransitionProperty, value); }
get { return GetValue(TabStripPlacementProperty); }
set { SetValue(TabStripPlacementProperty, value); }
}
/// <summary>
/// Gets or sets the tabstrip placement of the tabcontrol.
/// Gets or sets the default data template used to display the content of the selected tab.
/// </summary>
public Dock TabStripPlacement
public IDataTemplate ContentTemplate
{
get { return GetValue(TabStripPlacementProperty); }
set { SetValue(TabStripPlacementProperty, value); }
get { return GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
}
/// <summary>
/// Gets or sets the content of the selected tab.
/// </summary>
/// <value>
/// The content of the selected tab.
/// </value>
public object SelectedContent
{
get { return GetValue(SelectedContentProperty); }
internal set { SetValue(SelectedContentProperty, value); }
}
/// <summary>
/// Gets or sets the content template for the selected tab.
/// </summary>
/// <value>
/// The content template of the selected tab.
/// </value>
public IDataTemplate SelectedContentTemplate
{
get { return GetValue(SelectedContentTemplateProperty); }
internal set { SetValue(SelectedContentTemplateProperty, value); }
}
internal ItemsPresenter ItemsPresenterPart { get; private set; }
internal ContentPresenter ContentPart { get; private set; }
protected override IItemContainerGenerator CreateItemContainerGenerator()
{
// TabControl doesn't actually create items - instead its TabStrip and Carousel
// children create the items. However we want it to be a SelectingItemsControl
// so that it has the Items/SelectedItem etc properties. In this case, we can
// return a null ItemContainerGenerator to disable the creation of item containers.
return null;
return new TabItemContainerGenerator(this);
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
TabStrip = e.NameScope.Find<IControl>("PART_TabStrip");
Pages = e.NameScope.Find<IControl>("PART_Content");
ItemsPresenterPart = e.NameScope.Get<ItemsPresenter>("PART_ItemsPresenter");
ContentPart = e.NameScope.Get<ContentPresenter>("PART_Content");
}
/// <summary>
/// Selects the content of a tab item.
/// </summary>
/// <param name="o">The tab item.</param>
/// <returns>The content.</returns>
private static object SelectContent(object o)
/// <inheritdoc/>
protected override void OnGotFocus(GotFocusEventArgs e)
{
var content = o as IContentControl;
base.OnGotFocus(e);
if (content != null)
if (e.NavigationMethod == NavigationMethod.Directional)
{
return content.Content;
e.Handled = UpdateSelectionFromEventSource(e.Source);
}
else
{
return o;
}
}
/// <summary>
/// Selects the header of a tab item.
/// </summary>
/// <param name="o">The tab item.</param>
/// <returns>The content.</returns>
private static object SelectHeader(object o)
/// <inheritdoc/>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
var headered = o as IHeadered;
var control = o as IControl;
base.OnPointerPressed(e);
if (headered != null)
{
return headered.Header ?? string.Empty;
}
else if (control != null)
{
// Non-headered control items should result in TabStripItems with empty content.
// If a TabStrip is created with non IHeadered controls as its items, don't try to
// display the control in the TabStripItem: the content portion will also try to
// display this control, resulting in dual-parentage breakage.
return string.Empty;
}
else
if (e.MouseButton == MouseButton.Left)
{
return o;
e.Handled = UpdateSelectionFromEventSource(e.Source);
}
}
}

67
src/Avalonia.Controls/TabItem.cs

@ -11,6 +11,12 @@ namespace Avalonia.Controls
/// </summary>
public class TabItem : HeaderedContentControl, ISelectable
{
/// <summary>
/// Defines the <see cref="TabStripPlacement"/> property.
/// </summary>
public static readonly StyledProperty<Dock> TabStripPlacementProperty =
TabControl.TabStripPlacementProperty.AddOwner<TabItem>();
/// <summary>
/// Defines the <see cref="IsSelected"/> property.
/// </summary>
@ -24,6 +30,19 @@ namespace Avalonia.Controls
{
SelectableMixin.Attach<TabItem>(IsSelectedProperty);
FocusableProperty.OverrideDefaultValue(typeof(TabItem), true);
IsSelectedProperty.Changed.AddClassHandler<TabItem>(x => x.UpdateSelectedContent);
DataContextProperty.Changed.AddClassHandler<TabItem>(x => x.UpdateHeader);
}
/// <summary>
/// Gets the tab strip placement.
/// </summary>
/// <value>
/// The tab strip placement.
/// </value>
public Dock TabStripPlacement
{
get { return GetValue(TabStripPlacementProperty); }
}
/// <summary>
@ -34,5 +53,53 @@ namespace Avalonia.Controls
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
internal TabControl ParentTabControl { get; set; }
private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj)
{
if (Header == null)
{
if (obj.NewValue is IHeadered headered)
{
if (Header != headered.Header)
{
Header = headered.Header;
}
}
else
{
if (!(obj.NewValue is IControl))
{
Header = obj.NewValue;
}
}
}
else
{
if (Header == obj.OldValue)
{
Header = obj.NewValue;
}
}
}
private void UpdateSelectedContent(AvaloniaPropertyChangedEventArgs e)
{
if (!IsSelected)
{
return;
}
if (ParentTabControl.SelectedContentTemplate != ContentTemplate)
{
ParentTabControl.SelectedContentTemplate = ContentTemplate;
}
if (ParentTabControl.SelectedContent != Content)
{
ParentTabControl.SelectedContent = Content;
}
}
}
}

6
src/Avalonia.Controls/Templates/TemplateExtensions.cs

@ -24,12 +24,14 @@ namespace Avalonia.Controls.Templates
{
foreach (IControl child in control.GetVisualChildren())
{
if (child.TemplatedParent == templatedParent)
var childTemplatedParent = child.TemplatedParent;
if (childTemplatedParent == templatedParent)
{
yield return child;
}
if (child.TemplatedParent != null)
if (childTemplatedParent != null)
{
foreach (var descendant in GetTemplateChildren(child, templatedParent))
{

1
src/Avalonia.Controls/TextBox.cs

@ -262,7 +262,6 @@ namespace Avalonia.Controls
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
_presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
if (IsFocused)
{

23
src/Avalonia.Controls/Utils/GridLayout.cs

@ -143,14 +143,17 @@ namespace Avalonia.Controls.Utils
/// <param name="containerLength">
/// The container length. Usually, it is the constraint of the <see cref="Layoutable.MeasureOverride"/> method.
/// </param>
/// <param name="conventions">
/// Overriding conventions that allows the algorithm to handle external inputa
/// </param>
/// <returns>
/// The measured result that containing the desired size and all the column/row lengths.
/// </returns>
[NotNull, Pure]
internal MeasureResult Measure(double containerLength)
internal MeasureResult Measure(double containerLength, IReadOnlyList<LengthConvention> conventions = null)
{
// Prepare all the variables that this method needs to use.
var conventions = _conventions.Select(x => x.Clone()).ToList();
conventions = conventions ?? _conventions.Select(x => x.Clone()).ToList();
var starCount = conventions.Where(x => x.Length.IsStar).Sum(x => x.Length.Value);
var aggregatedLength = 0.0;
double starUnitLength;
@ -248,7 +251,7 @@ namespace Avalonia.Controls.Utils
// | min | max | | | min | | min max | max |
// |#des#| fix |#des#| fix | fix | fix | fix | #des# |#des#|
var desiredStarMin = AggregateAdditionalConventionsForStars(conventions);
var (minLengths, desiredStarMin) = AggregateAdditionalConventionsForStars(conventions);
aggregatedLength += desiredStarMin;
// M6/7. Determine the desired length of the grid for current container length. Its value is stored in desiredLength.
@ -282,7 +285,7 @@ namespace Avalonia.Controls.Utils
// Returns the measuring result.
return new MeasureResult(containerLength, desiredLength, greedyDesiredLength,
conventions, dynamicConvention);
conventions, dynamicConvention, minLengths);
}
/// <summary>
@ -306,14 +309,14 @@ namespace Avalonia.Controls.Utils
if (finalLength - measure.ContainerLength > LayoutTolerance)
{
// If the final length is larger, we will rerun the whole measure.
measure = Measure(finalLength);
measure = Measure(finalLength, measure.LeanLengthList);
}
else if (finalLength - measure.ContainerLength < -LayoutTolerance)
{
// If the final length is smaller, we measure the M6/6 procedure only.
var dynamicConvention = ExpandStars(measure.LeanLengthList, finalLength);
measure = new MeasureResult(finalLength, measure.DesiredLength, measure.GreedyDesiredLength,
measure.LeanLengthList, dynamicConvention);
measure.LeanLengthList, dynamicConvention, measure.MinLengths);
}
return new ArrangeResult(measure.LengthList);
@ -370,7 +373,7 @@ namespace Avalonia.Controls.Utils
/// <param name="conventions">All the conventions that have almost been fixed except the rest *.</param>
/// <returns>The total desired length of all the * length.</returns>
[Pure, MethodImpl(MethodImplOptions.AggressiveInlining)]
private double AggregateAdditionalConventionsForStars(
private (List<double>, double) AggregateAdditionalConventionsForStars(
IReadOnlyList<LengthConvention> conventions)
{
// 1. Determine all one-span column's desired widths or row's desired heights.
@ -403,7 +406,7 @@ namespace Avalonia.Controls.Utils
lengthList[group.Key] = Math.Max(lengthList[group.Key], length > 0 ? length : 0);
}
return lengthList.Sum() - fixedLength;
return (lengthList, lengthList.Sum() - fixedLength);
}
/// <summary>
@ -638,13 +641,14 @@ namespace Avalonia.Controls.Utils
/// Initialize a new instance of <see cref="MeasureResult"/>.
/// </summary>
internal MeasureResult(double containerLength, double desiredLength, double greedyDesiredLength,
IReadOnlyList<LengthConvention> leanConventions, IReadOnlyList<double> expandedConventions)
IReadOnlyList<LengthConvention> leanConventions, IReadOnlyList<double> expandedConventions, IReadOnlyList<double> minLengths)
{
ContainerLength = containerLength;
DesiredLength = desiredLength;
GreedyDesiredLength = greedyDesiredLength;
LeanLengthList = leanConventions;
LengthList = expandedConventions;
MinLengths = minLengths;
}
/// <summary>
@ -674,6 +678,7 @@ namespace Avalonia.Controls.Utils
/// Gets the length list for each column/row.
/// </summary>
public IReadOnlyList<double> LengthList { get; }
public IReadOnlyList<double> MinLengths { get; }
}
/// <summary>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save