Browse Source

Merge branch 'master' into fixes/2119

pull/2120/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
a56dee5791
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 3
      .gitmodules
  3. 5
      .ncrunch/Avalonia.Native.v3.ncrunchproject
  4. 0
      .nuke
  5. 24
      .travis.yml
  6. 62
      Avalonia.sln
  7. 3
      Avalonia.sln.DotSettings
  8. 2
      Directory.Build.props
  9. 23
      appveyor.yml
  10. 25
      azure-pipelines.yml
  11. 312
      build.cake
  12. 226
      build.ps1
  13. 139
      build.sh
  14. 8
      build/BuildTargets.targets
  15. 2
      build/Magick.NET-Q16-AnyCPU.props
  16. 1
      build/ReferenceCoreLibraries.props
  17. 4
      build/SkiaSharp.props
  18. 15
      cake.config
  19. 6
      native/Avalonia.Native/src/OSX/gl.mm
  20. 8
      nukebuild/.editorconfig
  21. 245
      nukebuild/Build.cs
  22. 142
      nukebuild/BuildParameters.cs
  23. 1
      nukebuild/Numerge
  24. 93
      nukebuild/Shims.cs
  25. 37
      nukebuild/_build.csproj
  26. 24
      nukebuild/_build.csproj.DotSettings
  27. 23
      nukebuild/numerge.config
  28. 22
      packages/Avalonia/Avalonia.csproj
  29. 3
      packages/Avalonia/Avalonia.props
  30. 3
      packages/Avalonia/Avalonia.targets
  31. 3
      packages/Avalonia/AvaloniaBuildTasks.props
  32. 43
      packages/Avalonia/AvaloniaBuildTasks.targets
  33. 6
      readme.md
  34. 10
      samples/ControlCatalog.NetCore/Program.cs
  35. 10
      samples/ControlCatalog/App.xaml
  36. 8
      samples/ControlCatalog/ControlCatalog.csproj
  37. 17
      samples/ControlCatalog/DecoratedWindow.xaml
  38. 67
      samples/ControlCatalog/MainView.xaml
  39. 17
      samples/ControlCatalog/MainView.xaml.cs
  40. 8
      samples/ControlCatalog/MainWindow.xaml
  41. 4
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  42. 4
      samples/ControlCatalog/Pages/BorderPage.xaml
  43. 3
      samples/ControlCatalog/Pages/ButtonPage.xaml
  44. 3
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml
  45. 3
      samples/ControlCatalog/Pages/CalendarPage.xaml
  46. 4
      samples/ControlCatalog/Pages/CanvasPage.xaml
  47. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  48. 3
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  49. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  50. 3
      samples/ControlCatalog/Pages/DatePickerPage.xaml
  51. 4
      samples/ControlCatalog/Pages/DialogsPage.xaml
  52. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  53. 4
      samples/ControlCatalog/Pages/DropDownPage.xaml
  54. 4
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  55. 14
      samples/ControlCatalog/Pages/ImagePage.xaml
  56. 5
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml
  57. 4
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  58. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  59. 3
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  60. 6
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  61. 5
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  62. 6
      samples/ControlCatalog/Pages/SliderPage.xaml
  63. 44
      samples/ControlCatalog/Pages/TabControlPage.xaml
  64. 4
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  65. 15
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  66. 6
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  67. 6
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  68. 66
      samples/ControlCatalog/Pages/ViewboxPage.xaml
  69. 18
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  70. 6
      samples/ControlCatalog/SideBar.xaml
  71. 5
      samples/RenderDemo/MainWindow.xaml
  72. 35
      samples/RenderDemo/Pages/AnimationsPage.xaml
  73. 2
      samples/RenderDemo/Pages/ClippingPage.xaml
  74. 113
      samples/RenderDemo/SideBar.xaml
  75. 1
      src/Avalonia.Animation/Animatable.cs
  76. 199
      src/Avalonia.Animation/Animation.cs
  77. 204
      src/Avalonia.Animation/AnimationInstance`1.cs
  78. 73
      src/Avalonia.Animation/Animators/Animator`1.cs
  79. 21
      src/Avalonia.Animation/Animators/BoolAnimator.cs
  80. 24
      src/Avalonia.Animation/Animators/ByteAnimator.cs
  81. 17
      src/Avalonia.Animation/Animators/DecimalAnimator.cs
  82. 17
      src/Avalonia.Animation/Animators/DoubleAnimator.cs
  83. 17
      src/Avalonia.Animation/Animators/FloatAnimator.cs
  84. 24
      src/Avalonia.Animation/Animators/Int16Animator.cs
  85. 24
      src/Avalonia.Animation/Animators/Int32Animator.cs
  86. 24
      src/Avalonia.Animation/Animators/Int64Animator.cs
  87. 24
      src/Avalonia.Animation/Animators/UInt16Animator.cs
  88. 24
      src/Avalonia.Animation/Animators/UInt32Animator.cs
  89. 24
      src/Avalonia.Animation/Animators/UInt64Animator.cs
  90. 1
      src/Avalonia.Animation/Avalonia.Animation.csproj
  91. 3
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  92. 35
      src/Avalonia.Animation/DoubleAnimator.cs
  93. 0
      src/Avalonia.Animation/Easing/IEasing.cs
  94. 176
      src/Avalonia.Animation/IterationCount.cs
  95. 4
      src/Avalonia.Animation/IterationCountTypeConverter.cs
  96. 15
      src/Avalonia.Animation/KeyFrame.cs
  97. 33
      src/Avalonia.Animation/KeyFramePair`1.cs
  98. 33
      src/Avalonia.Animation/KeyFrames.cs
  99. 3
      src/Avalonia.Animation/Properties/AssemblyInfo.cs
  100. 199
      src/Avalonia.Animation/RepeatCount.cs

1
.gitignore

@ -195,3 +195,4 @@ Logs/
ModuleCache.noindex/ ModuleCache.noindex/
Build/Intermediates.noindex/ Build/Intermediates.noindex/
info.plist info.plist
build-intermediate

3
.gitmodules

@ -1,3 +1,6 @@
[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"] [submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"]
path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
url = https://github.com/AvaloniaUI/Portable.Xaml.git url = https://github.com/AvaloniaUI/Portable.Xaml.git
[submodule "nukebuild/Numerge"]
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git

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

62
Avalonia.sln

@ -1,4 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27130.2027 VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
@ -147,6 +147,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Splat.props = build\Splat.props build\Splat.props = build\Splat.props
build\System.Memory.props = build\System.Memory.props build\System.Memory.props = build\System.Memory.props
build\XUnit.props = build\XUnit.props build\XUnit.props = build\XUnit.props
build\BuildTargets.targets = build\BuildTargets.targets
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
@ -188,6 +189,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalon
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Desktop", "src\Avalonia.Desktop\Avalonia.Desktop.csproj", "{3C471044-3640-45E3-B1B2-16D2FF8399EE}"
EndProject 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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -213,6 +220,10 @@ Global
Release|iPhoneSimulator = Release|iPhoneSimulator Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution 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.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|Any CPU.Build.0 = Release|Any CPU
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -1687,6 +1698,54 @@ Global
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.Build.0 = Release|Any CPU {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.ActiveCfg = Release|Any CPU
{3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.Build.0 = 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
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1740,6 +1799,7 @@ Global
{E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

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/=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/=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/=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>

2
Directory.Build.props

@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)artifacts/nuget</PackageOutputPath> <PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

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 sudo apt-get install castxml
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Install Cake' displayName: 'Install Nuke'
inputs: inputs:
script: | script: |
dotnet tool install -g Cake.Tool --version 0.30.0 dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Run Cake' displayName: 'Run Nuke'
inputs: inputs:
script: | script: |
export PATH="$PATH:$HOME/.dotnet/tools" export PATH="$PATH:$HOME/.dotnet/tools"
dotnet --info dotnet --info
printenv printenv
dotnet cake build.cake -target="Azure-Linux" -configuration="Release" nuke --target CiAzureLinux --configuration=Release
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: inputs:
@ -55,13 +54,13 @@ jobs:
script: brew install castxml script: brew install castxml
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Install Cake' displayName: 'Install Nuke'
inputs: inputs:
script: | script: |
dotnet tool install -g Cake.Tool --version 0.30.0 dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Run Cake' displayName: 'Run Nuke'
inputs: inputs:
script: | script: |
export COREHOST_TRACE=0 export COREHOST_TRACE=0
@ -72,7 +71,7 @@ jobs:
export PATH="$PATH:$HOME/.dotnet/tools" export PATH="$PATH:$HOME/.dotnet/tools"
dotnet --info dotnet --info
printenv printenv
dotnet cake build.cake -target="Azure-OSX" -configuration="Release" nuke --target CiAzureOSX --configuration Release
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: inputs:
@ -97,17 +96,17 @@ jobs:
vmImage: 'vs2017-win2016' vmImage: 'vs2017-win2016'
steps: steps:
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Install Cake' displayName: 'Install Nuke'
inputs: inputs:
script: | script: |
dotnet tool install -g Cake.Tool --version 0.30.0 dotnet tool install --global Nuke.GlobalTool --version 0.12.3
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Run Cake' displayName: 'Run Nuke'
inputs: inputs:
script: | script: |
set PATH=%PATH%;%USERPROFILE%\.dotnet\tools set PATH=%PATH%;%USERPROFILE%\.dotnet\tools
dotnet cake build.cake -target="Azure-Windows" -configuration="Release" nuke --target CiAzureWindows --configuration Release
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: 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()] [CmdletBinding()]
Param( Param(
[string]$Script = "build.cake", #[switch]$CustomParam,
[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,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs [string[]]$BuildArguments
) )
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null Write-Output "Windows PowerShell $($Host.Version)"
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
}
$TOOLS_DIR = Join-Path $PSScriptRoot "tools" Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 }
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
$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"
# Should we use mono? ###########################################################################
$UseMono = ""; # CONFIGURATION
if($Mono.IsPresent) { ###########################################################################
Write-Verbose -Message "Using the Mono based scripting engine."
$UseMono = "-mono"
}
# Should we use the new Roslyn? $BuildProjectFile = "$PSScriptRoot\nukebuild\_build.csproj"
$UseExperimental = ""; $TempDirectory = "$PSScriptRoot\\.tmp"
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
Write-Verbose -Message "Using experimental version of Roslyn."
$UseExperimental = "-experimental"
}
# Is this a dry run? $DotNetGlobalFile = "$PSScriptRoot\\global.json"
$UseDryRun = ""; $DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1"
if($WhatIf.IsPresent) { $DotNetChannel = "Current"
$UseDryRun = "-dryrun"
}
# Is this a dry run? $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
$UseSkipTests = ""; $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
if($SkipTests.IsPresent) { $env:NUGET_XMLDOC_MODE = "skip"
$UseSkipTests = "-skip-tests"
}
# Make sure tools folder exists ###########################################################################
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { # EXECUTION
Write-Verbose -Message "Creating tools directory..." ###########################################################################
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Make sure that packages.config exist. function ExecSafe([scriptblock] $cmd) {
if (!(Test-Path $PACKAGES_CONFIG)) { & $cmd
Write-Verbose -Message "Downloading packages.config..." if ($LASTEXITCODE) { exit $LASTEXITCODE }
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
Throw "Could not download packages.config."
}
} }
# Try find NuGet.exe in path if not exists # If global.json exists, load expected version
if (!(Test-Path $NUGET_EXE)) { if (Test-Path $DotNetGlobalFile) {
Write-Verbose -Message "Trying to find nuget.exe in PATH..." $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 $DotNetVersion = $DotNetGlobal.sdk.version
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
} }
} }
# Try download NuGet.exe if not exists # If dotnet is installed locally, and expected version is not set or installation matches the expected version
if (!(Test-Path $NUGET_EXE)) { if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and `
Write-Verbose -Message "Downloading NuGet.exe..." (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) {
try { $env:DOTNET_EXE = (Get-Command "dotnet").Path
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
} }
else {
# Save nuget.exe path to environment to be available to child processed $DotNetDirectory = "$TempDirectory\dotnet-win"
$ENV:NUGET_EXE = $NUGET_EXE $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
# Restore tools from NuGet? # Download install script
if(-Not $SkipToolPackageRestore.IsPresent) { $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
Push-Location md -force $TempDirectory > $null
Set-Location $TOOLS_DIR (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# Check for changes in packages.config and remove installed tools if true. # Install by channel or version
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) if (!(Test-Path variable:DotNetVersion)) {
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { } else {
Write-Verbose -Message "Missing or changed package.config hash..." ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
Remove-Item * -Recurse -Exclude packages.config,nuget.exe
} }
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. Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Start Cake ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -- $BuildArguments }
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

139
build.sh

@ -1,105 +1,72 @@
#!/usr/bin/env bash #!/usr/bin/env bash
########################################################################## echo $(bash --version 2>&1 | head -n 1)
# 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.
##########################################################################
# Define directories. #CUSTOMPARAM=0
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) BUILD_ARGUMENTS=()
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.
for i in "$@"; do for i in "$@"; do
case $1 in case $(echo $1 | awk '{print tolower($0)}') in
-s|--script) SCRIPT="$2"; shift ;; # -custom-param) CUSTOMPARAM=1;;
-t|--target) TARGET="$2"; shift ;; *) BUILD_ARGUMENTS+=("$1") ;;
-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") ;;
esac esac
shift shift
done done
# Make sure the tools folder exist. set -eo pipefail
if [ ! -d "$TOOLS_DIR" ]; then SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
mkdir "$TOOLS_DIR"
fi
# Make sure that packages.config exist. ###########################################################################
if [ ! -f "$TOOLS_DIR/packages.config" ]; then # CONFIGURATION
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
# Download NuGet if it does not exist. BUILD_PROJECT_FILE="$SCRIPT_DIR/nukebuild/_build.csproj"
if [ ! -f "$NUGET_EXE" ]; then TEMP_DIRECTORY="$SCRIPT_DIR//.tmp"
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
# Restore tools from NuGet. DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
pushd "$TOOLS_DIR" >/dev/null DOTNET_INSTALL_URL="https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.sh"
if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then DOTNET_CHANNEL="Current"
find . -type d ! -name . | xargs rm -rf
fi
mono "$NUGET_EXE" install -ExcludeVersion export DOTNET_CLI_TELEMETRY_OPTOUT=1
if [ $? -ne 0 ]; then export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
echo "Could not restore NuGet packages." export NUGET_XMLDOC_MODE="skip"
exit 1
fi
$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 global.json exists, load expected version
if [ ! -f "$CAKE_EXE" ]; then if [ -f "$DOTNET_GLOBAL_FILE" ]; then
echo "Could not find Cake.exe at '$CAKE_EXE'." DOTNET_VERSION=$(FirstJsonValue "version" $(cat "$DOTNET_GLOBAL_FILE"))
exit 1 if [ "$DOTNET_VERSION" == "" ]; then
unset DOTNET_VERSION
fi
fi fi
# Start Cake # If dotnet is installed locally, and expected version is not set or installation matches the expected version
if $SHOW_VERSION; then if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") ]]; then
exec mono "$CAKE_EXE" -version export DOTNET_EXE="$(command -v dotnet)"
else 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 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>

