Browse Source

Merge branch 'master' into add-remote-designer-test

add-remote-designer-test
Nikita Tsukanov 7 years ago
parent
commit
f360ac984d
  1. 27
      .github/PULL_REQUEST_TEMPLATE.md
  2. 1
      .gitignore
  3. 3
      .gitmodules
  4. 5
      .ncrunch/Avalonia.Desktop.v3.ncrunchproject
  5. 5
      .ncrunch/Avalonia.net461.v3.ncrunchproject
  6. 0
      .nuke
  7. 24
      .travis.yml
  8. 112
      Avalonia.sln
  9. 1
      Avalonia.v3.ncrunchsolution
  10. 2
      Directory.Build.props
  11. 23
      appveyor.yml
  12. 44
      azure-pipelines.yml
  13. 312
      build.cake
  14. 226
      build.ps1
  15. 139
      build.sh
  16. 2
      build/Magick.NET-Q16-AnyCPU.props
  17. 5
      build/NetFX.props
  18. 1
      build/ReferenceCoreLibraries.props
  19. 4
      build/SkiaSharp.props
  20. 8
      build/UnitTests.NetFX.props
  21. 1
      build/xunit.runner.mono.json
  22. 15
      cake.config
  23. 2
      global.json
  24. 6
      native/Avalonia.Native/src/OSX/gl.mm
  25. 21
      native/Avalonia.Native/src/OSX/window.mm
  26. 8
      nukebuild/.editorconfig
  27. 265
      nukebuild/Build.cs
  28. 142
      nukebuild/BuildParameters.cs
  29. 1
      nukebuild/Numerge
  30. 93
      nukebuild/Shims.cs
  31. 37
      nukebuild/_build.csproj
  32. 24
      nukebuild/_build.csproj.DotSettings
  33. 23
      nukebuild/numerge.config
  34. 8
      packages/Avalonia/Avalonia.csproj
  35. 2
      packages/Avalonia/Avalonia.props
  36. 129
      parameters.cake
  37. 6
      readme.md
  38. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  39. 10
      samples/ControlCatalog.NetCore/Program.cs
  40. 13
      samples/ControlCatalog/DecoratedWindow.xaml
  41. 2
      samples/ControlCatalog/MainView.xaml
  42. 1
      samples/ControlCatalog/Pages/DialogsPage.xaml
  43. 19
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  44. 9
      samples/ControlCatalog/Pages/DropDownPage.xaml
  45. 3
      samples/ControlCatalog/Pages/DropDownPage.xaml.cs
  46. 4
      samples/ControlCatalog/Pages/ScreenPage.cs
  47. 4
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  48. 2
      samples/ControlCatalog/SideBar.xaml
  49. 6
      samples/PlatformSanityChecks/App.xaml
  50. 13
      samples/PlatformSanityChecks/App.xaml.cs
  51. 13
      samples/PlatformSanityChecks/PlatformSanityChecks.csproj
  52. 132
      samples/PlatformSanityChecks/Program.cs
  53. 35
      samples/RenderDemo/Pages/AnimationsPage.xaml
  54. 2
      samples/RenderDemo/Pages/ClippingPage.xaml
  55. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  56. 8
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  57. 1
      src/Avalonia.Animation/Animatable.cs
  58. 199
      src/Avalonia.Animation/Animation.cs
  59. 207
      src/Avalonia.Animation/AnimationInstance`1.cs
  60. 1
      src/Avalonia.Animation/AnimatorKeyFrame.cs
  61. 73
      src/Avalonia.Animation/Animators/Animator`1.cs
  62. 21
      src/Avalonia.Animation/Animators/BoolAnimator.cs
  63. 24
      src/Avalonia.Animation/Animators/ByteAnimator.cs
  64. 17
      src/Avalonia.Animation/Animators/DecimalAnimator.cs
  65. 17
      src/Avalonia.Animation/Animators/DoubleAnimator.cs
  66. 17
      src/Avalonia.Animation/Animators/FloatAnimator.cs
  67. 24
      src/Avalonia.Animation/Animators/Int16Animator.cs
  68. 24
      src/Avalonia.Animation/Animators/Int32Animator.cs
  69. 24
      src/Avalonia.Animation/Animators/Int64Animator.cs
  70. 24
      src/Avalonia.Animation/Animators/UInt16Animator.cs
  71. 24
      src/Avalonia.Animation/Animators/UInt32Animator.cs
  72. 24
      src/Avalonia.Animation/Animators/UInt64Animator.cs
  73. 1
      src/Avalonia.Animation/Avalonia.Animation.csproj
  74. 3
      src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs
  75. 35
      src/Avalonia.Animation/DoubleAnimator.cs
  76. 0
      src/Avalonia.Animation/Easing/IEasing.cs
  77. 176
      src/Avalonia.Animation/IterationCount.cs
  78. 4
      src/Avalonia.Animation/IterationCountTypeConverter.cs
  79. 15
      src/Avalonia.Animation/KeyFrame.cs
  80. 33
      src/Avalonia.Animation/KeyFramePair`1.cs
  81. 33
      src/Avalonia.Animation/KeyFrames.cs
  82. 3
      src/Avalonia.Animation/Properties/AssemblyInfo.cs
  83. 199
      src/Avalonia.Animation/RepeatCount.cs
  84. 7
      src/Avalonia.Animation/Transitions/DoubleTransition.cs
  85. 0
      src/Avalonia.Animation/Transitions/FloatTransition.cs
  86. 0
      src/Avalonia.Animation/Transitions/IntegerTransition.cs
  87. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  88. 4
      src/Avalonia.Base/Collections/AvaloniaDictionary.cs
  89. 2
      src/Avalonia.Base/Collections/AvaloniaList.cs
  90. 24
      src/Avalonia.Base/Data/Converters/ObjectConverters.cs
  91. 4
      src/Avalonia.Base/Data/Converters/StringConverters.cs
  92. 1
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  93. 1
      src/Avalonia.Base/Platform/IAssetLoader.cs
  94. 29
      src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs
  95. 1
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  96. 9
      src/Avalonia.Controls/AppBuilderBase.cs
  97. 1
      src/Avalonia.Controls/Avalonia.Controls.csproj
  98. 4
      src/Avalonia.Controls/Button.cs
  99. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  100. 2
      src/Avalonia.Controls/Calendar/DatePicker.cs

27
.github/PULL_REQUEST_TEMPLATE.md

@ -1,16 +1,31 @@
This template is not intended to be prescriptive, but to help us review pull requests it would be useful if you included as much of the following information as possible: ## What does the pull request do?
- What does the pull request do? Give a bit of background on the PR here, together with links to with related issues etc.
- What is the current behavior?
- What is the updated/expected behavior with this PR?
- How was the solution implemented (if it's not obvious)?
Checklist: ## What is the current behavior?
If the PR is a fix, describe the current incorrect behavior, otherwise delete this section.
## What is the updated/expected behavior with this PR?
Describe how to test the PR.
## How was the solution implemented (if it's not obvious)?
Include any information that might be of use to a reviewer here.
## Checklist
- [ ] Added unit tests (if possible)? - [ ] Added unit tests (if possible)?
- [ ] Added XML documentation to any related classes? - [ ] Added XML documentation to any related classes?
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation - [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation
## Breaking changes
List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes
## Fixed issues
If the pull request fixes issue(s) list them like this: If the pull request fixes issue(s) list them like this:
Fixes #123 Fixes #123

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.Desktop.v3.ncrunchproject

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

5
.ncrunch/Avalonia.net461.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

112
Avalonia.sln

@ -1,3 +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
@ -130,6 +131,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
build\Base.props = build\Base.props build\Base.props = build\Base.props
build\Binding.props = build\Binding.props build\Binding.props = build\Binding.props
build\BuildTargets.targets = build\BuildTargets.targets
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@ -147,7 +149,6 @@ 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}"
@ -189,7 +190,15 @@ 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{41B02319-965D-4945-8005-C1A3D1224165}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}"
EndProject EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
@ -1714,6 +1723,102 @@ Global
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = 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.ActiveCfg = Release|Any CPU
{BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3F00BC43-5095-477F-93D8-E65B08179A00}.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
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.Build.0 = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.Build.0 = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.Build.0 = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1767,6 +1872,9 @@ 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}
{41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

1
Avalonia.v3.ncrunchsolution

@ -2,6 +2,7 @@
<Settings> <Settings>
<AdditionalFilesToIncludeForSolution> <AdditionalFilesToIncludeForSolution>
<Value>tests\TestFiles\**.*</Value> <Value>tests\TestFiles\**.*</Value>
<Value>src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Avalonia.Build.Tasks.dll</Value>
</AdditionalFilesToIncludeForSolution> </AdditionalFilesToIncludeForSolution>
<AllowParallelTestExecution>True</AllowParallelTestExecution> <AllowParallelTestExecution>True</AllowParallelTestExecution>
<ProjectConfigStoragePathRelativeToSolutionDir>.ncrunch</ProjectConfigStoragePathRelativeToSolutionDir> <ProjectConfigStoragePathRelativeToSolutionDir>.ncrunch</ProjectConfigStoragePathRelativeToSolutionDir>

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

44
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:
@ -39,6 +38,13 @@ jobs:
inputs: inputs:
version: '2.1.403' version: '2.1.403'
- task: CmdLine@2
displayName: 'Install Mono 5.18'
inputs:
script: |
curl -o ./mono.pkg https://download.mono-project.com/archive/5.18.0/macos-10-universal/MonoFramework-MDK-5.18.0.225.macos10.xamarin.universal.pkg
sudo installer -verbose -pkg ./mono.pkg -target /
- task: Xcode@5 - task: Xcode@5
inputs: inputs:
actions: 'build' actions: 'build'
@ -52,16 +58,18 @@ jobs:
- task: CmdLine@2 - task: CmdLine@2
displayName: 'Install CastXML' displayName: 'Install CastXML'
inputs: inputs:
script: brew install castxml script: |
brew update
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 +80,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:
@ -84,30 +92,30 @@ jobs:
inputs: inputs:
pathToPublish: '$(Build.SourcesDirectory)/Build/Products/Release/' pathToPublish: '$(Build.SourcesDirectory)/Build/Products/Release/'
artifactName: 'Avalonia.Native.OSX' artifactName: 'Avalonia.Native.OSX'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false)) condition: succeeded()
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: inputs:
pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget' pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget'
artifactName: 'NuGetOSX' artifactName: 'NuGetOSX'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false)) condition: succeeded()
- job: Windows - job: Windows
pool: pool:
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:
@ -119,10 +127,10 @@ jobs:
inputs: inputs:
pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget' pathtoPublish: '$(Build.SourcesDirectory)/artifacts/nuget'
artifactName: 'NuGet' artifactName: 'NuGet'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false)) condition: succeeded()
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
inputs: inputs:
pathToPublish: '$(Build.SourcesDirectory)/artifacts/zip' pathToPublish: '$(Build.SourcesDirectory)/artifacts/zip'
artifactName: 'Samples' artifactName: 'Samples'
condition: and(succeeded(), eq(variables['system.pullrequest.isfork'], false)) condition: succeeded()

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

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>

5
build/NetFX.props

@ -3,4 +3,9 @@
<FrameworkPathOverride>/usr/lib/mono/4.6.1-api</FrameworkPathOverride> <FrameworkPathOverride>/usr/lib/mono/4.6.1-api</FrameworkPathOverride>
<FrameworkPathOverride Condition="$([MSBuild]::IsOsPlatform('OSX'))">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api</FrameworkPathOverride> <FrameworkPathOverride Condition="$([MSBuild]::IsOsPlatform('OSX'))">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.6.1-api</FrameworkPathOverride>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net47' and '$(OS)' == 'Unix' ">
<FrameworkPathOverride>/usr/lib/mono/4.7-api/</FrameworkPathOverride>
<FrameworkPathOverride Condition="$([MSBuild]::IsOsPlatform('OSX'))">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.7-api</FrameworkPathOverride>
</PropertyGroup>
</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>

8
build/UnitTests.NetFX.props

@ -0,0 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildThisFileDirectory)/NetFX.props" />
<ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
<Content Include="$(MSBuildThisFileDirectory)/xunit.runner.mono.json" Link="xunit.runner.json" >
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

1
build/xunit.runner.mono.json

@ -0,0 +1 @@
{ "appDomain": "denied" }

15
cake.config

@ -1,15 +0,0 @@
; This is the default configuration file for Cake.
; This file was downloaded from https://github.com/cake-build/resources
[Nuget]
Source=https://api.nuget.org/v3/index.json
UseInProcessClient=true
LoadDependencies=false
[Paths]
Tools=./tools
Addins=./tools/Addins
Modules=./tools/Modules
[Settings]
SkipVerification=false

2
global.json

@ -1,7 +1,7 @@
{ {
"msbuild-sdks": { "msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43", "Microsoft.Build.Traversal": "1.0.43",
"MSBuild.Sdk.Extras": "1.6.46", "MSBuild.Sdk.Extras": "1.6.65",
"AggregatePackage.NuGet.Sdk" : "0.1.12" "AggregatePackage.NuGet.Sdk" : "0.1.12"
} }
} }

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

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

@ -425,7 +425,7 @@ private:
[[Window parentWindow] removeChildWindow:Window]; [[Window parentWindow] removeChildWindow:Window];
WindowBaseImpl::Show(); WindowBaseImpl::Show();
return SetWindowState(_lastWindowState); return SetWindowState(Normal);
} }
} }
@ -1184,6 +1184,25 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
} }
} }
- (void)windowDidMiniaturize:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}
- (void)windowDidDeminiaturize:(NSNotification *)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
{
parent->WindowStateChanged();
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
{ {

8
nukebuild/.editorconfig

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

265
nukebuild/Build.cs

@ -0,0 +1,265 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Xml.Linq;
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);
void ExecWait(string preamble, string command, string args)
{
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
}
ExecWait("dotnet version:", "dotnet", "--version");
if (Parameters.IsRunningOnUnix)
ExecWait("Mono version:", "mono", "--version");
}
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)
{
if(!project.EndsWith(".csproj"))
project = System.IO.Path.Combine(project, System.IO.Path.GetFileName(project)+".csproj");
Information("Running tests from " + project);
XDocument xdoc;
using (var s = File.OpenRead(project))
xdoc = XDocument.Load(s);
List<string> frameworks = null;
var targets = xdoc.Root.Descendants("TargetFrameworks").FirstOrDefault();
if (targets != null)
frameworks = targets.Value.Split(';').Where(f => !string.IsNullOrWhiteSpace(f)).ToList();
else
frameworks = new List<string> {xdoc.Root.Descendants("TargetFramework").First().Value};
foreach(var fw in frameworks)
{
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");
RunCoreTest("./tests/Avalonia.Base.UnitTests");
RunCoreTest("./tests/Avalonia.Controls.UnitTests");
RunCoreTest("./tests/Avalonia.Input.UnitTests");
RunCoreTest("./tests/Avalonia.Interactivity.UnitTests");
RunCoreTest("./tests/Avalonia.Layout.UnitTests");
RunCoreTest("./tests/Avalonia.Markup.UnitTests");
RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("./tests/Avalonia.Styling.UnitTests");
RunCoreTest("./tests/Avalonia.Visuals.UnitTests");
RunCoreTest("./tests/Avalonia.Skia.UnitTests");
RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests");
});
Target RunRenderTests => _ => _
.OnlyWhen(() => !Parameters.SkipTests)
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj");
});
Target RunDesignerTests => _ => _
.OnlyWhen(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
.DependsOn(Compile)
.Executes(() =>
{
RunCoreTest("./tests/Avalonia.DesignerSupport.Tests");
});
[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 Configuration { get; set; }
[Parameter("skip-tests")]
public bool SkipTests { get; set; }
[Parameter("force-nuget-version")]
public string ForceNugetVersion { 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.Configuration ?? "Release";
SkipTests = b.SkipTests;
// 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.ForceNugetVersion ?? 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
}
]
}
]
}

8
packages/Avalonia/Avalonia.csproj

@ -1,10 +1,10 @@
<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" /> <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
</ItemGroup> </ItemGroup>
@ -40,10 +40,6 @@
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\</PackagePath>
</Content> </Content>
<Content Include="../../src/Avalonia.Build.Tasks/bin/$(Configuration)/netstandard2.0/Avalonia.Build.Tasks.dll">
<Pack>true</Pack>
<PackagePath>tools\</PackagePath>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\SharedVersion.props" /> <Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" /> <Import Project="..\..\build\NetFX.props" />

2
packages/Avalonia/Avalonia.props

@ -2,7 +2,7 @@
<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\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation> <AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild> <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/> <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>

129
parameters.cake

@ -1,129 +0,0 @@
using System.Xml.Linq;
using System.Linq;
public class Parameters
{
public string Configuration { get; private set; }
public bool SkipTests { get; private set; }
public string MainRepo { get; private set; }
public string MasterBranch { get; private set; }
public string ReleasePlatform { get; private set; }
public string ReleaseConfiguration { get; private set; }
public string ReleaseBranchPrefix { get; private set; }
public string MSBuildSolution { get; private set; }
public bool IsLocalBuild { get; private set; }
public bool IsRunningOnUnix { get; private set; }
public bool IsRunningOnWindows { get; private set; }
public bool IsRunningOnAppVeyor { get; private set; }
public bool IsRunningOnAzure { get; private set; }
public bool IsPullRequest { get; private set; }
public bool IsMainRepo { get; private set; }
public bool IsMasterBranch { get; private set; }
public bool IsReleaseBranch { get; private set; }
public bool IsTagged { get; private set; }
public bool IsReleasable { get; private set; }
public bool IsMyGetRelease { get; private set; }
public bool IsNuGetRelease { get; private set; }
public bool PublishTestResults { get; private set; }
public string Version { get; private set; }
public DirectoryPath ArtifactsDir { get; private set; }
public DirectoryPath NugetRoot { get; private set; }
public DirectoryPath ZipRoot { get; private set; }
public DirectoryPath BinRoot { get; private set; }
public DirectoryPath TestResultsRoot { get; private set; }
public string DirSuffix { get; private set; }
public DirectoryPathCollection BuildDirs { get; private set; }
public string FileZipSuffix { get; private set; }
public FilePath ZipCoreArtifacts { get; private set; }
public FilePath ZipNuGetArtifacts { get; private set; }
public DirectoryPath ZipSourceControlCatalogDesktopDirs { get; private set; }
public FilePath ZipTargetControlCatalogDesktopDirs { get; private set; }
public Parameters(ICakeContext context)
{
var buildSystem = context.BuildSystem();
// ARGUMENTS
Configuration = context.Argument("configuration", "Release");
SkipTests = context.HasArgument("skip-tests");
// CONFIGURATION
MainRepo = "https://github.com/AvaloniaUI/Avalonia";
MasterBranch = "master";
ReleaseBranchPrefix = "refs/heads/release/";
ReleaseConfiguration = "Release";
MSBuildSolution = "./dirs.proj";
// PARAMETERS
IsLocalBuild = buildSystem.IsLocalBuild;
IsRunningOnUnix = context.IsRunningOnUnix();
IsRunningOnWindows = context.IsRunningOnWindows();
IsRunningOnAppVeyor = buildSystem.AppVeyor.IsRunningOnAppVeyor;
IsRunningOnAzure = buildSystem.IsRunningOnVSTS || buildSystem.IsRunningOnTFS || context.EnvironmentVariable("LOGNAME") == "vsts";
IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest;
IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, context.EnvironmentVariable("BUILD_REPOSITORY_URI"));
IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, context.EnvironmentVariable("BUILD_SOURCEBRANCHNAME"));
IsReleaseBranch = (context.EnvironmentVariable("BUILD_SOURCEBRANCH")??"").StartsWith(ReleaseBranchPrefix, StringComparison.OrdinalIgnoreCase);
IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag
&& !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name);
IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration);
IsMyGetRelease = !IsTagged && IsReleasable;
IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
// VERSION
Version = context.Argument("force-nuget-version", GetVersion());
if (IsRunningOnAppVeyor)
{
string tagVersion = null;
if (IsTagged)
{
var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name;
var nugetReleasePrefix = "nuget-release-";
IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix);
if(IsNuGetRelease)
tagVersion = tag.Substring(nugetReleasePrefix.Length);
}
if(tagVersion != null)
{
Version = tagVersion;
}
else
{
// Use AssemblyVersion with Build as version
Version += "-build" + context.EnvironmentVariable("APPVEYOR_BUILD_NUMBER") + "-beta";
}
}
else if (IsRunningOnAzure)
{
if(!IsNuGetRelease)
{
// Use AssemblyVersion with Build as version
Version += "-build" + context.EnvironmentVariable("BUILD_BUILDID") + "-beta";
}
PublishTestResults = true;
}
// DIRECTORIES
ArtifactsDir = (DirectoryPath)context.Directory("./artifacts");
NugetRoot = ArtifactsDir.Combine("nuget");
ZipRoot = ArtifactsDir.Combine("zip");
BinRoot = ArtifactsDir.Combine("bin");
TestResultsRoot = ArtifactsDir.Combine("test-results");
BuildDirs = context.GetDirectories("**/bin") + context.GetDirectories("**/obj");
DirSuffix = Configuration;
FileZipSuffix = Version + ".zip";
ZipCoreArtifacts = ZipRoot.CombineWithFilePath("Avalonia-" + FileZipSuffix);
ZipNuGetArtifacts = ZipRoot.CombineWithFilePath("Avalonia-NuGet-" + FileZipSuffix);
ZipSourceControlCatalogDesktopDirs = (DirectoryPath)context.Directory("./samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461");
ZipTargetControlCatalogDesktopDirs = ZipRoot.CombineWithFilePath("ControlCatalog.Desktop-" + FileZipSuffix);
}
private static string GetVersion()
{
var xdoc = XDocument.Load("./build/SharedVersion.props");
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
}
}

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 | NuGet | MyGet |
|---|---|---|---| |---|---|---|---|---|
| [![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) | [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) | [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) |
## About ## About

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -9,6 +9,7 @@
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
</ItemGroup> </ItemGroup>

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

13
samples/ControlCatalog/DecoratedWindow.xaml

@ -1,9 +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" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.DecoratedWindow" x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery" Title="Avalonia Control Gallery"
Icon="/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">
@ -20,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"/>

2
samples/ControlCatalog/MainView.xaml

@ -32,10 +32,10 @@
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem> <TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem> <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem> <TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
<TabItem Header="TextBox"><pages:TextBoxPage/></TabItem> <TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
<TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem> <TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem> <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
<TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
<TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem> <TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>
</TabControl> </TabControl>
</Grid> </Grid>

1
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -6,6 +6,7 @@
<Button Name="SaveFile">Save File</Button> <Button Name="SaveFile">Save File</Button>
<Button Name="SelectFolder">Select Folder</Button> <Button Name="SelectFolder">Select Folder</Button>
<Button Name="DecoratedWindow">Decorated window</Button> <Button Name="DecoratedWindow">Decorated window</Button>
<Button Name="DecoratedWindowDialog">Decorated window (dialog)</Button>
<Button Name="Dialog">Dialog</Button> <Button Name="Dialog">Dialog</Button>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

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

@ -31,15 +31,22 @@ namespace ControlCatalog.Pages
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
}; };
this.FindControl<Button>("DecoratedWindow").Click += delegate this.FindControl<Button>("DecoratedWindow").Click += delegate
{ {
new DecoratedWindow().ShowDialog(GetWindow()); new DecoratedWindow().Show();
}; };
this.FindControl<Button>("DecoratedWindowDialog").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
};
this.FindControl<Button>("Dialog").Click += delegate this.FindControl<Button>("Dialog").Click += delegate
{ {
new MainWindow().ShowDialog(GetWindow()); var window = new Window();
window.Height = 200;
window.Width = 200;
window.Content = new TextBlock { Text = "Hello world!" };
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
window.ShowDialog(GetWindow());
}; };
} }
Window GetWindow() => (Window)this.VisualRoot; Window GetWindow() => (Window)this.VisualRoot;

9
samples/ControlCatalog/Pages/DropDownPage.xaml

@ -27,6 +27,15 @@
<TextBox Text="TextBox"/> <TextBox Text="TextBox"/>
</DropDownItem> </DropDownItem>
</DropDown> </DropDown>
<DropDown x:Name="fontDropDown" SelectedIndex="0">
<DropDown.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</DropDown.ItemTemplate>
</DropDown>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

3
samples/ControlCatalog/Pages/DropDownPage.xaml.cs

@ -13,6 +13,9 @@ namespace ControlCatalog.Pages
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
var fontDropDown = this.Find<DropDown>("fontDropDown");
fontDropDown.Items = Avalonia.Media.FontFamily.SystemFontFamilies;
fontDropDown.SelectedIndex = 0;
} }
} }
} }

4
samples/ControlCatalog/Pages/ScreenPage.cs

@ -4,6 +4,7 @@ using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -23,6 +24,7 @@ namespace ControlCatalog.Pages
base.Render(context); base.Render(context);
Window w = (Window)VisualRoot; Window w = (Window)VisualRoot;
Screen[] screens = w.Screens.All; Screen[] screens = w.Screens.All;
var scaling = ((IRenderRoot)w).RenderScaling;
Pen p = new Pen(Brushes.Black); Pen p = new Pen(Brushes.Black);
if (screens != null) if (screens != null)
@ -56,7 +58,7 @@ namespace ControlCatalog.Pages
text.Text = $"Primary: {screen.Primary}"; text.Text = $"Primary: {screen.Primary}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text); context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new Rect(w.Position, w.Bounds.Size)))}"; text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}";
context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text); context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
} }

4
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -46,8 +46,8 @@
<TextBlock Classes="h2">res fonts</TextBlock> <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 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 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="avares://ControlCatalog/Assets/Fonts/SourceSansPro-Italic.ttf#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="avares://ControlCatalog/Assets/Fonts/SourceSansPro-*.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> </StackPanel>

2
samples/ControlCatalog/SideBar.xaml

@ -26,7 +26,7 @@
</ItemsPresenter> </ItemsPresenter>
</ScrollViewer> </ScrollViewer>
<ContentPresenter <ContentPresenter
Name="PART_Content" Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}" Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"

6
samples/PlatformSanityChecks/App.xaml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

13
samples/PlatformSanityChecks/App.xaml.cs

@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Markup.Xaml;
namespace PlatformSanityChecks
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
}
}

13
samples/PlatformSanityChecks/PlatformSanityChecks.csproj

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
</ItemGroup>
</Project>

132
samples/PlatformSanityChecks/Program.cs

@ -0,0 +1,132 @@
using System;
using System.Diagnostics;
using System.Reactive.Disposables;
using System.Runtime.CompilerServices;
using System.Threading;
using Avalonia;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.X11;
namespace PlatformSanityChecks
{
public class Program
{
static Thread UiThread;
static void Main(string[] args)
{
UiThread = Thread.CurrentThread;
AppBuilder.Configure<App>().RuntimePlatformServicesInitializer();
var app = new App();
AvaloniaX11PlatformExtensions.InitializeX11Platform();
CheckPlatformThreading();
}
static bool CheckAccess() => UiThread == Thread.CurrentThread;
static void VerifyAccess()
{
if (!CheckAccess())
Die("Call from invalid thread");
}
static Exception Die(string error)
{
Console.Error.WriteLine(error);
Console.Error.WriteLine(Environment.StackTrace);
Process.GetCurrentProcess().Kill();
throw new Exception(error);
}
static IDisposable Enter([CallerMemberName] string caller = null)
{
Console.WriteLine("Entering " + caller);
return Disposable.Create(() => { Console.WriteLine("Leaving " + caller); });
}
static void EnterLoop(Action<CancellationTokenSource> cb, [CallerMemberName] string caller = null)
{
using (Enter(caller))
{
var cts = new CancellationTokenSource();
cb(cts);
Dispatcher.UIThread.MainLoop(cts.Token);
if (!cts.IsCancellationRequested)
Die("Unexpected loop exit");
}
}
static void CheckTimerOrdering() => EnterLoop(cts =>
{
bool firstFired = false, secondFired = false;
DispatcherTimer.Run(() =>
{
Console.WriteLine("Second tick");
VerifyAccess();
if (!firstFired)
throw Die("Invalid timer ordering");
if (secondFired)
throw Die("Invocation of finished timer");
secondFired = true;
cts.Cancel();
return false;
}, TimeSpan.FromSeconds(2));
DispatcherTimer.Run(() =>
{
Console.WriteLine("First tick");
VerifyAccess();
if (secondFired)
throw Die("Invalid timer ordering");
if (firstFired)
throw Die("Invocation of finished timer");
firstFired = true;
return false;
}, TimeSpan.FromSeconds(1));
});
static void CheckTimerTicking() => EnterLoop(cts =>
{
int ticks = 0;
var st = Stopwatch.StartNew();
DispatcherTimer.Run(() =>
{
ticks++;
Console.WriteLine($"Tick {ticks} at {st.Elapsed}");
if (ticks == 5)
{
if (st.Elapsed.TotalSeconds < 4.5)
Die("Timer is too fast");
if (st.Elapsed.TotalSeconds > 6)
Die("Timer is too slow");
cts.Cancel();
return false;
}
return true;
}, TimeSpan.FromSeconds(1));
});
static void CheckSignaling() => EnterLoop(cts =>
{
ThreadPool.QueueUserWorkItem(_ =>
{
Thread.Sleep(100);
Dispatcher.UIThread.Post(() =>
{
VerifyAccess();
cts.Cancel();
});
});
});
static void CheckPlatformThreading()
{
CheckSignaling();
CheckTimerOrdering();
CheckTimerTicking();
}
}
}

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>

6
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -10,10 +10,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
class PopupImpl : TopLevelImpl, IPopupImpl class PopupImpl : TopLevelImpl, IPopupImpl
{ {
private Point _position; private PixelPoint _position;
private bool _isAdded; private bool _isAdded;
Action IWindowBaseImpl.Activated { get; set; } Action IWindowBaseImpl.Activated { get; set; }
public Action<Point> PositionChanged { get; set; } public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public PopupImpl() : base(ActivityTracker.Current, true) public PopupImpl() : base(ActivityTracker.Current, true)
@ -36,7 +36,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public IScreenImpl Screen { get; } public IScreenImpl Screen { get; }
public Point Position public PixelPoint Position
{ {
get { return _position; } get { return _position; }
set set

8
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -99,14 +99,14 @@ namespace Avalonia.Android.Platform.SkiaPlatform
if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate(); if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
} }
public Point PointToClient(Point point) public Point PointToClient(PixelPoint point)
{ {
return point; return point.ToPoint(1);
} }
public Point PointToScreen(Point point) public PixelPoint PointToScreen(Point point)
{ {
return point; return PixelPoint.FromPoint(point, 1);
} }
public void SetCursor(IPlatformHandle cursor) public void SetCursor(IPlatformHandle cursor)

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;

207
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,78 @@ 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 bool _playbackReversed;
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 +105,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 +131,7 @@ namespace Avalonia.Animation
if (!_gotFirstKFValue) if (!_gotFirstKFValue)
{ {
_firstKFValue = (T)_parent.First().Value; _firstKFValue = (T)_animator.First().Value;
_gotFirstKFValue = true; _gotFirstKFValue = true;
} }
} }
@ -138,75 +139,81 @@ 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 if (indexTime > 0 & indexTime <= initDelay)
_currentIteration = Math.Min(_repeatCount,(int)Math.Floor((double)((double)iterationTime.Ticks / iterationEndpoint.Ticks)) + 2); {
DoDelay();
} }
else else
{ {
return; // Calculate timebases.
} var iterationTime = iterDuration + iterDelay;
var opsTime = indexTime - initDelay;
var playbackTime = opsTime % iterationTime;
// Determine if the current iteration should have its normalized time inverted. _currentIteration = (ulong)(opsTime / iterationTime);
bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false :
_animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true : // Stop animation when the current iteration is beyond the iteration count
_animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false : // and snap the last iteration value to exact values.
_animationDirection == PlaybackDirection.Reverse ? true : false; if ((_currentIteration + 1) > _iterationCount)
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); var easedTime = _easeFunc.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue); _lastInterpValue = _interpolator(easedTime, _neutralValue);
DoComplete(); DoComplete();
return;
} }
}
iterationTime = TimeSpan.FromTicks((long)(iterationTime.Ticks % iterationEndpoint.Ticks));
if (delayEndpoint > TimeSpan.Zero & iterationTime < delayEndpoint)
{
DoDelay();
}
else
{
// Offset the delay time
iterationTime -= delayEndpoint;
iterationEndpoint -= delayEndpoint;
// Normalize time
var interpVal = (double)iterationTime.Ticks / iterationEndpoint.Ticks;
if (isCurIterReverse) if (playbackTime <= iterDuration)
interpVal = 1 - interpVal; {
// Normalize time for interpolation.
// Ease and interpolate var normalizedTime = playbackTime / iterDuration;
var easedTime = _easeFunc.Ease(interpVal);
_lastInterpValue = _interpolator(easedTime, _neutralValue); // Check if normalized time needs to be reversed according to PlaybackDirection
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();
}
} }
} }
} }

1
src/Avalonia.Animation/AnimatorKeyFrame.cs

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using Avalonia.Animation.Animators;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;

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", nameof(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);
}
}
}
}

7
src/Avalonia.Animation/DoubleTransition.cs → src/Avalonia.Animation/Transitions/DoubleTransition.cs

@ -14,9 +14,12 @@ namespace Avalonia.Animation
/// <inheritdocs/> /// <inheritdocs/>
public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue) public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue)
{ {
var delta = newValue - oldValue;
return progress return progress
.Select(p => Easing.Ease(p) * delta + oldValue); .Select(p =>
{
var f = Easing.Ease(p);
return ((newValue - oldValue) * f) + oldValue;
});
} }
} }
} }

0
src/Avalonia.Animation/FloatTransition.cs → src/Avalonia.Animation/Transitions/FloatTransition.cs

0
src/Avalonia.Animation/IntegerTransition.cs → src/Avalonia.Animation/Transitions/IntegerTransition.cs

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -4,7 +4,6 @@
<AssemblyName>Avalonia.Base</AssemblyName> <AssemblyName>Avalonia.Base</AssemblyName>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/> <ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/>

4
src/Avalonia.Base/Collections/AvaloniaDictionary.cs

@ -148,7 +148,7 @@ namespace Avalonia.Collections
{ {
if (_inner.TryGetValue(key, out TValue value)) if (_inner.TryGetValue(key, out TValue value))
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
if (CollectionChanged != null) if (CollectionChanged != null)
@ -209,7 +209,7 @@ namespace Avalonia.Collections
private void NotifyAdd(TKey key, TValue value) private void NotifyAdd(TKey key, TValue value)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));

2
src/Avalonia.Base/Collections/AvaloniaList.cs

@ -511,7 +511,7 @@ namespace Avalonia.Collections
/// </summary> /// </summary>
private void NotifyCountChanged() private void NotifyCountChanged()
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
} }
/// <summary> /// <summary>

24
src/Avalonia.Base/Data/Converters/ObjectConverters.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.
namespace Avalonia.Data.Converters
{
/// <summary>
/// Provides a set of useful <see cref="IValueConverter"/>s for working with objects.
/// </summary>
public static class ObjectConverters
{
/// <summary>
/// A value converter that returns true if the input object is a null reference.
/// </summary>
public static readonly IValueConverter IsNull =
new FuncValueConverter<object, bool>(x => x is null);
/// <summary>
/// A value converter that returns true if the input object is not null.
/// </summary>
public static readonly IValueConverter IsNotNull =
new FuncValueConverter<object, bool>(x => !(x is null));
}
}

4
src/Avalonia.Base/Data/Converters/StringConverters.cs

@ -12,13 +12,13 @@ namespace Avalonia.Data.Converters
/// <summary> /// <summary>
/// A value converter that returns true if the input string is null or an empty string. /// A value converter that returns true if the input string is null or an empty string.
/// </summary> /// </summary>
public static readonly IValueConverter NullOrEmpty = public static readonly IValueConverter IsNullOrEmpty =
new FuncValueConverter<string, bool>(string.IsNullOrEmpty); new FuncValueConverter<string, bool>(string.IsNullOrEmpty);
/// <summary> /// <summary>
/// A value converter that returns true if the input string is not null or empty. /// A value converter that returns true if the input string is not null or empty.
/// </summary> /// </summary>
public static readonly IValueConverter NotNullOrEmpty = public static readonly IValueConverter IsNotNullOrEmpty =
new FuncValueConverter<string, bool>(x => !string.IsNullOrEmpty(x)); new FuncValueConverter<string, bool>(x => !string.IsNullOrEmpty(x));
} }
} }

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

@ -147,6 +147,7 @@ namespace Avalonia.Data.Core
private void StopListening() private void StopListening()
{ {
StopListeningCore(); StopListeningCore();
_listening = false;
} }
private BindingNotification TargetNullNotification() private BindingNotification TargetNullNotification()

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

@ -65,6 +65,7 @@ namespace Avalonia.Platform
/// Gets all assets of a folder and subfolders that match specified uri. /// Gets all assets of a folder and subfolders that match specified uri.
/// </summary> /// </summary>
/// <param name="uri">The URI.</param> /// <param name="uri">The URI.</param>
/// <param name="baseUri">The base URI.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns> /// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri); IEnumerable<Uri> GetAssets(Uri uri, Uri baseUri);
} }

29
src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Avalonia.Threading
{
public class ThreadSafeObjectPool<T> where T : class, new()
{
private Stack<T> _stack = new Stack<T>();
private object _lock = new object();
public static ThreadSafeObjectPool<T> Default { get; } = new ThreadSafeObjectPool<T>();
public T Get()
{
lock (_lock)
{
if(_stack.Count == 0)
return new T();
return _stack.Pop();
}
}
public void Return(T obj)
{
lock (_stack)
{
_stack.Push(obj);
}
}
}
}

1
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -4,7 +4,6 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants> <DefineConstants>$(DefineConstants);BUILDTASK</DefineConstants>
<Packable>false</Packable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

9
src/Avalonia.Controls/AppBuilderBase.cs

@ -145,6 +145,15 @@ namespace Avalonia.Controls
Instance.Run(mainWindow); Instance.Run(mainWindow);
} }
public delegate void AppMainDelegate(Application app, string[] args);
public void Start(AppMainDelegate main, string[] args)
{
Setup();
BeforeStartCallback(Self);
main(Instance, args);
}
/// <summary> /// <summary>
/// Sets up the platform-specific services for the application, but does not run it. /// Sets up the platform-specific services for the application, but does not run it.
/// </summary> /// </summary>

1
src/Avalonia.Controls/Avalonia.Controls.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.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />

4
src/Avalonia.Controls/Button.cs

@ -253,10 +253,8 @@ namespace Avalonia.Controls
IsPressed = false; IsPressed = false;
e.Handled = true; e.Handled = true;
var hittest = this.GetVisualsAt(e.GetPosition(this));
if (ClickMode == ClickMode.Release && if (ClickMode == ClickMode.Release &&
hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this)) this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c)))
{ {
OnClick(); OnClick();
} }

2
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -52,7 +52,7 @@ namespace Avalonia.Controls.Primitives
internal Calendar Owner { get; set; } internal Calendar Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; } internal CalendarDayButton CurrentButton { get; set; }
public static StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>(); public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public IBrush HeaderBackground public IBrush HeaderBackground
{ {
get { return GetValue(HeaderBackgroundProperty); } get { return GetValue(HeaderBackgroundProperty); }

2
src/Avalonia.Controls/Calendar/DatePicker.cs

@ -954,7 +954,7 @@ namespace Avalonia.Controls
} }
else else
{ {
var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", "SelectedDate value is not valid."), text); var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
OnDateValidationError(dateValidationError); OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException) if (dateValidationError.ThrowException)

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

Loading…
Cancel
Save