2
build/Magick.NET-Q16-AnyCPU.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.0.6.102" /> <PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
build/ReferenceCoreLibraries.props

@ -1,5 +1,4 @@
<Project> <Project>
<Import Condition="'$(TargetFramework)' == 'netcoreapp2.0'" Project="CoreLibraries.props" />
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)../packages/Avalonia/Avalonia.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)../packages/Avalonia/Avalonia.csproj" />
</ItemGroup> </ItemGroup>

4
build/SkiaSharp.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="1.60.0" /> <PackageReference Include="SkiaSharp" Version="1.68.0" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="Avalonia.Skia.Linux.Natives" Version="1.60.0.1" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
</ItemGroup> </ItemGroup>
</Project> </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

6
native/Avalonia.Native/src/OSX/gl.mm

@ -208,9 +208,8 @@ public:
virtual ~AvnGlRenderingSession() virtual ~AvnGlRenderingSession()
{ {
glFlush();
[_context flushBuffer]; [_context flushBuffer];
[_context setView:nil]; [NSOpenGLContext clearCurrentContext];
CGLUnlockContext([_context CGLContextObj]); CGLUnlockContext([_context CGLContextObj]);
[_view unlockFocus]; [_view unlockFocus];
} }
@ -241,9 +240,8 @@ public:
auto gl = _context; auto gl = _context;
CGLLockContext([_context CGLContextObj]); CGLLockContext([_context CGLContextObj]);
[gl setView: _view]; [gl setView: _view];
[gl update];
[gl makeCurrentContext]; [gl makeCurrentContext];
auto frame = [_view frame];
*ret = new AvnGlRenderingSession(_window, _view, gl); *ret = new AvnGlRenderingSession(_window, _view, gl);
return S_OK; return S_OK;
} }

8
nukebuild/.editorconfig

@ -0,0 +1,8 @@
# editorconfig.org
# top-most EditorConfig file
root = false
# C# files
[*.cs]
dotnet_style_require_accessibility_modifiers = never

245
nukebuild/Build.cs

@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Nuke.Common;
using Nuke.Common.Git;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.MSBuild;
using Nuke.Common.Utilities;
using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.Xunit.XunitTasks;
/*
Before editing this file, install support plugin for your IDE,
running and debugging a particular target (optionally without deps) would be way easier
ReSharper/Rider - https://plugins.jetbrains.com/plugin/10803-nuke-support
VSCode - https://marketplace.visualstudio.com/items?itemName=nuke.support
*/
partial class Build : NukeBuild
{
BuildParameters Parameters { get; set; }
protected override void OnBuildInitialized()
{
Parameters = new BuildParameters(this);
Information("Building version {0} of Avalonia ({1}) using version {2} of Nuke.",
Parameters.Version,
Parameters.Configuration,
typeof(NukeBuild).Assembly.GetName().Version.ToString());
if (Parameters.IsLocalBuild)
{
Information("Repository Name: " + Parameters.RepositoryName);
Information("Repository Branch: " + Parameters.RepositoryBranch);
}
Information("Configuration: " + Parameters.Configuration);
Information("IsLocalBuild: " + Parameters.IsLocalBuild);
Information("IsRunningOnUnix: " + Parameters.IsRunningOnUnix);
Information("IsRunningOnWindows: " + Parameters.IsRunningOnWindows);
Information("IsRunningOnAzure:" + Parameters.IsRunningOnAzure);
Information("IsPullRequest: " + Parameters.IsPullRequest);
Information("IsMainRepo: " + Parameters.IsMainRepo);
Information("IsMasterBranch: " + Parameters.IsMasterBranch);
Information("IsReleaseBranch: " + Parameters.IsReleaseBranch);
Information("IsReleasable: " + Parameters.IsReleasable);
Information("IsMyGetRelease: " + Parameters.IsMyGetRelease);
Information("IsNuGetRelease: " + Parameters.IsNuGetRelease);
}
Target Clean => _ => _.Executes(() =>
{
DeleteDirectories(Parameters.BuildDirs);
EnsureCleanDirectories(Parameters.BuildDirs);
EnsureCleanDirectory(Parameters.ArtifactsDir);
EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
EnsureCleanDirectory(Parameters.NugetRoot);
EnsureCleanDirectory(Parameters.ZipRoot);
EnsureCleanDirectory(Parameters.TestResultsRoot);
});
Target Compile => _ => _
.DependsOn(Clean)
.Executes(() =>
{
if (Parameters.IsRunningOnWindows)
MSBuild(Parameters.MSBuildSolution, c => c
.SetArgumentConfigurator(a => a.Add("/r"))
.SetConfiguration(Parameters.Configuration)
.SetVerbosity(MSBuildVerbosity.Minimal)
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", "true")
.SetToolsVersion(MSBuildToolsVersion._15_0)
.AddTargets("Build")
);
else
DotNetBuild(Parameters.MSBuildSolution, c => c
.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
);
});
void RunCoreTest(string project, bool coreOnly = false)
{
if(!project.EndsWith(".csproj"))
project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project);
var frameworks = new List<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.Animation.UnitTests", false);
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)
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", true);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", true);
});
Target RunDesignerTests => _ => _
.OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", false);
});
[PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit;
Target RunLeakTests => _ => _
.OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
.DependsOn(Compile)
.Executes(() =>
{
var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll";
DotMemoryUnit(
$"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}",
timeout: 120_000);
});
Target ZipFiles => _ => _
.After(CreateNugetPackages, Compile, RunCoreLibsTests, Package)
.Executes(() =>
{
var data = Parameters;
Zip(data.ZipCoreArtifacts, data.BinRoot);
Zip(data.ZipNuGetArtifacts, data.NugetRoot);
Zip(data.ZipTargetControlCatalogDesktopDir,
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dll").Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.config")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.so")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dylib")).Concat(
GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe")));
});
Target CreateIntermediateNugetPackages => _ => _
.DependsOn(Compile)
.After(RunTests)
.Executes(() =>
{
if (Parameters.IsRunningOnWindows)
MSBuild(Parameters.MSBuildSolution, c => c
.SetConfiguration(Parameters.Configuration)
.SetVerbosity(MSBuildVerbosity.Minimal)
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", "true")
.SetToolsVersion(MSBuildToolsVersion._15_0)
.AddTargets("Pack"));
else
DotNetPack(Parameters.MSBuildSolution, c =>
c.SetConfiguration(Parameters.Configuration)
.AddProperty("PackageVersion", Parameters.Version));
});
Target CreateNugetPackages => _ => _
.DependsOn(CreateIntermediateNugetPackages)
.Executes(() =>
{
var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
EnsureCleanDirectory(Parameters.NugetRoot);
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger()))
throw new Exception("Package merge failed");
});
Target RunTests => _ => _
.DependsOn(RunCoreLibsTests)
.DependsOn(RunRenderTests)
.DependsOn(RunDesignerTests)
.DependsOn(RunLeakTests);
Target Package => _ => _
.DependsOn(RunTests)
.DependsOn(CreateNugetPackages);
Target CiAzureLinux => _ => _
.DependsOn(RunTests);
Target CiAzureOSX => _ => _
.DependsOn(Package)
.DependsOn(ZipFiles);
Target CiAzureWindows => _ => _
.DependsOn(Package)
.DependsOn(ZipFiles);
public static int Main() =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? Execute<Build>(x => x.Package)
: Execute<Build>(x => x.RunTests);
}

142
nukebuild/BuildParameters.cs

@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.BuildServers;
using Nuke.Common.Execution;
using Nuke.Common.IO;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
public partial class Build
{
[Parameter("configuration")]
public string NukeArgConfiguration { get; set; }
[Parameter("skip-tests")]
public bool NukeArgSkipTests { get; set; }
[Parameter("force-nuget-version")]
public string NukeArgForceNugetVersion { get; set; }
public class BuildParameters
{
public string Configuration { get; }
public bool SkipTests { get; }
public string MainRepo { get; }
public string MasterBranch { get; }
public string RepositoryName { get; }
public string RepositoryBranch { get; }
public string ReleaseConfiguration { get; }
public string ReleaseBranchPrefix { get; }
public string MSBuildSolution { get; }
public bool IsLocalBuild { get; }
public bool IsRunningOnUnix { get; }
public bool IsRunningOnWindows { get; }
public bool IsRunningOnAzure { get; }
public bool IsPullRequest { get; }
public bool IsMainRepo { get; }
public bool IsMasterBranch { get; }
public bool IsReleaseBranch { get; }
public bool IsReleasable { get; }
public bool IsMyGetRelease { get; }
public bool IsNuGetRelease { get; }
public bool PublishTestResults { get; }
public string Version { get; }
public AbsolutePath ArtifactsDir { get; }
public AbsolutePath NugetIntermediateRoot { get; }
public AbsolutePath NugetRoot { get; }
public AbsolutePath ZipRoot { get; }
public AbsolutePath BinRoot { get; }
public AbsolutePath TestResultsRoot { get; }
public string DirSuffix { get; }
public List<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";
NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget";
ZipRoot = ArtifactsDir / "zip";
BinRoot = ArtifactsDir / "bin";
TestResultsRoot = ArtifactsDir / "test-results";
BuildDirs = GlobDirectories(RootDirectory, "**bin").Concat(GlobDirectories(RootDirectory, "**obj")).ToList();
DirSuffix = Configuration;
FileZipSuffix = Version + ".zip";
ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix);
ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix);
ZipSourceControlCatalogDesktopDir =
RootDirectory / ("samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461");
ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix);
}
string GetVersion()
{
var xdoc = XDocument.Load(RootDirectory / "build/SharedVersion.props");
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
}
}
}

1
nukebuild/Numerge

@ -0,0 +1 @@
Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8

93
nukebuild/Shims.cs

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Nuke.Common;
using Nuke.Common.IO;
using Numerge;
public partial class Build
{
static void Information(string info)
{
Logger.Info(info);
}
static void Information(string info, params object[] args)
{
Logger.Info(info, args);
}
private void Zip(PathConstruction.AbsolutePath target, params string[] paths) => Zip(target, paths.AsEnumerable());
private void Zip(PathConstruction.AbsolutePath target, IEnumerable<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
}
}
}
class NumergeNukeLogger : INumergeLogger
{
public void Log(NumergeLogLevel level, string message)
{
if(level == NumergeLogLevel.Error)
Logger.Error(message);
else if (level == NumergeLogLevel.Warning)
Logger.Warn(message);
else
Logger.Info(message);
}
}
}

37
nukebuild/_build.csproj

@ -0,0 +1,37 @@
<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')" />
<Compile Remove="Numerge/**/*.*" />
<Compile Include="Numerge/Numerge/**/*.cs" />
</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>

23
nukebuild/numerge.config

@ -0,0 +1,23 @@
{
"Packages":
[
{
"Id": "Avalonia",
"MergeAll": true,
"Exclude": ["Avalonia.Remote.Protocol"],
"IncomingIncludeAssetsOverride": "",
"Merge": [
{
"Id": "Avalonia.Build.Tasks",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
},
{
"Id": "Avalonia.DesktopRuntime",
"IgnoreMissingFrameworkBinaries": true,
"IgnoreMissingFrameworkDependencies": true
}
]
}
]
}

22
packages/Avalonia/Avalonia.csproj

@ -1,10 +1,12 @@
<Project Sdk="AggregatePackage.NuGet.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>netstandard2.0;net461;netcoreapp2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" EmbedReference="false" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj"/>
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
@ -27,14 +29,18 @@
<Visible>false</Visible> <Visible>false</Visible>
<BuildAction>None</BuildAction> <BuildAction>None</BuildAction>
</_PackageFiles> </_PackageFiles>
<_PackageFiles Include="Avalonia.props">
<PackagePath>build/Avalonia.props</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
</ItemGroup> </ItemGroup>
</Target> </Target>
<ItemGroup>
<Content Include="*.props">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="*.targets">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
<Import Project="..\..\build\SharedVersion.props" /> <Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" /> <Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\CoreLibraries.props" /> <Import Project="..\..\build\CoreLibraries.props" />

3
packages/Avalonia/Avalonia.props

@ -2,5 +2,8 @@
<PropertyGroup> <PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath> <AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
</Project> </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 # Avalonia
| Gitter Chat | Build Status (Win, Linux, OSX) | Appveyor Build Status | Open Collective | | 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) | [![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](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 ## About

10
samples/ControlCatalog.NetCore/Program.cs

@ -23,6 +23,7 @@ namespace ControlCatalog.NetCore
break; break;
} }
} }
if (args.Contains("--fbdev")) if (args.Contains("--fbdev"))
AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl => AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
{ {
@ -30,7 +31,12 @@ namespace ControlCatalog.NetCore
System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer()); System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
}); });
else else
BuildAvaloniaApp().Start<MainWindow>(); BuildAvaloniaApp().Start(AppMain, args);
}
static void AppMain(Application app, string[] args)
{
app.Run(new MainWindow());
} }
/// <summary> /// <summary>
@ -46,4 +52,4 @@ namespace ControlCatalog.NetCore
Console.ReadKey(true); 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> <Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<Style Selector="TextBlock.h1"> <Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/> <Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/> <Setter Property="FontWeight" Value="Medium"/>
@ -12,6 +14,6 @@
<Style Selector="TextBlock.h3"> <Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/> <Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}"/>
</Style> </Style>
<StyleInclude Source="resm:ControlCatalog.SideBar.xaml"/> <StyleInclude Source="/SideBar.xaml"/>
</Application.Styles> </Application.Styles>
</Application> </Application>

8
samples/ControlCatalog/ControlCatalog.csproj

@ -6,10 +6,11 @@
<Compile Update="**\*.xaml.cs"> <Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon> <DependentUpon>%(Filename)</DependentUpon>
</Compile> </Compile>
<EmbeddedResource Include="**\*.xaml"> <AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </AvaloniaResource>
<EmbeddedResource Include="Assets\*" /> <AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" /> <EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
@ -24,4 +25,5 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\Serilog.props" /> <Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project> </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" Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico" xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window">
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False">
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5"> <Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" > <DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto"> <Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">
@ -18,7 +19,13 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
<Border Background="White" Margin="5"> <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> </Border>
</DockPanel> </DockPanel>
<Border Name="TopLeft" Background="Red"/> <Border Name="TopLeft" Background="Red"/>
@ -30,4 +37,4 @@
<Border Name="Bottom" Background="Blue" Grid.Row="2" Grid.Column="1" /> <Border Name="Bottom" Background="Blue" Grid.Row="2" Grid.Column="1" />
<Border Name="Left" Background="Blue" Grid.Row="1" /> <Border Name="Left" Background="Blue" Grid.Row="1" />
</Grid> </Grid>
</Window> </Window>

67
samples/ControlCatalog/MainView.xaml

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

17
samples/ControlCatalog/MainView.xaml.cs

@ -2,6 +2,7 @@ using System.Collections;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Platform; using Avalonia.Platform;
using ControlCatalog.Pages; 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() private void InitializeComponent()

8
samples/ControlCatalog/MainWindow.xaml

@ -1,6 +1,8 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300" <Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Title="Avalonia Control Gallery" Title="Avalonia Control Gallery"
Icon="resm:ControlCatalog.Assets.test_icon.ico?assembly=ControlCatalog" Icon="/Assets/test_icon.ico"
xmlns:local="clr-namespace:ControlCatalog"> xmlns:local="clr-namespace:ControlCatalog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainWindow">
<local:MainView/> <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">AutoCompleteBox</TextBlock> <TextBlock Classes="h1">AutoCompleteBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Border</TextBlock> <TextBlock Classes="h1">Border</TextBlock>
<TextBlock Classes="h2">A control which decorates a child with a border and background</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" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Button</TextBlock> <TextBlock Classes="h1">Button</TextBlock>
<TextBlock Classes="h2">A button control</TextBlock> <TextBlock Classes="h2">A button control</TextBlock>

3
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ButtonSpinner</TextBlock> <TextBlock Classes="h1">ButtonSpinner</TextBlock>

3
samples/ControlCatalog/Pages/CalendarPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Calendar</TextBlock> <TextBlock Classes="h1">Calendar</TextBlock>
<TextBlock Classes="h2">A calendar control for selecting dates</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Canvas</TextBlock> <TextBlock Classes="h1">Canvas</TextBlock>
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Carousel</TextBlock> <TextBlock Classes="h1">Carousel</TextBlock>
<TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock> <TextBlock Classes="h2">An items control that displays its items as pages that fill the control.</TextBlock>
@ -11,9 +13,9 @@
<Carousel.PageTransition> <Carousel.PageTransition>
<PageSlide Duration="0.25" Orientation="Vertical" /> <PageSlide Duration="0.25" Orientation="Vertical" />
</Carousel.PageTransition> </Carousel.PageTransition>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/> <Image Source="/Assets/delicate-arch-896885_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/> <Image Source="/Assets/hirsch-899118_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg"/> <Image Source="/Assets/maple-leaf-888807_640.jpg"/>
</Carousel> </Carousel>
<Button Name="right" VerticalAlignment="Center" Padding="20"> <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"/> <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" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">CheckBox</TextBlock> <TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Context Menu</TextBlock> <TextBlock Classes="h1">Context Menu</TextBlock>
<TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock> <TextBlock Classes="h2">A right click menu that can be applied to any control.</TextBlock>
@ -19,7 +21,7 @@
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Icon"> <MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/> <Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Checkbox"> <MenuItem Header="Menu Item with _Checkbox">

3
samples/ControlCatalog/Pages/DatePickerPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock> <TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock> <TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>

4
samples/ControlCatalog/Pages/DialogsPage.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.DialogsPage">
<StackPanel Orientation="Vertical" Spacing="4" Margin="4"> <StackPanel Orientation="Vertical" Spacing="4" Margin="4">
<Button Name="OpenFile">Open File</Button> <Button Name="OpenFile">Open File</Button>
<Button Name="SaveFile">Save File</Button> <Button Name="SaveFile">Save File</Button>

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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Drag+Drop</TextBlock> <TextBlock Classes="h1">Drag+Drop</TextBlock>
<TextBlock Classes="h2">Example of Drag+Drop capabilities</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DropDown</TextBlock> <TextBlock Classes="h1">DropDown</TextBlock>
<TextBlock Classes="h2">A drop-down list.</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Expander</TextBlock> <TextBlock Classes="h1">Expander</TextBlock>
<TextBlock Classes="h2">Expands to show nested content</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Image</TextBlock> <TextBlock Classes="h1">Image</TextBlock>
<TextBlock Classes="h2">Displays an image</TextBlock> <TextBlock Classes="h2">Displays an image</TextBlock>
@ -9,28 +11,28 @@
Spacing="16"> Spacing="16">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock>No Stretch</TextBlock> <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" Width="100" Height="200"
Stretch="None"/> Stretch="None"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock>Fill</TextBlock> <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" Width="100" Height="200"
Stretch="Fill"/> Stretch="Fill"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock>Uniform</TextBlock> <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" Width="100" Height="200"
Stretch="Uniform"/> Stretch="Uniform"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBlock>UniformToFill</TextBlock> <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" Width="100" Height="200"
Stretch="UniformToFill"/> Stretch="UniformToFill"/>
</StackPanel> </StackPanel>
@ -40,4 +42,4 @@
<Image Name="Icon" Width="100" Height="200" Stretch="None" /> <Image Name="Icon" Width="100" Height="200" Stretch="None" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

5
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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> <DockPanel>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto" Margin="16" DockPanel.Dock="Top"> <Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto" Margin="16" DockPanel.Dock="Top">
<TextBlock Grid.Column="0" Grid.Row="0">Rotation</TextBlock> <TextBlock Grid.Column="0" Grid.Row="0">Rotation</TextBlock>
@ -23,4 +24,4 @@
</LayoutTransformControl> </LayoutTransformControl>
</Grid> </Grid>
</DockPanel> </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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ListBox</TextBlock> <TextBlock Classes="h1">ListBox</TextBlock>
<TextBlock Classes="h2">Hosts a collection of ListBoxItem.</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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Menu</TextBlock> <TextBlock Classes="h1">Menu</TextBlock>
<TextBlock Classes="h2">A window menu</TextBlock> <TextBlock Classes="h2">A window menu</TextBlock>
@ -19,7 +21,7 @@
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Icon"> <MenuItem Header="Menu Item with _Icon">
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="resm:ControlCatalog.Assets.github_icon.png"/> <Image Source="/Assets/github_icon.png"/>
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Header="Menu Item with _Checkbox"> <MenuItem Header="Menu Item with _Checkbox">

3
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Margin="2" Classes="h1">Numeric up-down control</TextBlock> <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" 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>

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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock> <TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock> <TextBlock Classes="h2">A progress bar control</TextBlock>
@ -21,4 +23,4 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

5
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,5 +1,6 @@
<UserControl xmlns="https://github.com/avaloniaui" <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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">RadioButton</TextBlock> <TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock> <TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
@ -37,4 +38,4 @@
</StackPanel> </StackPanel>
</StackPanel> </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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Slider</TextBlock> <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> <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>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

44
samples/ControlCatalog/Pages/TabControlPage.xaml

@ -1,4 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"> <UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.TabControlPage"
xmlns="https://github.com/avaloniaui">
<DockPanel> <DockPanel>
<TextBlock <TextBlock
DockPanel.Dock="Top" DockPanel.Dock="Top"
@ -26,43 +29,19 @@
<TabControl <TabControl
Margin="0 16" Margin="0 16"
TabStripPlacement="{Binding TabPlacement}"> TabStripPlacement="{Binding TabPlacement}">
<TabItem> <TabItem Header="Arch">
<TabItem.Header>
<TextBlock
Text="Arch"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="8">
</TextBlock>
</TabItem.Header>
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBlock>This is the first page in the TabControl.</TextBlock> <TextBlock>This is the first page in the TabControl.</TextBlock>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg" Width="300"/> <Image Source="/Assets/delicate-arch-896885_640.jpg" Width="300"/>
</StackPanel> </StackPanel>
</TabItem> </TabItem>
<TabItem> <TabItem Header="Leaf">
<TabItem.Header>
<TextBlock
Text="Leaf"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="8">
</TextBlock>
</TabItem.Header>
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBlock>This is the second page in the TabControl.</TextBlock> <TextBlock>This is the second page in the TabControl.</TextBlock>
<Image Source="resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg" Width="300"/> <Image Source="/Assets/maple-leaf-888807_640.jpg" Width="300"/>
</StackPanel> </StackPanel>
</TabItem> </TabItem>
<TabItem IsEnabled="False"> <TabItem Header="Disabled" IsEnabled="False">
<TabItem.Header>
<TextBlock
Text="Disabled"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="8">
</TextBlock>
</TabItem.Header>
<TextBlock>You should not see this.</TextBlock> <TextBlock>You should not see this.</TextBlock>
</TabItem> </TabItem>
</TabControl> </TabControl>
@ -82,10 +61,7 @@
<TabControl.ItemTemplate> <TabControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock <TextBlock
Text="{Binding Header}" Text="{Binding Header}">
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="8">
</TextBlock> </TextBlock>
</DataTemplate> </DataTemplate>
</TabControl.ItemTemplate> </TabControl.ItemTemplate>

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

@ -26,13 +26,13 @@ namespace ControlCatalog.Pages
{ {
Header = "Arch", Header = "Arch",
Text = "This is the first templated tab page.", Text = "This is the first templated tab page.",
Image = LoadBitmap("resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg?assembly=ControlCatalog"), Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"),
}, },
new TabItemViewModel new TabItemViewModel
{ {
Header = "Leaf", Header = "Leaf",
Text = "This is the second templated tab page.", Text = "This is the second templated tab page.",
Image = LoadBitmap("resm:ControlCatalog.Assets.maple-leaf-888807_640.jpg?assembly=ControlCatalog"), Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"),
}, },
new TabItemViewModel new TabItemViewModel
{ {

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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TextBox</TextBlock> <TextBlock Classes="h1">TextBox</TextBlock>
<TextBlock Classes="h2">A control into which the user can input text</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." /> 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>
<StackPanel Orientation="Vertical" Spacing="8"> <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 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 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" 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"/> <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>
<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>
</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" <StackPanel Orientation="Vertical"
Spacing="4"> Spacing="4">
<TextBlock Classes="h1">ToolTip</TextBlock> <TextBlock Classes="h1">ToolTip</TextBlock>
@ -38,4 +40,4 @@
</Border> </Border>
</Grid> </Grid>
</StackPanel> </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"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TreeView</TextBlock> <TextBlock Classes="h1">TreeView</TextBlock>
<TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock> <TextBlock Classes="h2">Displays a hierachical tree of data.</TextBlock>
@ -16,4 +18,4 @@
</TreeView> </TreeView>
</StackPanel> </StackPanel>
</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);
}
}
}

6
samples/ControlCatalog/SideBar.xaml

@ -1,5 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles 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.SideBar">
<Style Selector="TabControl.sidebar"> <Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/> <Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/> <Setter Property="Padding" Value="8 0 0 0"/>
@ -17,8 +18,7 @@
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}" VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}"> Background="{TemplateBinding Background}">
<ItemsPresenter <ItemsPresenter
Name="PART_ItemsPresenter" Name="PART_ItemsPresenter"
MinWidth="190"
Items="{TemplateBinding Items}" Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}" ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}" ItemTemplate="{TemplateBinding ItemTemplate}"

5
samples/RenderDemo/MainWindow.xaml

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

35
samples/RenderDemo/Pages/AnimationsPage.xaml

@ -43,7 +43,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="Border.Rect1:pointerover"> <Style Selector="Border.Rect1:pointerover">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:2.5" <Animation Duration="0:0:2.5"
RepeatCount="4" IterationCount="4"
FillMode="None" FillMode="None"
PlaybackDirection="AlternateReverse" PlaybackDirection="AlternateReverse"
Easing="SineEaseInOut"> Easing="SineEaseInOut">
@ -73,7 +73,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:0.5" <Animation Duration="0:0:0.5"
Easing="QuadraticEaseInOut" Easing="QuadraticEaseInOut"
RepeatCount="Loop"> IterationCount="Infinite">
<KeyFrame Cue="50%"> <KeyFrame Cue="50%">
<Setter Property="ScaleTransform.ScaleX" Value="0.8"/> <Setter Property="ScaleTransform.ScaleX" Value="0.8"/>
<Setter Property="ScaleTransform.ScaleY" Value="0.8"/> <Setter Property="ScaleTransform.ScaleY" Value="0.8"/>
@ -87,6 +87,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Animation Duration="0:0:3" Easing="BounceEaseInOut"> <Animation Duration="0:0:3" Easing="BounceEaseInOut">
<KeyFrame Cue="48%"> <KeyFrame Cue="48%">
<Setter Property="TranslateTransform.Y" Value="-100"/> <Setter Property="TranslateTransform.Y" Value="-100"/>
<Setter Property="Background" Value="Magenta"/>
</KeyFrame> </KeyFrame>
</Animation> </Animation>
</Style.Animations> </Style.Animations>
@ -103,6 +104,35 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="Border.Rect6">
<Style.Animations>
<Animation Duration="0:0:3"
IterationCount="Infinite"
PlaybackDirection="Alternate">
<KeyFrame Cue="0%">
<Setter Property="Background" Value="Red"/>
</KeyFrame>
<KeyFrame Cue="15%">
<Setter Property="Background" Value="Yellow"/>
</KeyFrame>
<KeyFrame Cue="30%">
<Setter Property="Background" Value="Green"/>
</KeyFrame>
<KeyFrame Cue="45%">
<Setter Property="Background" Value="Cyan"/>
</KeyFrame>
<KeyFrame Cue="60%">
<Setter Property="Background" Value="Blue"/>
</KeyFrame>
<KeyFrame Cue="75%">
<Setter Property="Background" Value="Indigo"/>
</KeyFrame>
<KeyFrame Cue="90%">
<Setter Property="Background" Value="Violet"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Styles> </Styles>
</UserControl.Styles> </UserControl.Styles>
<Grid> <Grid>
@ -120,6 +150,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Border Classes="Test Rect3"/> <Border Classes="Test Rect3"/>
<Border Classes="Test Rect4" Background="Navy"/> <Border Classes="Test Rect4" Background="Navy"/>
<Border Classes="Test Rect5" Background="SeaGreen"/> <Border Classes="Test Rect5" Background="SeaGreen"/>
<Border Classes="Test Rect6" Background="Red"/>
</WrapPanel> </WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>

2
samples/RenderDemo/Pages/ClippingPage.xaml

@ -8,7 +8,7 @@ xmlns="https://github.com/avaloniaui">
</Style> </Style>
<Style Selector="Border#clipChild"> <Style Selector="Border#clipChild">
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:2" RepeatCount="Loop"> <Animation Duration="0:0:2" IterationCount="Infinite">
<KeyFrame Cue="100%"> <KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="360"/> <Setter Property="RotateTransform.Angle" Value="360"/>
</KeyFrame> </KeyFrame>

113
samples/RenderDemo/SideBar.xaml

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

1
src/Avalonia.Animation/Animatable.cs

@ -7,6 +7,7 @@ using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {

199
src/Avalonia.Animation/Animation.cs

@ -7,68 +7,209 @@ using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.Metadata;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Tracks the progress of an animation. /// Tracks the progress of an animation.
/// </summary> /// </summary>
public class Animation : AvaloniaList<KeyFrame>, IAnimation public class Animation : AvaloniaObject, IAnimation
{ {
/// <summary>
/// Defines the <see cref="Duration"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DurationProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_duration),
o => o._duration,
(o, v) => o._duration = v);
/// <summary>
/// Defines the <see cref="IterationCount"/> property.
/// </summary>
public static readonly DirectProperty<Animation, IterationCount> IterationCountProperty =
AvaloniaProperty.RegisterDirect<Animation, IterationCount>(
nameof(_iterationCount),
o => o._iterationCount,
(o, v) => o._iterationCount = v);
/// <summary>
/// Defines the <see cref="PlaybackDirection"/> property.
/// </summary>
public static readonly DirectProperty<Animation, PlaybackDirection> PlaybackDirectionProperty =
AvaloniaProperty.RegisterDirect<Animation, PlaybackDirection>(
nameof(_playbackDirection),
o => o._playbackDirection,
(o, v) => o._playbackDirection = v);
/// <summary>
/// Defines the <see cref="FillMode"/> property.
/// </summary>
public static readonly DirectProperty<Animation, FillMode> FillModeProperty =
AvaloniaProperty.RegisterDirect<Animation, FillMode>(
nameof(_fillMode),
o => o._fillMode,
(o, v) => o._fillMode = v);
/// <summary>
/// Defines the <see cref="Easing"/> property.
/// </summary>
public static readonly DirectProperty<Animation, Easing> EasingProperty =
AvaloniaProperty.RegisterDirect<Animation, Easing>(
nameof(_easing),
o => o._easing,
(o, v) => o._easing = v);
/// <summary>
/// Defines the <see cref="Delay"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delay),
o => o._delay,
(o, v) => o._delay = v);
/// <summary>
/// Defines the <see cref="DelayBetweenIterations"/> property.
/// </summary>
public static readonly DirectProperty<Animation, TimeSpan> DelayBetweenIterationsProperty =
AvaloniaProperty.RegisterDirect<Animation, TimeSpan>(
nameof(_delayBetweenIterations),
o => o._delayBetweenIterations,
(o, v) => o._delayBetweenIterations = v);
/// <summary>
/// Defines the <see cref="SpeedRatio"/> property.
/// </summary>
public static readonly DirectProperty<Animation, double> SpeedRatioProperty =
AvaloniaProperty.RegisterDirect<Animation, double>(
nameof(_speedRatio),
o => o._speedRatio,
(o, v) => o._speedRatio = v,
defaultBindingMode: BindingMode.TwoWay);
private TimeSpan _duration;
private IterationCount _iterationCount = new IterationCount(1);
private PlaybackDirection _playbackDirection;
private FillMode _fillMode;
private Easing _easing = new LinearEasing();
private TimeSpan _delay = TimeSpan.Zero;
private TimeSpan _delayBetweenIterations = TimeSpan.Zero;
private double _speedRatio = 1d;
/// <summary> /// <summary>
/// Gets or sets the active time of this animation. /// Gets or sets the active time of this animation.
/// </summary> /// </summary>
public TimeSpan Duration { get; set; } public TimeSpan Duration
{
get { return _duration; }
set { SetAndRaise(DurationProperty, ref _duration, value); }
}
/// <summary> /// <summary>
/// Gets or sets the repeat count for this animation. /// Gets or sets the repeat count for this animation.
/// </summary> /// </summary>
public RepeatCount RepeatCount { get; set; } public IterationCount IterationCount
{
get { return _iterationCount; }
set { SetAndRaise(IterationCountProperty, ref _iterationCount, value); }
}
/// <summary> /// <summary>
/// Gets or sets the playback direction for this animation. /// Gets or sets the playback direction for this animation.
/// </summary> /// </summary>
public PlaybackDirection PlaybackDirection { get; set; } public PlaybackDirection PlaybackDirection
{
get { return _playbackDirection; }
set { SetAndRaise(PlaybackDirectionProperty, ref _playbackDirection, value); }
}
/// <summary> /// <summary>
/// Gets or sets the value fill mode for this animation. /// Gets or sets the value fill mode for this animation.
/// </summary> /// </summary>
public FillMode FillMode { get; set; } public FillMode FillMode
{
get { return _fillMode; }
set { SetAndRaise(FillModeProperty, ref _fillMode, value); }
}
/// <summary> /// <summary>
/// Gets or sets the easing function to be used for this animation. /// Gets or sets the easing function to be used for this animation.
/// </summary> /// </summary>
public Easing Easing { get; set; } = new LinearEasing(); public Easing Easing
{
/// <summary> get { return _easing; }
/// Gets or sets the speed multiple for this animation. set { SetAndRaise(EasingProperty, ref _easing, value); }
/// </summary> }
public double SpeedRatio { get; set; } = 1d;
/// <summary> /// <summary>
/// Gets or sets the delay time for this animation. /// Gets or sets the initial delay time for this animation.
/// </summary> /// </summary>
/// <remarks> public TimeSpan Delay
/// Describes a delay to be added before the animation starts, and optionally between {
/// repeats of the animation if <see cref="DelayBetweenIterations"/> is set. get { return _delay; }
/// </remarks> set { SetAndRaise(DelayProperty, ref _delay, value); }
public TimeSpan Delay { get; set; } }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether <see cref="Delay"/> will be applied between /// Gets or sets the delay time in between iterations.
/// iterations of the animation. /// </summary>
public TimeSpan DelayBetweenIterations
{
get { return _delayBetweenIterations; }
set { SetAndRaise(DelayBetweenIterationsProperty, ref _delayBetweenIterations, value); }
}
/// <summary>
/// Gets or sets the speed multiple for this animation.
/// </summary> /// </summary>
/// <remarks> public double SpeedRatio
/// If this property is not set, then <see cref="Delay"/> will only be applied to the first {
/// iteration of the animation. get { return _speedRatio; }
/// </remarks> set { SetAndRaise(SpeedRatioProperty, ref _speedRatio, value); }
public bool DelayBetweenIterations { get; set; } }
/// <summary>
/// Obsolete: Do not use this property, use <see cref="IterationCount"/> instead.
/// </summary>
/// <value></value>
[Obsolete("This property has been superceded by IterationCount.")]
public string RepeatCount
{
get { return IterationCount.ToString(); }
set
{
var val = value.ToUpper();
val = val.Replace("LOOP", "INFINITE");
val = val.Replace("NONE", "1");
IterationCount = IterationCount.Parse(val);
}
}
/// <summary>
/// Gets the children of the <see cref="Animation"/>.
/// </summary>
[Content]
public KeyFrames Children { get; } = new KeyFrames();
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)> private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{ {
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ) ( prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator) ),
( prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator) ),
( prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator) ),
( prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator) ),
( prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator) ),
( prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator) ),
( prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator) ),
( prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator) ),
( prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator) ),
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ),
( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
}; };
public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition) public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
@ -95,9 +236,9 @@ namespace Avalonia.Animation
var animatorKeyFrames = new List<AnimatorKeyFrame>(); var animatorKeyFrames = new List<AnimatorKeyFrame>();
var subscriptions = new List<IDisposable>(); var subscriptions = new List<IDisposable>();
foreach (var keyframe in this) foreach (var keyframe in Children)
{ {
foreach (var setter in keyframe) foreach (var setter in keyframe.Setters)
{ {
var handler = GetAnimatorType(setter.Property); var handler = GetAnimatorType(setter.Property);
@ -179,7 +320,7 @@ namespace Avalonia.Animation
{ {
var run = new TaskCompletionSource<object>(); var run = new TaskCompletionSource<object>();
if (this.RepeatCount == RepeatCount.Loop) if (this.IterationCount == IterationCount.Infinite)
run.SetException(new InvalidOperationException("Looping animations must not use the Run method.")); run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
IDisposable subscriptions = null; IDisposable subscriptions = null;

204
src/Avalonia.Animation/AnimationInstance`1.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
@ -15,78 +16,77 @@ namespace Avalonia.Animation
{ {
private T _lastInterpValue; private T _lastInterpValue;
private T _firstKFValue; private T _firstKFValue;
private long _repeatCount; private ulong? _iterationCount;
private double _currentIteration; private ulong _currentIteration;
private bool _isLooping;
private bool _gotFirstKFValue; private bool _gotFirstKFValue;
private bool _iterationDelay;
private FillMode _fillMode; private FillMode _fillMode;
private PlaybackDirection _animationDirection; private PlaybackDirection _playbackDirection;
private Animator<T> _parent; private Animator<T> _animator;
private Animation _animation;
private Animatable _targetControl; private Animatable _targetControl;
private T _neutralValue; private T _neutralValue;
private double _speedRatio; private double _speedRatioConv;
private TimeSpan _delay; private TimeSpan _initialDelay;
private TimeSpan _iterationDelay;
private TimeSpan _duration; private TimeSpan _duration;
private Easings.Easing _easeFunc; private Easings.Easing _easeFunc;
private Action _onCompleteAction; private Action _onCompleteAction;
private Func<double, T, T> _interpolator; private Func<double, T, T> _interpolator;
private IDisposable _timerSubscription; private IDisposable _timerSub;
private readonly IClock _baseClock; private readonly IClock _baseClock;
private IClock _clock; private IClock _clock;
public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator) public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator)
{ {
if (animation.SpeedRatio <= 0) _animator = animator;
throw new InvalidOperationException("Speed ratio cannot be negative or zero."); _animation = animation;
_targetControl = control;
_onCompleteAction = OnComplete;
_interpolator = Interpolator;
_baseClock = baseClock;
_neutralValue = (T)_targetControl.GetValue(_animator.Property);
if (animation.Duration.TotalSeconds <= 0) FetchProperties();
throw new InvalidOperationException("Duration cannot be negative or zero."); }
_parent = animator; private void FetchProperties()
_easeFunc = animation.Easing; {
_targetControl = control; if (_animation.SpeedRatio < 0d)
_neutralValue = (T)_targetControl.GetValue(_parent.Property); throw new ArgumentOutOfRangeException("SpeedRatio value should not be negative.");
_speedRatio = animation.SpeedRatio; if (_animation.Duration.TotalSeconds <= 0)
throw new InvalidOperationException("Duration value cannot be negative or zero.");
_delay = animation.Delay; _easeFunc = _animation.Easing;
_duration = animation.Duration;
_iterationDelay = animation.DelayBetweenIterations;
switch (animation.RepeatCount.RepeatType) _speedRatioConv = 1d / _animation.SpeedRatio;
{
case RepeatType.None:
_repeatCount = 1;
break;
case RepeatType.Loop:
_isLooping = true;
break;
case RepeatType.Repeat:
_repeatCount = (long)animation.RepeatCount.Value;
break;
}
_animationDirection = animation.PlaybackDirection; _initialDelay = _animation.Delay;
_fillMode = animation.FillMode; _duration = _animation.Duration;
_onCompleteAction = OnComplete; _iterationDelay = _animation.DelayBetweenIterations;
_interpolator = Interpolator;
_baseClock = baseClock; if (_animation.IterationCount.RepeatType == IterationType.Many)
} _iterationCount = _animation.IterationCount.Value;
else
_iterationCount = null;
_playbackDirection = _animation.PlaybackDirection;
_fillMode = _animation.FillMode;
}
protected override void Unsubscribed() protected override void Unsubscribed()
{ {
//Animation may have been stopped before it has finished // Animation may have been stopped before it has finished.
ApplyFinalFill(); ApplyFinalFill();
_timerSubscription?.Dispose(); _timerSub?.Dispose();
_clock.PlayState = PlayState.Stop; _clock.PlayState = PlayState.Stop;
} }
protected override void Subscribed() protected override void Subscribed()
{ {
_clock = new Clock(_baseClock); _clock = new Clock(_baseClock);
_timerSubscription = _clock.Subscribe(Step); _timerSub = _clock.Subscribe(Step);
} }
public void Step(TimeSpan frameTick) public void Step(TimeSpan frameTick)
@ -104,7 +104,7 @@ namespace Avalonia.Animation
private void ApplyFinalFill() private void ApplyFinalFill()
{ {
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both) if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both)
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue); _targetControl.SetValue(_animator.Property, _lastInterpValue, BindingPriority.LocalValue);
} }
private void DoComplete() private void DoComplete()
@ -130,7 +130,7 @@ namespace Avalonia.Animation
if (!_gotFirstKFValue) if (!_gotFirstKFValue)
{ {
_firstKFValue = (T)_parent.First().Value; _firstKFValue = (T)_animator.First().Value;
_gotFirstKFValue = true; _gotFirstKFValue = true;
} }
} }
@ -138,75 +138,77 @@ namespace Avalonia.Animation
private void InternalStep(TimeSpan time) private void InternalStep(TimeSpan time)
{ {
DoPlayStates(); DoPlayStates();
var delayEndpoint = _delay;
var iterationEndpoint = delayEndpoint + _duration;
var iterationTime = time;
//determine if time is currently in the first iteration. FetchProperties();
if (time >= TimeSpan.Zero & time <= iterationEndpoint)
{
_currentIteration = 1;
}
else if (time > iterationEndpoint)
{
//Subtract first iteration to properly get the subsequent iteration time
iterationTime -= iterationEndpoint;
if (!_iterationDelay & delayEndpoint > TimeSpan.Zero) // Scale timebases according to speedratio.
{ var indexTime = time.Ticks;
delayEndpoint = TimeSpan.Zero; var iterDuration = _duration.Ticks * _speedRatioConv;
iterationEndpoint = _duration; var iterDelay = _iterationDelay.Ticks * _speedRatioConv;
} var initDelay = _initialDelay.Ticks * _speedRatioConv;
//Calculate the current iteration number
_currentIteration = Math.Min(_repeatCount,(int)Math.Floor((double)((double)iterationTime.Ticks / iterationEndpoint.Ticks)) + 2);
}
else
{
return;
}
// Determine if the current iteration should have its normalized time inverted. if (indexTime > 0 & indexTime <= initDelay)
bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false :
_animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true :
_animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false :
_animationDirection == PlaybackDirection.Reverse ? true : false;
if (!_isLooping)
{
var totalTime = _iterationDelay ? _repeatCount * ( _duration.Ticks + _delay.Ticks) : _repeatCount * _duration.Ticks + _delay.Ticks;
if (time.Ticks >= totalTime)
{
var easedTime = _easeFunc.Ease(isCurIterReverse ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
DoComplete();
return;
}
}
iterationTime = TimeSpan.FromTicks((long)(iterationTime.Ticks % iterationEndpoint.Ticks));
if (delayEndpoint > TimeSpan.Zero & iterationTime < delayEndpoint)
{ {
DoDelay(); DoDelay();
} }
else else
{ {
// Offset the delay time // Calculate timebases.
iterationTime -= delayEndpoint; var iterationTime = iterDuration + iterDelay;
iterationEndpoint -= delayEndpoint; var opsTime = indexTime - initDelay;
var playbackTime = opsTime % iterationTime;
// Normalize time _currentIteration = (ulong)(opsTime / iterationTime);
var interpVal = (double)iterationTime.Ticks / iterationEndpoint.Ticks;
if (isCurIterReverse) // Stop animation when the current iteration is beyond the iteration count.
interpVal = 1 - interpVal; if ((_currentIteration + 1) > _iterationCount)
DoComplete();
// Ease and interpolate if (playbackTime <= iterDuration)
var easedTime = _easeFunc.Ease(interpVal); {
_lastInterpValue = _interpolator(easedTime, _neutralValue); // Normalize time for interpolation.
var normalizedTime = playbackTime / iterDuration;
// Check if normalized time needs to be reversed according to PlaybackDirection
bool playbackReversed;
switch (_playbackDirection)
{
case PlaybackDirection.Normal:
playbackReversed = false;
break;
case PlaybackDirection.Reverse:
playbackReversed = true;
break;
case PlaybackDirection.Alternate:
playbackReversed = (_currentIteration % 2 == 0) ? false : true;
break;
case PlaybackDirection.AlternateReverse:
playbackReversed = (_currentIteration % 2 == 0) ? true : false;
break;
default:
throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}");
}
if (playbackReversed)
normalizedTime = 1 - normalizedTime;
// Ease and interpolate
var easedTime = _easeFunc.Ease(normalizedTime);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
PublishNext(_lastInterpValue); PublishNext(_lastInterpValue);
}
else if (playbackTime > iterDuration &
playbackTime <= iterationTime &
iterDelay > 0)
{
// The last iteration's trailing delay should be skipped.
if ((_currentIteration + 1) < _iterationCount)
DoDelay();
else
DoComplete();
}
} }
} }
} }

73
src/Avalonia.Animation/Animator`1.cs → src/Avalonia.Animation/Animators/Animator`1.cs

@ -10,10 +10,10 @@ using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
namespace Avalonia.Animation namespace Avalonia.Animation.Animators
{ {
/// <summary> /// <summary>
/// Base class for KeyFrames objects /// Base class for <see cref="Animator{T}"/> objects
/// </summary> /// </summary>
public abstract class Animator<T> : AvaloniaList<AnimatorKeyFrame>, IAnimator public abstract class Animator<T> : AvaloniaList<AnimatorKeyFrame>, IAnimator
{ {
@ -45,17 +45,10 @@ namespace Avalonia.Animation
return match.Subscribe(subject); return match.Subscribe(subject);
} }
/// <summary> protected T InterpolationHandler(double animationTime, T neutralValue)
/// Get the nearest pair of cue-time ordered keyframes
/// according to the given time parameter that is relative to the
/// total animation time and the normalized intra-keyframe pair time
/// (i.e., the normalized time between the selected keyframes, relative to the
/// time parameter).
/// </summary>
/// <param name="animationTime">The time parameter, relative to the total animation time</param>
protected (double IntraKFTime, KeyFramePair<T> KFPair) GetKFPairAndIntraKFTime(double animationTime)
{ {
AnimatorKeyFrame firstKeyframe, lastKeyframe; AnimatorKeyFrame firstKeyframe, lastKeyframe;
int kvCount = _convertedKeyframes.Count; int kvCount = _convertedKeyframes.Count;
if (kvCount > 2) if (kvCount > 2)
{ {
@ -84,38 +77,31 @@ namespace Avalonia.Animation
double t0 = firstKeyframe.Cue.CueValue; double t0 = firstKeyframe.Cue.CueValue;
double t1 = lastKeyframe.Cue.CueValue; double t1 = lastKeyframe.Cue.CueValue;
var intraframeTime = (animationTime - t0) / (t1 - t0);
var firstFrameData = (firstKeyframe.GetTypedValue<T>(), firstKeyframe.isNeutral); double progress = (animationTime - t0) / (t1 - t0);
var lastFrameData = (lastKeyframe.GetTypedValue<T>(), lastKeyframe.isNeutral);
return (intraframeTime, new KeyFramePair<T>(firstFrameData, lastFrameData)); T oldValue, newValue;
if (firstKeyframe.isNeutral)
oldValue = neutralValue;
else
oldValue = (T)firstKeyframe.Value;
if (lastKeyframe.isNeutral)
newValue = neutralValue;
else
newValue = (T)lastKeyframe.Value;
return Interpolate(progress, oldValue, newValue);
} }
private int FindClosestBeforeKeyFrame(double time) private int FindClosestBeforeKeyFrame(double time)
{ {
int FindClosestBeforeKeyFrame(int startIndex, int length) for (int i = 0; i < _convertedKeyframes.Count; i++)
{ if (_convertedKeyframes[i].Cue.CueValue > time)
if (length == 0 || length == 1) return i - 1;
{
return startIndex;
}
int middle = startIndex + (length / 2); throw new Exception("Index time is out of keyframe time range.");
if (_convertedKeyframes[middle].Cue.CueValue < time)
{
return FindClosestBeforeKeyFrame(middle, length - middle);
}
else if (_convertedKeyframes[middle].Cue.CueValue > time)
{
return FindClosestBeforeKeyFrame(startIndex, middle - startIndex);
}
else
{
return middle;
}
}
return FindClosestBeforeKeyFrame(0, _convertedKeyframes.Count);
} }
/// <summary> /// <summary>
@ -129,18 +115,15 @@ namespace Avalonia.Animation
this, this,
clock ?? control.Clock ?? Clock.GlobalClock, clock ?? control.Clock ?? Clock.GlobalClock,
onComplete, onComplete,
DoInterpolation); InterpolationHandler);
return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation); return control.Bind<T>((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
} }
/// <summary> /// <summary>
/// Interpolates a value given the desired time. /// Interpolates in-between two key values given the desired progress time.
/// </summary> /// </summary>
protected abstract T DoInterpolation(double time, T neutralValue); public abstract T Interpolate(double progress, T oldValue, T newValue);
/// <summary>
/// Verifies, converts and sorts keyframe values according to this class's target type.
/// </summary>
private void VerifyConvertKeyFrames() private void VerifyConvertKeyFrames()
{ {
foreach (AnimatorKeyFrame keyframe in this) foreach (AnimatorKeyFrame keyframe in this)
@ -188,4 +171,4 @@ namespace Avalonia.Animation
} }
} }
} }
} }

21
src/Avalonia.Animation/Animators/BoolAnimator.cs

@ -0,0 +1,21 @@
// 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.
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="bool"/> properties.
/// </summary>
public class BoolAnimator : Animator<bool>
{
/// <inheritdocs/>
public override bool Interpolate(double progress, bool oldValue, bool newValue)
{
if(progress >= 1d)
return newValue;
if(progress >= 0)
return oldValue;
return oldValue;
}
}
}

24
src/Avalonia.Animation/Animators/ByteAnimator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="byte"/> properties.
/// </summary>
public class ByteAnimator : Animator<byte>
{
const double maxVal = (double)byte.MaxValue;
/// <inheritdocs/>
public override byte Interpolate(double progress, byte oldValue, byte newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (byte)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

17
src/Avalonia.Animation/Animators/DecimalAnimator.cs

@ -0,0 +1,17 @@
// 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.
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="decimal"/> properties.
/// </summary>
public class DecimalAnimator : Animator<decimal>
{
/// <inheritdocs/>
public override decimal Interpolate(double progress, decimal oldValue, decimal newValue)
{
return ((newValue - oldValue) * (decimal)progress) + oldValue;
}
}
}

17
src/Avalonia.Animation/Animators/DoubleAnimator.cs

@ -0,0 +1,17 @@
// 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.
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="double"/> properties.
/// </summary>
public class DoubleAnimator : Animator<double>
{
/// <inheritdocs/>
public override double Interpolate(double progress, double oldValue, double newValue)
{
return ((newValue - oldValue) * progress) + oldValue;
}
}
}

17
src/Avalonia.Animation/Animators/FloatAnimator.cs

@ -0,0 +1,17 @@
// 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.
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="float"/> properties.
/// </summary>
public class FloatAnimator : Animator<float>
{
/// <inheritdocs/>
public override float Interpolate(double progress, float oldValue, float newValue)
{
return (float)(((newValue - oldValue) * progress) + oldValue);
}
}
}

24
src/Avalonia.Animation/Animators/Int16Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int16"/> properties.
/// </summary>
public class Int16Animator : Animator<Int16>
{
const double maxVal = (double)Int16.MaxValue;
/// <inheritdocs/>
public override Int16 Interpolate(double progress, Int16 oldValue, Int16 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int16)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/Int32Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int32"/> properties.
/// </summary>
public class Int32Animator : Animator<Int32>
{
const double maxVal = (double)Int32.MaxValue;
/// <inheritdocs/>
public override Int32 Interpolate(double progress, Int32 oldValue, Int32 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int32)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/Int64Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="Int64"/> properties.
/// </summary>
public class Int64Animator : Animator<Int64>
{
const double maxVal = (double)Int64.MaxValue;
/// <inheritdocs/>
public override Int64 Interpolate(double progress, Int64 oldValue, Int64 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (Int64)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt16Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt16"/> properties.
/// </summary>
public class UInt16Animator : Animator<UInt16>
{
const double maxVal = (double)UInt16.MaxValue;
/// <inheritdocs/>
public override UInt16 Interpolate(double progress, UInt16 oldValue, UInt16 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt16)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt32Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt32"/> properties.
/// </summary>
public class UInt32Animator : Animator<UInt32>
{
const double maxVal = (double)UInt32.MaxValue;
/// <inheritdocs/>
public override UInt32 Interpolate(double progress, UInt32 oldValue, UInt32 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt32)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

24
src/Avalonia.Animation/Animators/UInt64Animator.cs

@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Animation.Animators
{
/// <summary>
/// Animator that handles <see cref="UInt64"/> properties.
/// </summary>
public class UInt64Animator : Animator<UInt64>
{
const double maxVal = (double)UInt64.MaxValue;
/// <inheritdocs/>
public override UInt64 Interpolate(double progress, UInt64 oldValue, UInt64 newValue)
{
var normOV = oldValue / maxVal;
var normNV = newValue / maxVal;
var deltaV = normNV - normOV;
return (UInt64)Math.Round(maxVal * ((deltaV * progress) + normOV));
}
}
}

1
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />

3
src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Data; using Avalonia.Data;
@ -33,8 +34,6 @@ namespace Avalonia.Animation
this._onComplete = onComplete; this._onComplete = onComplete;
this._clock = clock; this._clock = clock;
} }
public void Dispose() public void Dispose()
{ {
_lastInstance?.Dispose(); _lastInstance?.Dispose();

35
src/Avalonia.Animation/DoubleAnimator.cs

@ -1,35 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Animation
{
/// <summary>
/// Animator that handles <see cref="double"/> properties.
/// </summary>
public class DoubleAnimator : Animator<double>
{
/// <inheritdocs/>
protected override double DoInterpolation(double t, double neutralValue)
{
var pair = GetKFPairAndIntraKFTime(t);
double y0, y1;
var firstKF = pair.KFPair.FirstKeyFrame;
var secondKF = pair.KFPair.SecondKeyFrame;
if (firstKF.isNeutral)
y0 = neutralValue;
else
y0 = firstKF.TargetValue;
if (secondKF.isNeutral)
y1 = neutralValue;
else
y1 = secondKF.TargetValue;
// Do linear parametric interpolation
return y0 + (pair.IntraKFTime) * (y1 - y0);
}
}
}

0
src/Avalonia.Animation/IEasing.cs → src/Avalonia.Animation/Easing/IEasing.cs

176
src/Avalonia.Animation/IterationCount.cs

@ -0,0 +1,176 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.ComponentModel;
using System.Globalization;
namespace Avalonia.Animation
{
/// <summary>
/// Defines the valid modes for a <see cref="IterationCount"/>.
/// </summary>
public enum IterationType
{
Many,
Infinite
}
/// <summary>
/// Determines the number of iterations of an animation.
/// Also defines its repeat behavior.
/// </summary>
[TypeConverter(typeof(IterationCountTypeConverter))]
public struct IterationCount : IEquatable<IterationCount>
{
private readonly IterationType _type;
private readonly ulong _value;
/// <summary>
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
/// </summary>
/// <param name="value">The number of iterations of an animation.</param>
public IterationCount(ulong value)
: this(value, IterationType.Many)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="IterationCount"/> struct.
/// </summary>
/// <param name="value">The size of the IterationCount.</param>
/// <param name="type">The unit of the IterationCount.</param>
public IterationCount(ulong value, IterationType type)
{
if (type > IterationType.Infinite)
{
throw new ArgumentException("Invalid value", "type");
}
_type = type;
_value = value;
}
/// <summary>
/// Gets an instance of <see cref="IterationCount"/> that indicates that an animation
/// should repeat forever.
/// </summary>
public static IterationCount Infinite => new IterationCount(0, IterationType.Infinite);
/// <summary>
/// Gets the unit of the <see cref="IterationCount"/>.
/// </summary>
public IterationType RepeatType => _type;
/// <summary>
/// Gets a value that indicates whether the <see cref="IterationCount"/> is set to loop.
/// </summary>
public bool IsInfinite => _type == IterationType.Infinite;
/// <summary>
/// Gets the number of repeat iterations.
/// </summary>
public ulong Value => _value;
/// <summary>
/// Compares two IterationCount structures for equality.
/// </summary>
/// <param name="a">The first IterationCount.</param>
/// <param name="b">The second IterationCount.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public static bool operator ==(IterationCount a, IterationCount b)
{
return (a.IsInfinite && b.IsInfinite)
|| (a._value == b._value && a._type == b._type);
}
/// <summary>
/// Compares two IterationCount structures for inequality.
/// </summary>
/// <param name="rc1">The first IterationCount.</param>
/// <param name="rc2">The first IterationCount.</param>
/// <returns>True if the structures are unequal, otherwise false.</returns>
public static bool operator !=(IterationCount rc1, IterationCount rc2)
{
return !(rc1 == rc2);
}
/// <summary>
/// Determines whether the <see cref="IterationCount"/> is equal to the specified object.
/// </summary>
/// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is IterationCount))
{
return false;
}
return this == (IterationCount)o;
}
/// <summary>
/// Compares two IterationCount structures for equality.
/// </summary>
/// <param name="IterationCount">The structure with which to test equality.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public bool Equals(IterationCount IterationCount)
{
return this == IterationCount;
}
/// <summary>
/// Gets a hash code for the IterationCount.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return _value.GetHashCode() ^ _type.GetHashCode();
}
/// <summary>
/// Gets a string representation of the <see cref="IterationCount"/>.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (IsInfinite)
{
return "Infinite";
}
string s = _value.ToString();
return s;
}
/// <summary>
/// Parses a string to return a <see cref="IterationCount"/>.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="IterationCount"/>.</returns>
public static IterationCount Parse(string s)
{
s = s.ToUpperInvariant().Trim();
if (s.EndsWith("INFINITE"))
{
return Infinite;
}
else
{
if (s.StartsWith("-"))
throw new InvalidCastException("IterationCount can't be a negative number.");
var value = ulong.Parse(s, CultureInfo.InvariantCulture);
return new IterationCount(value);
}
}
}
}

4
src/Avalonia.Animation/RepeatCountTypeConverter.cs → src/Avalonia.Animation/IterationCountTypeConverter.cs

@ -7,7 +7,7 @@ using System.Globalization;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
public class RepeatCountTypeConverter : TypeConverter public class IterationCountTypeConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ {
@ -16,7 +16,7 @@ namespace Avalonia.Animation
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{ {
return RepeatCount.Parse((string)value); return IterationCount.Parse((string)value);
} }
} }
} }

15
src/Avalonia.Animation/KeyFrame.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Metadata;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -17,7 +18,7 @@ namespace Avalonia.Animation
/// Stores data regarding a specific key /// Stores data regarding a specific key
/// point and value in an animation. /// point and value in an animation.
/// </summary> /// </summary>
public class KeyFrame : AvaloniaList<IAnimationSetter> public class KeyFrame : AvaloniaObject
{ {
private TimeSpan _ktimeSpan; private TimeSpan _ktimeSpan;
private Cue _kCue; private Cue _kCue;
@ -26,13 +27,11 @@ namespace Avalonia.Animation
{ {
} }
public KeyFrame(IEnumerable<IAnimationSetter> items) : base(items) /// <summary>
{ /// Gets the setters of <see cref="KeyFrame"/>.
} /// </summary>
[Content]
public KeyFrame(params IAnimationSetter[] items) : base(items) public AvaloniaList<IAnimationSetter> Setters { get; } = new AvaloniaList<IAnimationSetter>();
{
}
internal KeyFrameTimingMode TimingMode { get; private set; } internal KeyFrameTimingMode TimingMode { get; private set; }

33
src/Avalonia.Animation/KeyFramePair`1.cs

@ -1,33 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Animation
{
/// <summary>
/// Represents a pair of keyframe, usually the
/// Start and End keyframes of a <see cref="Animator{T}"/> object.
/// </summary>
public struct KeyFramePair<T>
{
/// <summary>
/// Initializes this <see cref="KeyFramePair{T}"/>
/// </summary>
/// <param name="FirstKeyFrame"></param>
/// <param name="LastKeyFrame"></param>
public KeyFramePair((T TargetValue, bool isNeutral) FirstKeyFrame, (T TargetValue, bool isNeutral) LastKeyFrame) : this()
{
this.FirstKeyFrame = FirstKeyFrame;
this.SecondKeyFrame = LastKeyFrame;
}
/// <summary>
/// First <see cref="KeyFrame"/> object.
/// </summary>
public (T TargetValue, bool isNeutral) FirstKeyFrame { get; }
/// <summary>
/// Second <see cref="KeyFrame"/> object.
/// </summary>
public (T TargetValue, bool isNeutral) SecondKeyFrame { get; }
}
}

33
src/Avalonia.Animation/KeyFrames.cs

@ -0,0 +1,33 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Collections;
namespace Avalonia.Animation
{
/// <summary>
/// A collection of <see cref="KeyFrame"/>s.
/// </summary>
public class KeyFrames : AvaloniaList<KeyFrame>
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
/// </summary>
public KeyFrames()
{
ResetBehavior = ResetBehavior.Remove;
}
/// <summary>
/// Initializes a new instance of the <see cref="KeyFrames"/> class.
/// </summary>
/// <param name="items">The initial items in the collection.</param>
public KeyFrames(IEnumerable<KeyFrame> items)
: base(items)
{
ResetBehavior = ResetBehavior.Remove;
}
}
}

3
src/Avalonia.Animation/Properties/AssemblyInfo.cs

@ -5,4 +5,5 @@ using Avalonia.Metadata;
using System.Reflection; using System.Reflection;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")]

199
src/Avalonia.Animation/RepeatCount.cs

@ -1,199 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.ComponentModel;
using System.Globalization;
namespace Avalonia.Animation
{
/// <summary>
/// Defines the valid modes for a <see cref="RepeatCount"/>.
/// </summary>
public enum RepeatType
{
None,
Repeat,
Loop
}
/// <summary>
/// Determines the number of iterations of an animation.
/// Also defines its repeat behavior.
/// </summary>
[TypeConverter(typeof(RepeatCountTypeConverter))]
public struct RepeatCount : IEquatable<RepeatCount>
{
private readonly RepeatType _type;
private readonly ulong _value;
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
/// </summary>
/// <param name="value">The number of iterations of an animation.</param>
public RepeatCount(ulong value)
: this(value, RepeatType.Repeat)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RepeatCount"/> struct.
/// </summary>
/// <param name="value">The size of the RepeatCount.</param>
/// <param name="type">The unit of the RepeatCount.</param>
public RepeatCount(ulong value, RepeatType type)
{
if (type < RepeatType.None || type > RepeatType.Loop)
{
throw new ArgumentException("Invalid value", "type");
}
_type = type;
_value = value;
}
/// <summary>
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
/// should repeat forever.
/// </summary>
public static RepeatCount Loop => new RepeatCount(0, RepeatType.Loop);
/// <summary>
/// Gets an instance of <see cref="RepeatCount"/> that indicates that an animation
/// should not repeat.
/// </summary>
public static RepeatCount None => new RepeatCount(0, RepeatType.None);
/// <summary>
/// Gets the unit of the <see cref="RepeatCount"/>.
/// </summary>
public RepeatType RepeatType => _type;
/// <summary>
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to loop.
/// </summary>
public bool IsLoop => _type == RepeatType.Loop;
/// <summary>
/// Gets a value that indicates whether the <see cref="RepeatCount"/> is set to not repeat.
/// </summary>
public bool IsNone => _type == RepeatType.None;
/// <summary>
/// Gets the number of repeat iterations.
/// </summary>
public ulong Value => _value;
/// <summary>
/// Compares two RepeatCount structures for equality.
/// </summary>
/// <param name="a">The first RepeatCount.</param>
/// <param name="b">The second RepeatCount.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public static bool operator ==(RepeatCount a, RepeatCount b)
{
return (a.IsNone && b.IsNone) && (a.IsLoop && b.IsLoop)
|| (a._value == b._value && a._type == b._type);
}
/// <summary>
/// Compares two RepeatCount structures for inequality.
/// </summary>
/// <param name="rc1">The first RepeatCount.</param>
/// <param name="rc2">The first RepeatCount.</param>
/// <returns>True if the structures are unequal, otherwise false.</returns>
public static bool operator !=(RepeatCount rc1, RepeatCount rc2)
{
return !(rc1 == rc2);
}
/// <summary>
/// Determines whether the <see cref="RepeatCount"/> is equal to the specified object.
/// </summary>
/// <param name="o">The object with which to test equality.</param>
/// <returns>True if the objects are equal, otherwise false.</returns>
public override bool Equals(object o)
{
if (o == null)
{
return false;
}
if (!(o is RepeatCount))
{
return false;
}
return this == (RepeatCount)o;
}
/// <summary>
/// Compares two RepeatCount structures for equality.
/// </summary>
/// <param name="RepeatCount">The structure with which to test equality.</param>
/// <returns>True if the structures are equal, otherwise false.</returns>
public bool Equals(RepeatCount RepeatCount)
{
return this == RepeatCount;
}
/// <summary>
/// Gets a hash code for the RepeatCount.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
return _value.GetHashCode() ^ _type.GetHashCode();
}
/// <summary>
/// Gets a string representation of the <see cref="RepeatCount"/>.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
if (IsLoop)
{
return "Auto";
}
else if (IsNone)
{
return "None";
}
string s = _value.ToString();
return s;
}
/// <summary>
/// Parses a string to return a <see cref="RepeatCount"/>.
/// </summary>
/// <param name="s">The string.</param>
/// <returns>The <see cref="RepeatCount"/>.</returns>
public static RepeatCount Parse(string s)
{
s = s.ToUpperInvariant().Trim();
if (s == "NONE")
{
return None;
}
else if (s.EndsWith("LOOP"))
{
return Loop;
}
else
{
if(s.StartsWith("-"))
throw new InvalidCastException("RepeatCount can't be a negative number.");
var value = ulong.Parse(s, CultureInfo.InvariantCulture);
if (value == 1)
return None;
return new RepeatCount(value);
}
}
}
}

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

Loading…
Cancel
Save