Browse Source

Merge branch 'master' into feature/relative-panel

pull/3987/head
Jumar Macato 6 years ago
committed by GitHub
parent
commit
879296d24f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/NativeEmbedSample.v3.ncrunchproject
  2. 58
      Avalonia.sln
  3. 32
      NOTICE.md
  4. 1
      build/CoreLibraries.props
  5. 2
      build/Moq.props
  6. 7
      build/Serilog.props
  7. 4
      build/readme.md
  8. 24
      native/Avalonia.Native/inc/avalonia-native.h
  9. 8
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  10. 36
      native/Avalonia.Native/src/OSX/app.mm
  11. 9
      native/Avalonia.Native/src/OSX/common.h
  12. 160
      native/Avalonia.Native/src/OSX/controlhost.mm
  13. 17
      native/Avalonia.Native/src/OSX/deadlock.mm
  14. 98
      native/Avalonia.Native/src/OSX/window.mm
  15. 4
      nukebuild/Build.cs
  16. 2
      nukebuild/Numerge
  17. 4
      packages/Avalonia/Avalonia.csproj
  18. 10
      packages/Avalonia/AvaloniaBuildTasks.props
  19. 22
      packages/Avalonia/AvaloniaBuildTasks.targets
  20. 18
      packages/Avalonia/AvaloniaItemSchema.xaml
  21. 37
      readme.md
  22. 2
      samples/BindingDemo/App.xaml.cs
  23. 1
      samples/BindingDemo/BindingDemo.csproj
  24. 1
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  25. 2
      samples/ControlCatalog.Desktop/Program.cs
  26. 7
      samples/ControlCatalog.NetCore/Program.cs
  27. 1
      samples/ControlCatalog/ControlCatalog.csproj
  28. 9
      samples/ControlCatalog/MainView.xaml
  29. 6
      samples/ControlCatalog/Models/Person.cs
  30. 7
      samples/ControlCatalog/Pages/BorderPage.xaml
  31. 4
      samples/ControlCatalog/Pages/ButtonPage.xaml
  32. 18
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
  33. 14
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  34. 123
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml
  35. 30
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  36. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml
  37. 15
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  38. 7
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  39. 34
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  40. 12
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  41. 26
      samples/ControlCatalog/Pages/ProgressBarPage.xaml
  42. 35
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml
  43. 65
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs
  44. 21
      samples/ControlCatalog/Pages/SliderPage.xaml
  45. 97
      samples/ControlCatalog/Pages/SplitViewPage.xaml
  46. 21
      samples/ControlCatalog/Pages/SplitViewPage.xaml.cs
  47. 79
      samples/ControlCatalog/Pages/ToggleSwitchPage.xaml
  48. 19
      samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs
  49. 2
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  50. 3
      samples/ControlCatalog/SideBar.xaml
  51. 13
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  52. 46
      samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs
  53. 2
      samples/RenderDemo/App.xaml.cs
  54. 3
      samples/RenderDemo/MainWindow.xaml
  55. 115
      samples/RenderDemo/Pages/TransitionsPage.xaml
  56. 37
      samples/RenderDemo/Pages/TransitionsPage.xaml.cs
  57. 1
      samples/RenderDemo/RenderDemo.csproj
  58. 2
      samples/VirtualizationDemo/Program.cs
  59. 1
      samples/VirtualizationDemo/VirtualizationDemo.csproj
  60. 1
      samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj
  61. 8
      samples/interop/NativeEmbedSample/App.xaml
  62. 22
      samples/interop/NativeEmbedSample/App.xaml.cs
  63. 121
      samples/interop/NativeEmbedSample/EmbedSample.cs
  64. 58
      samples/interop/NativeEmbedSample/GtkHelper.cs
  65. 39
      samples/interop/NativeEmbedSample/MacHelper.cs
  66. 52
      samples/interop/NativeEmbedSample/MainWindow.xaml
  67. 36
      samples/interop/NativeEmbedSample/MainWindow.xaml.cs
  68. 30
      samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
  69. 17
      samples/interop/NativeEmbedSample/Program.cs
  70. 74
      samples/interop/NativeEmbedSample/WinApi.cs
  71. 1
      samples/interop/NativeEmbedSample/nodes-license.md
  72. BIN
      samples/interop/NativeEmbedSample/nodes.mp4
  73. 2
      src/Android/Avalonia.Android/AndroidPlatform.cs
  74. 4
      src/Android/Avalonia.Android/AvaloniaView.cs
  75. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  76. 1
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  77. 6
      src/Avalonia.Animation/Easing/Easing.cs
  78. 85
      src/Avalonia.Animation/Easing/SplineEasing.cs
  79. 35
      src/Avalonia.Animation/KeySpline.cs
  80. 25
      src/Avalonia.Animation/KeySplineTypeConverter.cs
  81. 36
      src/Avalonia.Animation/TransitionInstance.cs
  82. 9
      src/Avalonia.Animation/Transition`1.cs
  83. 12
      src/Avalonia.Base/AvaloniaObject.cs
  84. 4
      src/Avalonia.Base/AvaloniaProperty.cs
  85. 4
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  86. 1
      src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
  87. 2
      src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
  88. 3
      src/Avalonia.Base/Collections/Pooled/PooledList.cs
  89. 34
      src/Avalonia.Base/Data/BindingValue.cs
  90. 3
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  91. 36
      src/Avalonia.Base/Data/Core/LogicalNotNode.cs
  92. 15
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  93. 18
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  94. 16
      src/Avalonia.Base/Data/Optional.cs
  95. 2
      src/Avalonia.Base/DirectPropertyBase.cs
  96. 2
      src/Avalonia.Base/IStyledPropertyMetadata.cs
  97. 179
      src/Avalonia.Base/Logging/DebugLogSink.cs
  98. 5
      src/Avalonia.Base/Logging/ILogSink.cs
  99. 17
      src/Avalonia.Base/Logging/Logger.cs
  100. 32
      src/Avalonia.Base/Logging/ParametrizedLogger.cs

5
.ncrunch/NativeEmbedSample.v3.ncrunchproject

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

58
Avalonia.sln

@ -95,8 +95,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.UnitTests", "tests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Benchmarks", "tests\Avalonia.Benchmarks\Avalonia.Benchmarks.csproj", "{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Logging.Serilog", "src\Avalonia.Logging.Serilog\Avalonia.Logging.Serilog.csproj", "{B61B66A3-B82D-4875-8001-89D3394FE0C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport", "src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{D0A739B9-3C68-4BA6-A328-41606954B6BD}"
@ -140,7 +138,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props
build\Serilog.props = build\Serilog.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
build\System.Memory.props = build\System.Memory.props
@ -206,14 +203,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 5
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
@ -1027,30 +1026,6 @@ Global
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|Any CPU.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhone.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhone.Build.0 = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|Any CPU.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhone.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhone.Build.0 = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{B61B66A3-B82D-4875-8001-89D3394FE0C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -1923,6 +1898,30 @@ Global
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.Build.0 = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.ActiveCfg = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.Build.0 = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@ -2004,6 +2003,7 @@ Global
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

32
NOTICE.md

@ -271,3 +271,35 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
# Chromium
https://github.com/chromium/chromium
// Copyright 2015 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1
build/CoreLibraries.props

@ -7,7 +7,6 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Input/Avalonia.Input.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Interactivity/Avalonia.Interactivity.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Layout/Avalonia.Layout.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Visuals/Avalonia.Visuals.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Styling/Avalonia.Styling.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj" />

2
build/Moq.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Moq" Version="4.7.99" />
<PackageReference Include="Moq" Version="4.14.1" />
</ItemGroup>
</Project>

7
build/Serilog.props

@ -1,7 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Serilog" Version="2.5.0" />
<PackageReference Include="Serilog.Sinks.Trace" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="1.0.0" />
</ItemGroup>
</Project>

4
build/readme.md

@ -9,8 +9,6 @@
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\NetCore.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Serilog.Sinks.Trace.props" />
<Import Project="..\..\build\SharpDX.props" />
<Import Project="..\..\build\SkiaSharp.Desktop.props" />
<Import Project="..\..\build\SkiaSharp.props" />
@ -22,4 +20,4 @@
```XML
<Import Project="..\..\build\UnitTests.NetCore.targets" />
```
```

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

@ -26,7 +26,8 @@ struct IAvnStringArray;
struct IAvnDndResultCallback;
struct IAvnGCHandleDeallocatorCallback;
struct IAvnMenuEvents;
struct IAvnNativeControlHost;
struct IAvnNativeControlHostTopLevelAttachment;
enum SystemDecorations {
SystemDecorationsNone = 0,
SystemDecorationsBorderOnly = 1,
@ -256,6 +257,7 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandle(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) = 0;
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0;
virtual HRESULT SetBlurEnabled (bool enable) = 0;
@ -276,6 +278,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
virtual HRESULT TakeFocusFromChildren() = 0;
};
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
@ -295,6 +298,7 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
virtual void ScalingChanged(double scaling) = 0;
virtual void RunRenderPriorityJobs() = 0;
virtual void LostFocus() = 0;
virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers, AvnDragDropEffects effects,
IAvnClipboard* clipboard, void* dataObjectHandle) = 0;
@ -478,4 +482,22 @@ AVNCOM(IAvnGCHandleDeallocatorCallback, 22) : IUnknown
virtual void FreeGCHandle(void* handle) = 0;
};
AVNCOM(IAvnNativeControlHost, 20) : IUnknown
{
virtual HRESULT CreateDefaultChild(void* parent, void** retOut) = 0;
virtual IAvnNativeControlHostTopLevelAttachment* CreateAttachment() = 0;
virtual void DestroyDefaultChild(void* child) = 0;
};
AVNCOM(IAvnNativeControlHostTopLevelAttachment, 21) : IUnknown
{
virtual void* GetParentHandle() = 0;
virtual HRESULT InitializeWithChildHandle(void* child) = 0;
virtual HRESULT AttachTo(IAvnNativeControlHost* host) = 0;
virtual void ShowInBounds(float x, float y, float width, float height) = 0;
virtual void HideWithSize(float width, float height) = 0;
virtual void ReleaseChild() = 0;
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();

8
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -10,6 +10,8 @@
1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; };
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
@ -32,6 +34,8 @@
1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; };
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = "<group>"; };
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
@ -86,11 +90,13 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */,
37DDA9AF219330F8002E132B /* AvnString.mm */,
37A4E71A2178846A00EACBCD /* headers */,
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */,
1AFD334023E03C4F0042899B /* controlhost.mm */,
5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
@ -191,6 +197,7 @@
files = (
1A002B9E232135EE00021753 /* app.mm in Sources */,
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */,
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */,
5B21A982216530F500CEE36E /* cursor.mm in Sources */,
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
@ -199,6 +206,7 @@
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,
1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,

36
native/Avalonia.Native/src/OSX/app.mm

@ -29,9 +29,43 @@ NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivati
@end
@interface AvnApplication : NSApplication
@end
@implementation AvnApplication
{
BOOL _isHandlingSendEvent;
}
- (void)sendEvent:(NSEvent *)event
{
bool oldHandling = _isHandlingSendEvent;
_isHandlingSendEvent = true;
@try {
[super sendEvent: event];
} @finally {
_isHandlingSendEvent = oldHandling;
}
}
// This is needed for certain embedded controls
- (BOOL) isHandlingSendEvent
{
return _isHandlingSendEvent;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
{
_isHandlingSendEvent = handlingSendEvent;
}
@end
extern void InitializeAvnApp()
{
NSApplication* app = [NSApplication sharedApplication];
NSApplication* app = [AvnApplication sharedApplication];
id delegate = [AvnAppDelegate new];
[app setDelegate:delegate];
}

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

@ -24,6 +24,7 @@ extern IAvnGlDisplay* GetGlDisplay();
extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events);
extern IAvnMenuItem* CreateAppMenuItem();
extern IAvnMenuItem* CreateAppMenuItemSeperator();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
@ -54,4 +55,12 @@ template<typename T> inline T* objc_cast(id from) {
- (void) action;
@end
class AvnInsidePotentialDeadlock
{
public:
static bool IsInside();
AvnInsidePotentialDeadlock();
~AvnInsidePotentialDeadlock();
};
#endif

160
native/Avalonia.Native/src/OSX/controlhost.mm

@ -0,0 +1,160 @@
#include "common.h"
IAvnNativeControlHostTopLevelAttachment* CreateAttachment();
class AvnNativeControlHost :
public ComSingleObject<IAvnNativeControlHost, &IID_IAvnNativeControlHost>
{
public:
FORWARD_IUNKNOWN();
NSView* View;
AvnNativeControlHost(NSView* view)
{
View = view;
}
virtual HRESULT CreateDefaultChild(void* parent, void** retOut) override
{
NSView* view = [NSView new];
[view setWantsLayer: true];
*retOut = (__bridge_retained void*)view;
return S_OK;
};
virtual IAvnNativeControlHostTopLevelAttachment* CreateAttachment() override
{
return ::CreateAttachment();
};
virtual void DestroyDefaultChild(void* child) override
{
// ARC will release the object for us
(__bridge_transfer NSView*) child;
}
};
class AvnNativeControlHostTopLevelAttachment :
public ComSingleObject<IAvnNativeControlHostTopLevelAttachment, &IID_IAvnNativeControlHostTopLevelAttachment>
{
NSView* _holder;
NSView* _child;
public:
FORWARD_IUNKNOWN();
AvnNativeControlHostTopLevelAttachment()
{
_holder = [NSView new];
[_holder setWantsLayer:true];
}
virtual ~AvnNativeControlHostTopLevelAttachment()
{
if(_child != nil && [_child superview] == _holder)
{
[_child removeFromSuperview];
}
if([_holder superview] != nil)
{
[_holder removeFromSuperview];
}
}
virtual void* GetParentHandle() override
{
return (__bridge void*)_holder;
};
virtual HRESULT InitializeWithChildHandle(void* child) override
{
if(_child != nil)
return E_FAIL;
_child = (__bridge NSView*)child;
if(_child == nil)
return E_FAIL;
[_holder addSubview:_child];
[_child setHidden: false];
return S_OK;
};
virtual HRESULT AttachTo(IAvnNativeControlHost* host) override
{
if(host == nil)
{
[_holder removeFromSuperview];
[_holder setHidden: true];
}
else
{
AvnNativeControlHost* chost = dynamic_cast<AvnNativeControlHost*>(host);
if(chost == nil || chost->View == nil)
return E_FAIL;
[_holder setHidden:true];
[chost->View addSubview:_holder];
}
return S_OK;
};
virtual void ShowInBounds(float x, float y, float width, float height) override
{
if(_child == nil)
return;
if(AvnInsidePotentialDeadlock::IsInside())
{
IAvnNativeControlHostTopLevelAttachment* slf = this;
slf->AddRef();
dispatch_async(dispatch_get_main_queue(), ^{
slf->ShowInBounds(x, y, width, height);
slf->Release();
});
return;
}
NSRect childFrame = {0, 0, width, height};
NSRect holderFrame = {x, y, width, height};
[_child setFrame: childFrame];
[_holder setFrame: holderFrame];
[_holder setHidden: false];
if([_holder superview] != nil)
[[_holder superview] setNeedsDisplay:true];
}
virtual void HideWithSize(float width, float height) override
{
if(_child == nil)
return;
if(AvnInsidePotentialDeadlock::IsInside())
{
IAvnNativeControlHostTopLevelAttachment* slf = this;
slf->AddRef();
dispatch_async(dispatch_get_main_queue(), ^{
slf->HideWithSize(width, height);
slf->Release();
});
return;
}
NSRect frame = {0, 0, width, height};
[_holder setHidden: true];
[_child setFrame: frame];
}
virtual void ReleaseChild() override
{
[_child removeFromSuperview];
_child = nil;
}
};
IAvnNativeControlHostTopLevelAttachment* CreateAttachment()
{
return new AvnNativeControlHostTopLevelAttachment();
}
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent)
{
return new AvnNativeControlHost(parent);
}

17
native/Avalonia.Native/src/OSX/deadlock.mm

@ -0,0 +1,17 @@
#include "common.h"
static int Counter = 0;
AvnInsidePotentialDeadlock::AvnInsidePotentialDeadlock()
{
Counter++;
}
AvnInsidePotentialDeadlock::~AvnInsidePotentialDeadlock()
{
Counter--;
}
bool AvnInsidePotentialDeadlock::IsInside()
{
return Counter!=0;
}

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

@ -116,10 +116,15 @@ public:
{
SetPosition(lastPositionSet);
UpdateStyle();
[Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
if(ShouldTakeFocusOnShow())
{
[Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
}
else
{
[Window orderFront: Window];
}
[Window setTitle:_lastTitle];
_shown = true;
@ -128,6 +133,11 @@ public:
}
}
virtual bool ShouldTakeFocusOnShow()
{
return true;
}
virtual HRESULT Hide () override
{
@autoreleasepool
@ -390,6 +400,14 @@ public:
return *ppv == nil ? E_FAIL : S_OK;
}
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override
{
if(View == NULL)
return E_FAIL;
*retOut = ::CreateNativeControlHost(View);
return S_OK;
}
virtual HRESULT SetBlurEnabled (bool enable) override
{
[Window setContentView: enable ? VisualEffect : View];
@ -766,6 +784,15 @@ private:
}
}
virtual HRESULT TakeFocusFromChildren () override
{
if(Window == nil)
return S_OK;
if([Window isKeyWindow])
[Window makeFirstResponder: View];
return S_OK;
}
void EnterFullScreenMode ()
{
_fullScreenActive = true;
@ -1060,9 +1087,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
}
- (void)updateLayer
{
AvnInsidePotentialDeadlock deadlock;
if (_parent == nullptr)
{
return;
@ -1107,7 +1134,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget];
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
if(_parent != nullptr)
{
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
}
[super viewDidChangeBackingProperties];
}
@ -1138,7 +1169,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return;
}
[self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
auto avnPoint = [self toAvnPoint:localPoint];
auto point = [self translateLocalPoint:avnPoint];
@ -1165,11 +1195,31 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]];
[self becomeFirstResponder];
_parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
if(type != AvnRawMouseEventType::Move ||
(
[self window] != nil &&
(
[[self window] firstResponder] == nil
|| ![[[self window] firstResponder] isKindOfClass: [NSView class]]
)
)
)
[self becomeFirstResponder];
if(_parent != nullptr)
{
_parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
}
[super mouseMoved:event];
}
- (BOOL) resignFirstResponder
{
_parent->BaseEvents->LostFocus();
return YES;
}
- (void)mouseMoved:(NSEvent *)event
{
[self mouseEvent:event withType:Move];
@ -1290,7 +1340,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]];
_lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
if(_parent != nullptr)
{
_lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key);
}
}
- (BOOL)performKeyEquivalent:(NSEvent *)event
@ -1381,7 +1434,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
if(!_lastKeyHandled)
{
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
if(_parent != nullptr)
{
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
}
}
}
@ -1677,7 +1733,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
[self showWindowMenuWithAppMenu];
_parent->BaseEvents->Activated();
if(_parent != nullptr)
{
_parent->BaseEvents->Activated();
}
[super becomeKeyWindow];
}
}
@ -1794,8 +1854,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)windowDidMove:(NSNotification *)notification
{
AvnPoint position;
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position);
if(_parent != nullptr)
{
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position);
}
}
@end
@ -1813,7 +1877,6 @@ private:
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
}
protected:
virtual NSWindowStyleMask GetStyle() override
{
@ -1831,6 +1894,11 @@ protected:
return S_OK;
}
}
public:
virtual bool ShouldTakeFocusOnShow() override
{
return false;
}
};
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)

4
nukebuild/Build.cs

@ -101,7 +101,7 @@ partial class Build : NukeBuild
.SetProjectFile(projectFile)
// This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, c => c
Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true)
@ -176,7 +176,7 @@ partial class Build : NukeBuild
.SetFramework(fw)
.EnableNoBuild()
.EnableNoRestore()
.When(Parameters.PublishTestResults, c => c
.When(Parameters.PublishTestResults, _ => _
.SetLogger("trx")
.SetResultsDirectory(Parameters.TestResultsRoot)));
}

2
nukebuild/Numerge

@ -1 +1 @@
Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8
Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5

4
packages/Avalonia/Avalonia.csproj

@ -41,6 +41,10 @@
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
<Content Include="AvaloniaItemSchema.xaml">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
<Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.props" />

10
packages/Avalonia/AvaloniaBuildTasks.props

@ -1,3 +1,11 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="AvaloniaXaml" />
<AvailableItemName Include="AvaloniaResource" />
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)AvaloniaItemSchema.xaml" />
</ItemGroup>
<ItemGroup Condition="'$(EnableDefaultItems)'=='True'">
<AvaloniaXaml Include="**\*.axaml" SubType="Designer" />
<AvaloniaXaml Include="**\*.paml" SubType="Designer" />
</ItemGroup>
</Project>

22
packages/Avalonia/AvaloniaBuildTasks.targets

@ -4,6 +4,20 @@
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
<AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance>
</PropertyGroup>
<!-- Unfortunately we have to update default items in .targets since custom nuget props are improted before Microsoft.NET.Sdk.DefaultItems.props -->
<ItemGroup Condition="'$(EnableDefaultItems)'=='True'">
<Compile Update="**\*.paml.cs">
<DependentUpon>%(Filename)</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="**\*.axaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
<SubType>Code</SubType>
</Compile>
<None Remove="**\*.axaml" />
<None Remove="**\*.paml" />
</ItemGroup>
<UsingTask TaskName="GenerateAvaloniaResourcesTask"
AssemblyFile="$(AvaloniaBuildTasksLocation)"
@ -31,9 +45,12 @@
<Target Name="GenerateAvaloniaResources"
BeforeTargets="CoreCompile;CoreResGen"
Inputs="@(AvaloniaResource);$(MSBuildAllProjects)"
Inputs="@(AvaloniaResource);@(AvaloniaXaml);$(MSBuildAllProjects)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)">
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)">
<ItemGroup>
<AvaloniaResource Include="@(AvaloniaXaml)"/>
</ItemGroup>
<GenerateAvaloniaResourcesTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
Output="$(AvaloniaResourcesTemporaryFilePath)"
@ -79,5 +96,6 @@
<ItemGroup>
<UpToDateCheckInput Include="@(AvaloniaResource)" />
<UpToDateCheckInput Include="@(AvaloniaXaml)" />
</ItemGroup>
</Project>

18
packages/Avalonia/AvaloniaItemSchema.xaml

@ -0,0 +1,18 @@
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">
<ContentType
Name="AvaloniaXaml"
DisplayName="Avalonia XAML"
ItemType="AvaloniaXaml">
<NameValuePair Name="DependentFileExtensions" Value=".cs" />
<NameValuePair Name="DefaultMetadata_SubType" Value="Designer" />
</ContentType>
<ItemType Name="AvaloniaXaml" DisplayName="Avalonia XAML" />
<FileExtension Name=".axaml" ContentType="AvaloniaXaml" />
<FileExtension Name=".paml" ContentType="AvaloniaXaml" />
<ContentType
Name="AvaloniaResource"
DisplayName="Avalonia Resource"
ItemType="AvaloniaResource"
/>
</ProjectSchemaDefinitions>

37
readme.md

@ -1,24 +1,20 @@
<img src='https://avatars2.githubusercontent.com/u/14075148?s=200&v=4' width='100' />
[![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) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
<br />
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](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) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)
# Avalonia
<img alt="Avalonia" src="https://user-images.githubusercontent.com/6759207/84897744-cab6d800-b0ae-11ea-8214-e5174d71f5c8.png" width="400"/>
| 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) | [![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 AvaloniaUI
## About
Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development.
**Avalonia** is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), macOS.
<img src="https://user-images.githubusercontent.com/6759207/84751662-7c79da00-afc5-11ea-8780-dda28db70b76.png" width="700" />
**Avalonia** is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development.
> **Note:** The UI theme you see in the picture above is still work-in-progress and will be available in the upcoming Avalonia 0.10.0 release. However, you can connect to our nightly build feed and install latest pre-release versions of Avalonia NuGet packages, if you are willing to help out with the development and testing. See [Using nightly build feed](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) for more info.
To see the status of some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239).
To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been.
[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
## Getting Started
## 🚀 Getting Started
The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starer guide see our [documentation](http://avaloniaui.net/docs/quickstart/create-new-project).
@ -30,6 +26,17 @@ Install-Package Avalonia
Install-Package Avalonia.Desktop
```
## Showcase
Examples of UIs built with Avalonia
![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png)
![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG)
![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG)
![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png)
## JetBrains Rider
If you need to develop Avalonia app with JetBrains Rider, go and *vote* on [this issue](https://youtrack.jetbrains.com/issue/RIDER-39247) in their tracker. JetBrains won't do things without their users telling them that they want the feature, so only **YOU** can make it happen.
@ -61,7 +68,7 @@ Avalonia is licenced under the [MIT licence](licence.md).
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing/contributing)].
This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing)].
<a href="https://github.com/AvaloniaUI/Avalonia/graphs/contributors"><img src="https://opencollective.com/Avalonia/contributors.svg?width=890&button=false" /></a>
### Backers

2
samples/BindingDemo/App.xaml.cs

@ -1,10 +1,8 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Serilog;
namespace BindingDemo
{

1
samples/BindingDemo/BindingDemo.csproj

@ -10,7 +10,6 @@
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />

1
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -10,6 +10,5 @@
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\NetFX.props" />
</Project>

2
samples/ControlCatalog.Desktop/Program.cs

@ -2,10 +2,8 @@ using System;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.Platform;
using Avalonia.ReactiveUI;
using Serilog;
namespace ControlCatalog
{

7
samples/ControlCatalog.NetCore/Program.cs

@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using Avalonia;
using Avalonia.ReactiveUI;
using Avalonia.Dialogs;
using Avalonia.OpenGL;
using Avalonia.ReactiveUI;
namespace ControlCatalog.NetCore
{
@ -69,7 +67,8 @@ namespace ControlCatalog.NetCore
})
.UseSkia()
.UseReactiveUI()
.UseManagedSystemDialogs();
.UseManagedSystemDialogs()
.LogToDebug();
static void SilenceConsole()
{

1
samples/ControlCatalog/ControlCatalog.csproj

@ -28,6 +28,5 @@
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
</ItemGroup>
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\BuildTargets.targets" />
</Project>

9
samples/ControlCatalog/MainView.xaml

@ -29,7 +29,11 @@
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<pages:DataGridPage/>
</TabItem>
<TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
<TabItem Header="Date/Time Picker">
<pages:DateTimePickerPage/>
</TabItem>
<TabItem Header="CalendarDatePicker">
<pages:CalendarDatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"
@ -51,11 +55,14 @@
<TabItem Header="Pointers (Touch)"><pages:PointersPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="ScrollViewer"><pages:ScrollViewerPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="SplitView"><pages:SplitViewPage/></TabItem>
<TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
<TabItem Header="TabStrip"><pages:TabStripPage/></TabItem>
<TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>
<TabItem Header="TextBlock"><pages:TextBlockPage/></TabItem>
<TabItem Header="ToggleSwitch"><pages:ToggleSwitchPage/></TabItem>
<TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
<TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>

6
samples/ControlCatalog/Models/Person.cs

@ -21,12 +21,12 @@ namespace ControlCatalog.Models
get => _firstName;
set
{
_firstName = value;
if (string.IsNullOrWhiteSpace(value))
SetError(nameof(FirstName), "First Name Required");
else
SetError(nameof(FirstName), null);
_firstName = value;
OnPropertyChanged(nameof(FirstName));
}
@ -37,12 +37,12 @@ namespace ControlCatalog.Models
get => _lastName;
set
{
_lastName = value;
if (string.IsNullOrWhiteSpace(value))
SetError(nameof(LastName), "Last Name Required");
else
SetError(nameof(LastName), null);
_lastName = value;
OnPropertyChanged(nameof(LastName));
}
}
@ -95,4 +95,4 @@ namespace ControlCatalog.Models
return null;
}
}
}
}

7
samples/ControlCatalog/Pages/BorderPage.xaml

@ -29,6 +29,13 @@
Padding="16">
<TextBlock>Rounded Corners</TextBlock>
</Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush2}" Width="100" Height="100"
BorderThickness="0"
Background="White"
CornerRadius="100" ClipToBounds="True">
<Image Source="/Assets/maple-leaf-888807_640.jpg" Stretch="UniformToFill" />
</Border>
<TextBlock Text="Border with Clipping" HorizontalAlignment="Center" />
</StackPanel>
</StackPanel>
</UserControl>

4
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -26,7 +26,8 @@
</Button>
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock" Text="Repeat Button: 0" />
</RepeatButton>
</RepeatButton>
<ToggleButton Content="Toggle Button"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8" Width="150">
@ -34,6 +35,7 @@
<Button BorderBrush="{DynamicResource ThemeAccentBrush}">Border Color</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4" IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel>
</StackPanel>
</StackPanel>

18
samples/ControlCatalog/Pages/DatePickerPage.xaml → samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml

@ -1,8 +1,8 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DatePickerPage">
x:Class="ControlCatalog.Pages.CalendarDatePickerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h1">CalendarDatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>
<StackPanel Orientation="Horizontal"
@ -12,34 +12,34 @@
<StackPanel Orientation="Vertical"
Width="200">
<TextBlock Text="SelectedDateFormat: Short"/>
<DatePicker Name="DatePicker1"
<CalendarDatePicker Name="DatePicker1"
SelectedDateFormat="Short"
Margin="0,0,0,8"/>
<TextBlock Text="SelectedDateFormat: Long"/>
<DatePicker Name="DatePicker2"
<CalendarDatePicker Name="DatePicker2"
SelectedDateFormat="Long"
Margin="0,0,0,8"/>
<TextBlock Text="SelectedDateFormat: Custom"/>
<DatePicker Name="DatePicker3"
<CalendarDatePicker Name="DatePicker3"
SelectedDateFormat="Custom"
CustomDateFormatString="ddd, MMM d"
Margin="0,0,0,8"/>
<TextBlock Text="Blackout Dates"/>
<DatePicker Name="DatePicker4"
<CalendarDatePicker Name="DatePicker4"
Margin="0,0,0,8"/>
<DatePicker Margin="0,0,0,8"
<CalendarDatePicker Margin="0,0,0,8"
Watermark="Watermark"/>
<DatePicker Margin="0,0,0,8"
<CalendarDatePicker Margin="0,0,0,8"
Name="DatePicker5"
Watermark="Floating Watermark"
UseFloatingWatermark="True"/>
<TextBlock Text="Disabled"/>
<DatePicker IsEnabled="False"/>
<CalendarDatePicker IsEnabled="False"/>
</StackPanel>
</StackPanel>

14
samples/ControlCatalog/Pages/DatePickerPage.xaml.cs → samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@ -4,17 +4,17 @@ using System;
namespace ControlCatalog.Pages
{
public class DatePickerPage : UserControl
public class CalendarDatePickerPage : UserControl
{
public DatePickerPage()
public CalendarDatePickerPage()
{
InitializeComponent();
var dp1 = this.FindControl<DatePicker>("DatePicker1");
var dp2 = this.FindControl<DatePicker>("DatePicker2");
var dp3 = this.FindControl<DatePicker>("DatePicker3");
var dp4 = this.FindControl<DatePicker>("DatePicker4");
var dp5 = this.FindControl<DatePicker>("DatePicker5");
var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);

123
samples/ControlCatalog/Pages/DateTimePickerPage.xaml

@ -0,0 +1,123 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.DateTimePickerPage">
<StackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Stretch">
<TextBlock Classes="h1">DatePicker and TimePicker</TextBlock>
<TextBlock Name="DatePickerDesc" Classes="h2" TextWrapping="Wrap"/>
<TextBlock Name="TimePickerDesc" Classes="h2" TextWrapping="Wrap"/>
<StackPanel Orientation="Vertical"
Margin="16"
HorizontalAlignment="Stretch"
Spacing="16">
<TextBlock FontSize="18">A simple DatePicker with a header</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<DatePicker Header="Pick a date" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;DatePicker Header="Pick a date" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
<TextBlock FontSize="18">A DatePicker with day formatted and year hidden.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<DatePicker x:Name="Control2" DayFormat="d (ddd)"
YearVisible="False" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;DatePicker DayFormat="d (ddd)" YearVisible="False" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
<Border Background="{DynamicResource SystemControlHighlightBaseLowBrush}" BorderThickness="1" Margin="15" />
<TextBlock FontSize="18">A simple TimePicker.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
<TextBlock FontSize="18">A TimePicker with a header and minute increments specified.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker Header="Arrival time" MinuteIncrement="15" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker Header="Arrival time" MinuteIncrement="15" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
<TextBlock FontSize="18">A TimePicker using a 12-hour clock.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker ClockIdentifier="12HourClock" Header="12 hour clock" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
<TextBlock FontSize="18">A TimePicker using a 24-hour clock.</TextBlock>
<StackPanel Orientation="Vertical">
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1" Padding="15">
<TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" />
</Border>
<Panel Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
<TextBlock Padding="15">
<TextBlock.Text>
<x:String>
&lt;TimePicker ClockIdentifier="24HourClock" Header="24 hour clock" /&gt;
</x:String>
</TextBlock.Text>
</TextBlock>
</Panel>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

30
samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs

@ -0,0 +1,30 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class DateTimePickerPage : UserControl
{
public DateTimePickerPage()
{
this.InitializeComponent();
this.FindControl<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
"for example to schedule an appointment. The DatePicker displays three controls for month, day, and year. " +
"These controls are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"Order of month, day, and year is dynamically set based on user date settings";
this.FindControl<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
"to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
"are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden.";
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

2
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -11,5 +11,7 @@
<Button Name="DecoratedWindowDialog">Decorated window (dialog)</Button>
<Button Name="Dialog">Dialog</Button>
<Button Name="DialogNoTaskbar">Dialog (No taskbar icon)</Button>
<Button Name="OwnedWindow">Owned window</Button>
<Button Name="OwnedWindowNoTaskbar">Owned window (No taskbar icon)</Button>
</StackPanel>
</UserControl>

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

@ -93,6 +93,21 @@ namespace ControlCatalog.Pages
window.ShowInTaskbar = false;
window.ShowDialog(GetWindow());
};
this.FindControl<Button>("OwnedWindow").Click += delegate
{
var window = CreateSampleWindow();
window.Show(GetWindow());
};
this.FindControl<Button>("OwnedWindowNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
window.ShowInTaskbar = false;
window.Show(GetWindow());
};
}
private Window CreateSampleWindow()

7
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -16,6 +16,8 @@
<Button Command="{Binding AddItem}">Add Item</Button>
<Button Command="{Binding RandomizeHeights}">Randomize Heights</Button>
<Button Command="{Binding ResetItems}">Reset items</Button>
<Button x:Name="scrollToLast">Scroll to Last</Button>
<Button x:Name="scrollToRandom">Scroll to Random</Button>
</StackPanel>
<Border BorderThickness="1" BorderBrush="{DynamicResource ThemeBorderMidBrush}" Margin="0 0 0 16">
<ScrollViewer Name="scroller"
@ -24,7 +26,10 @@
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<TextBlock Focusable="True" Height="{Binding Height}" Text="{Binding Text}"/>
<TextBlock Focusable="True"
Background="{Binding Background}"
Height="{Binding Height}"
Text="{Binding Text}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>

34
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -5,23 +5,32 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ItemsRepeaterPage : UserControl
{
private readonly ItemsRepeaterPageViewModel _viewModel;
private ItemsRepeater _repeater;
private ScrollViewer _scroller;
private Button _scrollToLast;
private Button _scrollToRandom;
private Random _random = new Random(0);
public ItemsRepeaterPage()
{
this.InitializeComponent();
_repeater = this.FindControl<ItemsRepeater>("repeater");
_scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom");
_repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown;
DataContext = new ItemsRepeaterPageViewModel();
_scrollToLast.Click += scrollToLast_Click;
_scrollToRandom.Click += scrollToRandom_Click;
DataContext = _viewModel = new ItemsRepeaterPageViewModel();
}
private void InitializeComponent()
@ -73,18 +82,37 @@ namespace ControlCatalog.Pages
}
}
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();
}
private void RepeaterClick(object sender, PointerPressedEventArgs e)
{
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item;
_viewModel.SelectedItem = item;
}
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.F5)
{
((ItemsRepeaterPageViewModel)DataContext).ResetItems();
_viewModel.ResetItems();
}
}
private void scrollToLast_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
ScrollTo(_viewModel.Items.Count - 1);
}
private void scrollToRandom_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
ScrollTo(_random.Next(_viewModel.Items.Count - 1));
}
}
}

12
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -50,22 +50,22 @@
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Margin="2">Text:</TextBlock>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding #upDown.Text}" VerticalAlignment="Center" Margin="2" />
</Grid>
<Grid Grid.Row="0" Grid.Column="2" Margin="10,2,2,2" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, 120">
<Grid Grid.Row="0" Grid.Column="2" Margin="10,2,2,2" RowDefinitions="Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Minimum:</TextBlock>
<NumericUpDown Grid.Row="0" Grid.Column="1" Value="{Binding #upDown.Minimum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" Width="70" HorizontalAlignment="Center"/>
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Maximum:</TextBlock>
<NumericUpDown Grid.Row="1" Grid.Column="1" Value="{Binding #upDown.Maximum}"
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" Width="70" HorizontalAlignment="Center"/>
CultureInfo="{Binding #upDown.CultureInfo}" VerticalAlignment="Center" Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Increment:</TextBlock>
<NumericUpDown Grid.Row="2" Grid.Column="1" Value="{Binding #upDown.Increment}" VerticalAlignment="Center"
Margin="2" Width="70" HorizontalAlignment="Center"/>
Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock>
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
Margin="2" Width="70" HorizontalAlignment="Center"/>
Margin="2" HorizontalAlignment="Center"/>
</Grid>
</Grid>
@ -73,7 +73,7 @@
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10">
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock>
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5"
CultureInfo="en-US" VerticalAlignment="Center" Width="100"
CultureInfo="en-US" VerticalAlignment="Center"
Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel>

26
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,29 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ProgressBarPage">
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock>
<StackPanel>
<CheckBox
x:Name="showProgress"
Margin="10,16,0,0"
Content="Show Progress Text" />
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" />
<CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" />
<StackPanel Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16">
<StackPanel Spacing="16">
<ProgressBar ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" />
<ProgressBar IsIndeterminate="True"/>
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" />
</StackPanel>
<ProgressBar ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" />
<ProgressBar Orientation="Vertical" IsIndeterminate="True" />
<ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" />
</StackPanel>
<StackPanel Margin="16">
<Slider Name="hprogress" Maximum="100" Value="40"/>
<Slider Name="vprogress" Maximum="100" Value="60"/>
<Slider Name="hprogress" Maximum="100" Value="40" />
<Slider Name="vprogress" Maximum="100" Value="60" />
</StackPanel>
</StackPanel>
</StackPanel>

35
samples/ControlCatalog/Pages/ScrollViewerPage.xaml

@ -0,0 +1,35 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ScrollViewerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ScrollViewer</TextBlock>
<TextBlock Classes="h2">Allows for horizontal and vertical content scrolling.</TextBlock>
<Grid ColumnDefinitions="Auto, *">
<StackPanel Orientation="Vertical" Spacing="4">
<ToggleSwitch IsChecked="{Binding AllowAutoHide}" Content="Allow auto hide" />
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Horizontal Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding HorizontalScrollVisibility}" />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Vertical Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding VerticalScrollVisibility}" />
</StackPanel>
</StackPanel>
<ScrollViewer x:Name="ScrollViewer"
Grid.Column="1"
Width="400" Height="400"
AllowAutoHide="{Binding AllowAutoHide}"
HorizontalScrollBarVisibility="{Binding HorizontalScrollVisibility}"
VerticalScrollBarVisibility="{Binding VerticalScrollVisibility}">
<Image Width="800" Height="800" Stretch="UniformToFill"
Source="/Assets/delicate-arch-896885_640.jpg" />
</ScrollViewer>
</Grid>
</StackPanel>
</UserControl>

65
samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs

@ -0,0 +1,65 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using ReactiveUI;
namespace ControlCatalog.Pages
{
public class ScrollViewerPageViewModel : ReactiveObject
{
private bool _allowAutoHide;
private ScrollBarVisibility _horizontalScrollVisibility;
private ScrollBarVisibility _verticalScrollVisibility;
public ScrollViewerPageViewModel()
{
AvailableVisibility = new List<ScrollBarVisibility>
{
ScrollBarVisibility.Auto,
ScrollBarVisibility.Visible,
ScrollBarVisibility.Hidden,
ScrollBarVisibility.Disabled,
};
HorizontalScrollVisibility = ScrollBarVisibility.Auto;
VerticalScrollVisibility = ScrollBarVisibility.Auto;
AllowAutoHide = true;
}
public bool AllowAutoHide
{
get => _allowAutoHide;
set => this.RaiseAndSetIfChanged(ref _allowAutoHide, value);
}
public ScrollBarVisibility HorizontalScrollVisibility
{
get => _horizontalScrollVisibility;
set => this.RaiseAndSetIfChanged(ref _horizontalScrollVisibility, value);
}
public ScrollBarVisibility VerticalScrollVisibility
{
get => _verticalScrollVisibility;
set => this.RaiseAndSetIfChanged(ref _verticalScrollVisibility, value);
}
public List<ScrollBarVisibility> AvailableVisibility { get; }
}
public class ScrollViewerPage : UserControl
{
public ScrollViewerPage()
{
InitializeComponent();
DataContext = new ScrollViewerPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

21
samples/ControlCatalog/Pages/SliderPage.xaml

@ -6,11 +6,22 @@
<TextBlock Classes="h2">A control that lets the user select from a range of values by moving a Thumb control along a Track.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16">
<Slider Value="0"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300"/>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center">
<Slider Value="0"
Minimum="0"
Maximum="100"
TickFrequency="10"
Width="300" />
<Slider Name="CustomTickedSlider"
Value="0"
Minimum="0"
Maximum="100"
TickPlacement="BottomRight"
IsSnapToTickEnabled="True"
Ticks="0,20,25,40,75,100"
Width="300" />
</StackPanel>
<Slider Value="0"
Minimum="0"
Maximum="100"

97
samples/ControlCatalog/Pages/SplitViewPage.xaml

@ -0,0 +1,97 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.SplitViewPage">
<Border>
<Grid ColumnDefinitions="*,225">
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="4" Margin="5">
<ToggleButton Name="PaneOpenButton"
Content="IsPaneOpen"
IsChecked="{Binding IsPaneOpen, ElementName=SplitView}" />
<ToggleButton Name="UseLightDismissOverlayModeButton"
Content="UseLightDismissOverlayMode"
IsChecked="{Binding UseLightDismissOverlayMode, ElementName=SplitView}" />
<ToggleSwitch OffContent="Left" OnContent="Right" Content="Placement" IsChecked="{Binding !IsLeft}" />
<TextBlock Text="DisplayMode" />
<ComboBox Name="DisplayModeSelector" Width="170" Margin="10" SelectedIndex="{Binding DisplayMode}">
<ComboBoxItem>Inline</ComboBoxItem>
<ComboBoxItem>CompactInline</ComboBoxItem>
<ComboBoxItem>Overlay</ComboBoxItem>
<ComboBoxItem>CompactOverlay</ComboBoxItem>
</ComboBox>
<TextBlock Text="PaneBackground" />
<ComboBox Name="PaneBackgroundSelector" SelectedIndex="0" Width="170" Margin="10">
<ComboBoxItem Tag="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">SystemControlBackgroundChromeMediumLowBrush</ComboBoxItem>
<ComboBoxItem Tag="Red">Red</ComboBoxItem>
<ComboBoxItem Tag="Blue">Blue</ComboBoxItem>
<ComboBoxItem Tag="Green">Green</ComboBoxItem>
</ComboBox>
<TextBlock Text="{Binding Value, ElementName=OpenPaneLengthSlider, StringFormat='{}OpenPaneLength: {0}'}" />
<Slider Name="OpenPaneLengthSlider" Value="256" Minimum="128" Maximum="500"
Width="150" />
<TextBlock Text="{Binding Value, ElementName=CompactPaneLengthSlider, StringFormat='{}CompactPaneLength: {0}'}" />
<Slider Name="CompactPaneLengthSlider" Value="48" Minimum="24" Maximum="128"
Width="150" />
</StackPanel>
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}"
BorderThickness="1">
<!--{Binding SelectedItem.Tag, ElementName=PaneBackgroundSelector}-->
<SplitView Name="SplitView"
PanePlacement="{Binding PanePlacement}"
PaneBackground="{Binding SelectedItem.Tag, ElementName=PaneBackgroundSelector}"
OpenPaneLength="{Binding Value, ElementName=OpenPaneLengthSlider}"
CompactPaneLength="{Binding Value, ElementName=CompactPaneLengthSlider}"
DisplayMode="{Binding CurrentDisplayMode}">
<SplitView.Pane>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="PANE CONTENT" FontWeight="Bold" Name="PaneHeader" Margin="5,12,0,0" />
<ListBoxItem Grid.Row="1" VerticalAlignment="Top" Margin="0 10">
<StackPanel Orientation="Horizontal">
<!--Path glyph from materialdesignicons.com-->
<Border Width="48">
<Viewbox Width="24" Height="24" HorizontalAlignment="Left">
<Canvas Width="24" Height="24">
<Path Fill="{DynamicResource SystemControlForegroundBaseHighBrush}" Data="M16 17V19H2V17S2 13 9 13 16 17 16 17M12.5 7.5A3.5 3.5 0 1 0 9 11A3.5 3.5 0 0 0 12.5 7.5M15.94 13A5.32 5.32 0 0 1 18 17V19H22V17S22 13.37 15.94 13M15 4A3.39 3.39 0 0 0 13.07 4.59A5 5 0 0 1 13.07 10.41A3.39 3.39 0 0 0 15 11A3.5 3.5 0 0 0 15 4Z" />
</Canvas>
</Viewbox>
</Border>
<TextBlock Text="People" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<TextBlock Grid.Row="2" Text="Item at bottom" Margin="60,12" />
</Grid>
</SplitView.Pane>
<Grid>
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" HorizontalAlignment="Right" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" VerticalAlignment="Bottom" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" VerticalAlignment="Bottom" HorizontalAlignment="Right" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" />
</Grid>
</SplitView>
</Border>
</Grid>
</Border>
</UserControl>

21
samples/ControlCatalog/Pages/SplitViewPage.xaml.cs

@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class SplitViewPage : UserControl
{
public SplitViewPage()
{
this.InitializeComponent();
DataContext = new SplitViewPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

79
samples/ControlCatalog/Pages/ToggleSwitchPage.xaml

@ -0,0 +1,79 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ToggleSwitchPage" Margin="5">
<StackPanel Width="500" HorizontalAlignment="Center">
<TextBlock Text="Simple ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<StackPanel>
<ToggleSwitch Margin="10"/>
<TextBox Text="&lt;ToggleSwitch/&gt;" Classes="CodeBox"/>
</StackPanel>
</Border>
<TextBlock Text="headered ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<StackPanel>
<ToggleSwitch Content="headered" IsChecked="true" Margin="10"/>
<TextBox Classes="CodeBox"
Text="&lt;ToggleSwitch&gt;headered&lt;/ToggleSwitch&gt;"/>
</StackPanel>
</Border>
<TextBlock Text="Custom content ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<StackPanel>
<ToggleSwitch Content="Custom"
OnContent="On"
OffContent="Off"
Margin="10"/>
<TextBox Text="&lt;ToggleSwitch Content=&quot;Custom&quot;
ContentOn=&quot;On&quot;
ContentOff=&quot;Off&quot; /&gt;"
Classes="CodeBox"/>
</StackPanel>
</Border>
<TextBlock Text="Image content ToggleSwitch" Classes="header"/>
<Border Classes="Thin">
<StackPanel>
<ToggleSwitch Content="Just Click!" Margin="10">
<ToggleSwitch.OnContent>
<Image Source="/Assets/hirsch-899118_640.jpg" Height="32"/>
</ToggleSwitch.OnContent>
<ToggleSwitch.OffContent>
<Image Source="/Assets/delicate-arch-896885_640.jpg" Height="32"/>
</ToggleSwitch.OffContent>
</ToggleSwitch>
</StackPanel>
</Border>
</StackPanel>
<UserControl.Styles >
<Style Selector="TextBox.CodeBox" >
<Setter Property="Padding" Value="10"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="IsEnabled" Value="true"/>
</Style>
<Style Selector="TextBlock.header">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="0 20 0 20"/>
</Style>
<Style Selector="Border.Thin">
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="0.5"/>
<Setter Property="CornerRadius" Value="2"/>
</Style>
</UserControl.Styles>
</UserControl>

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

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

2
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -18,7 +18,7 @@
ToolTip.Tip="This is a ToolTip">
<TextBlock>Hover Here</TextBlock>
</Border>
<CheckBox Grid.Column="1"
<ToggleSwitch Grid.Column="1"
Margin="5"
Grid.Row="0"
IsChecked="{Binding ElementName=Border, Path=(ToolTip.IsOpen)}"

3
samples/ControlCatalog/SideBar.xaml

@ -67,6 +67,9 @@
<Setter Property="(ScrollViewer.HorizontalScrollBarVisibility)" Value="Auto"/>
<Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Auto"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>

13
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Media;
using ReactiveUI;
namespace ControlCatalog.ViewModels
@ -27,7 +28,7 @@ namespace ControlCatalog.ViewModels
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
Items.Insert(index + 1, new Item { Text = $"New Item {_newItemIndex++}" });
Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
}
public void RandomizeHeights()
@ -52,7 +53,7 @@ namespace ControlCatalog.ViewModels
_newGenerationIndex++;
return new ObservableCollection<Item>(
Enumerable.Range(1, 100000).Select(i => new Item
Enumerable.Range(1, 100000).Select(i => new Item(i)
{
Text = $"Item {i.ToString()} {suffix}"
}));
@ -61,6 +62,12 @@ namespace ControlCatalog.ViewModels
public class Item : ReactiveObject
{
private double _height = double.NaN;
private int _index;
public Item(int index)
{
_index = index;
}
public string Text { get; set; }
@ -69,6 +76,8 @@ namespace ControlCatalog.ViewModels
get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value);
}
public IBrush Background => ((_index % 2) == 0) ? Brushes.Yellow : Brushes.Wheat;
}
}
}

46
samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs

@ -0,0 +1,46 @@
using System;
using Avalonia.Controls;
using ReactiveUI;
namespace ControlCatalog.ViewModels
{
public class SplitViewPageViewModel : ReactiveObject
{
private bool _isLeft = true;
private int _displayMode = 3; //CompactOverlay
public bool IsLeft
{
get => _isLeft;
set
{
this.RaiseAndSetIfChanged(ref _isLeft, value);
this.RaisePropertyChanged(nameof(PanePlacement));
}
}
public int DisplayMode
{
get => _displayMode;
set
{
this.RaiseAndSetIfChanged(ref _displayMode, value);
this.RaisePropertyChanged(nameof(CurrentDisplayMode));
}
}
public SplitViewPanePlacement PanePlacement => _isLeft ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right;
public SplitViewDisplayMode CurrentDisplayMode
{
get
{
if (Enum.IsDefined(typeof(SplitViewDisplayMode), _displayMode))
{
return (SplitViewDisplayMode)_displayMode;
}
return SplitViewDisplayMode.CompactOverlay;
}
}
}
}

2
samples/RenderDemo/App.xaml.cs

@ -1,5 +1,4 @@
using Avalonia;
using Avalonia.Logging.Serilog;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
@ -22,6 +21,5 @@ namespace RenderDemo
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug();
}
}

3
samples/RenderDemo/MainWindow.xaml

@ -29,6 +29,9 @@
<TabItem Header="Animations">
<pages:AnimationsPage/>
</TabItem>
<TabItem Header="Transitions">
<pages:TransitionsPage/>
</TabItem>
<TabItem Header="Clipping">
<pages:ClippingPage/>
</TabItem>

115
samples/RenderDemo/Pages/TransitionsPage.xaml

@ -0,0 +1,115 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.Pages.TransitionsPage">
<UserControl.Styles>
<Styles>
<Styles.Resources>
<Template x:Key="Acorn">
<Path Fill="White" Stretch="Uniform"
Data="F1 M 16.6309,18.6563C 17.1309,
8.15625 29.8809,14.1563 29.8809,
14.1563C 30.8809,11.1563 34.1308,
11.4063 34.1308,11.4063C 33.5,12
34.6309,13.1563 34.6309,13.1563C
32.1309,13.1562 31.1309,14.9062
31.1309,14.9062C 41.1309,23.9062
32.6309,27.9063 32.6309,27.9062C
24.6309,24.9063 21.1309,22.1562
16.6309,18.6563 Z M 16.6309,19.9063C
21.6309,24.1563 25.1309,26.1562
31.6309,28.6562C 31.6309,28.6562
26.3809,39.1562 18.3809,36.1563C
18.3809,36.1563 18,38 16.3809,36.9063C
15,36 16.3809,34.9063 16.3809,34.9063C
16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"/>
</Template>
<Template x:Key="Heart">
<Path Fill="Red" Stretch="Uniform" Data="
M 272.70141,238.71731
C 206.46141,238.71731 152.70146,292.4773 152.70146,358.71731
C 152.70146,493.47282 288.63461,528.80461 381.26391,662.02535
C 468.83815,529.62199 609.82641,489.17075 609.82641,358.71731
C 609.82641,292.47731 556.06651,238.7173 489.82641,238.71731
C 441.77851,238.71731 400.42481,267.08774 381.26391,307.90481
C 362.10311,267.08773 320.74941,238.7173 272.70141,238.71731 z "/>
</Template>
</Styles.Resources>
<Style Selector="Border.Test">
<Setter Property="Margin" Value="15"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Child" Value="{StaticResource Acorn}"/>
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:1" />
</Transitions>
</Setter>
<Setter Property="RenderTransform" Value="none" />
</Style>
<Style Selector="Border.Rect1:pointerover">
<Setter Property="RenderTransform" Value="rotate(120deg) scale(1.5)" />
</Style>
<Style Selector="Border.Rect2:pointerover">
<Setter Property="RenderTransform" Value="scale(0.8)" />
</Style>
<Style Selector="Border.Rect3">
<Setter Property="Child" Value="{StaticResource Heart}"/>
</Style>
<Style Selector="Border.Rect3:pointerover">
<Setter Property="RenderTransform" Value="rotate(1turn)" />
</Style>
<Style Selector="Border.Rect4:pointerover">
<Setter Property="RenderTransform" Value="translateY(-100px)" />
</Style>
<Style Selector="Border.Rect5:pointerover">
<Setter Property="RenderTransform" Value="skewX(-20deg)" />
</Style>
<Style Selector="Border.Rect5:pointerover">
<Setter Property="RenderTransform" Value="skewX(-20deg)" />
</Style>
<Style Selector="Border.Rect6">
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.5" Delay="0:0:1"/>
</Transitions>
</Setter>
<Setter Property="RenderTransform" Value="scale(0.8)" />
</Style>
<Style Selector="Border.Rect6:pointerover">
<Setter Property="RenderTransform" Value="none" />
</Style>
</Styles>
</UserControl.Styles>
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False">
<StackPanel.Clock>
<Clock />
</StackPanel.Clock>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock>
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" />
</StackPanel>
<WrapPanel ClipToBounds="False">
<Border Classes="Test Rect1" Background="DarkRed"/>
<Border Classes="Test Rect2" Background="Magenta"/>
<Border Classes="Test Rect3"/>
<Border Classes="Test Rect4" Background="Navy"/>
<Border Classes="Test Rect5" Background="SeaGreen"/>
<Border Classes="Test Rect6" Background="Orange"/>
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>

37
samples/RenderDemo/Pages/TransitionsPage.xaml.cs

@ -0,0 +1,37 @@
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using RenderDemo.ViewModels;
namespace RenderDemo.Pages
{
public class TransitionsPage : UserControl
{
public TransitionsPage()
{
InitializeComponent();
this.DataContext = new AnimationsPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void ToggleClock(object sender, RoutedEventArgs args)
{
var button = sender as Button;
var clock = button.Clock;
if (clock.PlayState == PlayState.Run)
{
clock.PlayState = PlayState.Pause;
}
else if (clock.PlayState == PlayState.Pause)
{
clock.PlayState = PlayState.Run;
}
}
}
}

1
samples/RenderDemo/RenderDemo.csproj

@ -13,7 +13,6 @@
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />

2
samples/VirtualizationDemo/Program.cs

@ -1,9 +1,7 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Logging.Serilog;
using Avalonia.ReactiveUI;
using Serilog;
namespace VirtualizationDemo
{

1
samples/VirtualizationDemo/VirtualizationDemo.csproj

@ -10,7 +10,6 @@
</ItemGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />

1
samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj

@ -26,7 +26,6 @@
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
</Project>

8
samples/interop/NativeEmbedSample/App.xaml

@ -0,0 +1,8 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NativeEmbedSample.App">
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
</Application.Styles>
</Application>

22
samples/interop/NativeEmbedSample/App.xaml.cs

@ -0,0 +1,22 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace NativeEmbedSample
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
desktopLifetime.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
}
}

121
samples/interop/NativeEmbedSample/EmbedSample.cs

@ -0,0 +1,121 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Threading;
using MonoMac.AppKit;
using MonoMac.Foundation;
using MonoMac.WebKit;
using Encoding = SharpDX.Text.Encoding;
namespace NativeEmbedSample
{
public class EmbedSample : NativeControlHost
{
public bool IsSecond { get; set; }
private Process _mplayer;
IPlatformHandle CreateLinux(IPlatformHandle parent)
{
if (IsSecond)
{
var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle);
if (chooser != null)
return chooser;
}
var control = base.CreateNativeControlCore(parent);
var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName,
"..",
"nodes.mp4"));
_mplayer = Process.Start(new ProcessStartInfo("mplayer",
$"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"")
{
UseShellExecute = false,
});
return control;
}
void DestroyLinux(IPlatformHandle handle)
{
_mplayer?.Kill();
_mplayer = null;
base.DestroyNativeControlCore(handle);
}
private const string RichText =
@"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;}
{\*\generator Riched20 6.3.9600}\viewkind4\uc1
\pard\sa200\sl276\slmult1\f0\fs22\lang9 <PREFIX>I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par
}";
IPlatformHandle CreateWin32(IPlatformHandle parent)
{
WinApi.LoadLibrary("Msftedit.dll");
var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W",
@"Rich Edit",
0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle,
IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero);
var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 };
var text = RichText.Replace("<PREFIX>", IsSecond ? "\\qr " : "");
var bytes = Encoding.UTF8.GetBytes(text);
WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes);
return new PlatformHandle(handle, "HWND");
}
void DestroyWin32(IPlatformHandle handle)
{
WinApi.DestroyWindow(handle.Handle);
}
IPlatformHandle CreateOSX(IPlatformHandle parent)
{
// Note: We are using MonoMac for example purposes
// It shouldn't be used in production apps
MacHelper.EnsureInitialized();
var webView = new WebView();
Dispatcher.UIThread.Post(() =>
{
webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl(
IsSecond ? "https://bing.com": "https://google.com/")));
});
return new MacOSViewHandle(webView);
}
void DestroyOSX(IPlatformHandle handle)
{
((MacOSViewHandle)handle).Dispose();
}
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return CreateLinux(parent);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return CreateWin32(parent);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return CreateOSX(parent);
return base.CreateNativeControlCore(parent);
}
protected override void DestroyNativeControlCore(IPlatformHandle control)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
DestroyLinux(control);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
DestroyWin32(control);
else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
DestroyOSX(control);
else
base.DestroyNativeControlCore(control);
}
}
}

58
samples/interop/NativeEmbedSample/GtkHelper.cs

@ -0,0 +1,58 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
using Avalonia.X11.NativeDialogs;
using static Avalonia.X11.NativeDialogs.Gtk;
using static Avalonia.X11.NativeDialogs.Glib;
namespace NativeEmbedSample
{
public class GtkHelper
{
private static Task<bool> s_gtkTask;
class FileChooser : INativeControlHostDestroyableControlHandle
{
private readonly IntPtr _widget;
public FileChooser(IntPtr widget, IntPtr xid)
{
_widget = widget;
Handle = xid;
}
public IntPtr Handle { get; }
public string HandleDescriptor => "XID";
public void Destroy()
{
RunOnGlibThread(() =>
{
gtk_widget_destroy(_widget);
return 0;
}).Wait();
}
}
public static IPlatformHandle CreateGtkFileChooser(IntPtr parentXid)
{
if (s_gtkTask == null)
s_gtkTask = StartGtk();
if (!s_gtkTask.Result)
return null;
return RunOnGlibThread(() =>
{
using (var title = new Utf8Buffer("Embedded"))
{
var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder,
IntPtr.Zero);
gtk_widget_realize(widget);
var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
gtk_window_present(widget);
return new FileChooser(widget, xid);
}
}).Result;
}
}
}

39
samples/interop/NativeEmbedSample/MacHelper.cs

@ -0,0 +1,39 @@
using System;
using Avalonia.Platform;
using MonoMac.AppKit;
namespace NativeEmbedSample
{
public class MacHelper
{
private static bool _isInitialized;
public static void EnsureInitialized()
{
if (_isInitialized)
return;
_isInitialized = true;
NSApplication.Init();
}
}
class MacOSViewHandle : IPlatformHandle, IDisposable
{
private NSView _view;
public MacOSViewHandle(NSView view)
{
_view = view;
}
public IntPtr Handle => _view?.Handle ?? IntPtr.Zero;
public string HandleDescriptor => "NSView";
public void Dispose()
{
_view.Dispose();
_view = null;
}
}
}

52
samples/interop/NativeEmbedSample/MainWindow.xaml

@ -0,0 +1,52 @@
<Window xmlns="https://github.com/avaloniaui" MinWidth="500" MinHeight="300"
Width="1024" Height="800"
Title="Native embedding sample"
xmlns:local="clr-namespace:NativeEmbedSample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NativeEmbedSample.MainWindow">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Test">
<MenuItem Header="SubMenu">
<MenuItem Header="Item 1"/>
<MenuItem Header="Item 2"/>
<MenuItem Header="Item 3"/>
</MenuItem>
<MenuItem Header="Item 1"/>
<MenuItem Header="Item 2"/>
<MenuItem Header="Item 3"/>
</MenuItem>
</Menu>
<DockPanel DockPanel.Dock="Top">
<Button DockPanel.Dock="Right" Click="ShowPopupDelay">Show popup (delay)</Button>
<Button DockPanel.Dock="Right" Click="ShowPopup">Show popup</Button>
<Border DockPanel.Dock="Right" Background="#c0c0c0">
<ToolTip.Tip>
<ToolTip>
<TextBlock>Text</TextBlock>
</ToolTip>
</ToolTip.Tip>
<TextBlock>Tooltip</TextBlock>
</Border>
<TextBox Text="Lorem ipsum dolor sit amet"/>
</DockPanel>
<Grid ColumnDefinitions="*,5,*">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<CheckBox x:Name="firstVisible" IsChecked="True"/>
<TextBlock>Visible</TextBlock>
</StackPanel>
<local:EmbedSample IsVisible="{Binding #firstVisible.IsChecked}"/>
</DockPanel>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<DockPanel Grid.Column="2">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<CheckBox x:Name="secondVisible" IsChecked="True"/>
<TextBlock>Visible</TextBlock>
</StackPanel>
<local:EmbedSample IsSecond="True" IsVisible="{Binding #secondVisible.IsChecked}"/>
</DockPanel>
</Grid>
</DockPanel>
</Window>

36
samples/interop/NativeEmbedSample/MainWindow.xaml.cs

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace NativeEmbedSample
{
public class MainWindow : Window
{
public MainWindow()
{
AvaloniaXamlLoader.Load(this);
this.AttachDevTools();
}
public async void ShowPopupDelay(object sender, RoutedEventArgs args)
{
await Task.Delay(3000);
ShowPopup(sender, args);
}
public void ShowPopup(object sender, RoutedEventArgs args)
{
new ContextMenu()
{
Items = new List<MenuItem>
{
new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
}
}.Open((Control)sender);
}
}
}

30
samples/interop/NativeEmbedSample/NativeEmbedSample.csproj

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
<ProjectReference Include="..\..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2019013001" />
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
<None Remove="nodes.mp4" />
<Content Include="nodes.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Compile Include="..\..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" />
</ItemGroup>
<Import Project="..\..\..\build\SampleApp.props" />
<Import Project="..\..\..\build\BuildTargets.targets" />
<Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
</Project>

17
samples/interop/NativeEmbedSample/Program.cs

@ -0,0 +1,17 @@
using Avalonia;
namespace NativeEmbedSample
{
class Program
{
static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.With(new AvaloniaNativePlatformOptions()
{
})
.UsePlatformDetect();
}
}

74
samples/interop/NativeEmbedSample/WinApi.cs

@ -0,0 +1,74 @@
using System;
using System.Runtime.InteropServices;
namespace NativeEmbedSample
{
public unsafe class WinApi
{
public enum CommonControls : uint
{
ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header
ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips
ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips
ICC_TAB_CLASSES = 0x00000008, // tab, tooltips
ICC_UPDOWN_CLASS = 0x00000010, // updown
ICC_PROGRESS_CLASS = 0x00000020, // progress
ICC_HOTKEY_CLASS = 0x00000040, // hotkey
ICC_ANIMATE_CLASS = 0x00000080, // animate
ICC_WIN95_CLASSES = 0x000000FF,
ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown
ICC_USEREX_CLASSES = 0x00000200, // comboex
ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control
ICC_INTERNET_CLASSES = 0x00000800,
ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller
ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control
ICC_STANDARD_CLASSES = 0x00004000,
ICC_LINK_CLASS = 0x00008000
}
[StructLayout(LayoutKind.Sequential)]
public struct INITCOMMONCONTROLSEX
{
public int dwSize;
public uint dwICC;
}
[DllImport("Comctl32.dll")]
public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lib);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpClassName,
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[StructLayout(LayoutKind.Sequential)]
public struct SETTEXTEX
{
public uint Flags;
public uint Codepage;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam);
}
}

1
samples/interop/NativeEmbedSample/nodes-license.md

@ -0,0 +1 @@
nodes.mp4 by beeple is licensed under the creative commons license, downloaded from https://vimeo.com/9936271

BIN
samples/interop/NativeEmbedSample/nodes.mp4

Binary file not shown.

2
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -67,7 +67,7 @@ namespace Avalonia.Android
throw new NotSupportedException();
}
public IEmbeddableWindowImpl CreateEmbeddableWindow()
public IWindowImpl CreateEmbeddableWindow()
{
throw new NotSupportedException();
}

4
src/Android/Avalonia.Android/AvaloniaView.cs

@ -33,10 +33,8 @@ namespace Avalonia.Android
return _view.View.DispatchKeyEvent(e);
}
class ViewImpl : TopLevelImpl, IEmbeddableWindowImpl
class ViewImpl : TopLevelImpl
{
public event Action LostFocus;
public ViewImpl(Context context) : base(context)
{
View.Focusable = true;

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

@ -194,6 +194,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
}
public IPopupImpl CreatePopup() => null;
public Action LostFocus { get; set; }
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface);
}

1
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -149,7 +149,6 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Base.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\System.Memory.props" />

6
src/Avalonia.Animation/Easing/Easing.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
namespace Avalonia.Animation.Easings
@ -25,6 +26,11 @@ namespace Avalonia.Animation.Easings
/// <returns>Returns the instance of the parsed type.</returns>
public static Easing Parse(string e)
{
if (e.Contains(','))
{
return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture));
}
if (_easingTypes == null)
{
_easingTypes = new Dictionary<string, Type>();

85
src/Avalonia.Animation/Easing/SplineEasing.cs

@ -0,0 +1,85 @@
namespace Avalonia.Animation.Easings
{
/// <summary>
/// Eases a <see cref="double"/> value
/// using a user-defined cubic bezier curve.
/// Good for custom easing functions that doesn't quite
/// fit with the built-in ones.
/// </summary>
public class SplineEasing : Easing
{
/// <summary>
/// X coordinate of the first control point
/// </summary>
public double X1
{
get => _internalKeySpline.ControlPointX1;
set
{
_internalKeySpline.ControlPointX1 = value;
}
}
/// <summary>
/// Y coordinate of the first control point
/// </summary>
public double Y1
{
get => _internalKeySpline.ControlPointY1;
set
{
_internalKeySpline.ControlPointY1 = value;
}
}
/// <summary>
/// X coordinate of the second control point
/// </summary>
public double X2
{
get => _internalKeySpline.ControlPointX2;
set
{
_internalKeySpline.ControlPointX2 = value;
}
}
/// <summary>
/// Y coordinate of the second control point
/// </summary>
public double Y2
{
get => _internalKeySpline.ControlPointY2;
set
{
_internalKeySpline.ControlPointY2 = value;
}
}
private readonly KeySpline _internalKeySpline;
public SplineEasing(double x1 = 0d, double y1 = 0d, double x2 = 1d, double y2 = 1d)
{
_internalKeySpline = new KeySpline();
this.X1 = x1;
this.Y1 = y1;
this.X2 = x2;
this.Y1 = y2;
}
public SplineEasing(KeySpline keySpline)
{
_internalKeySpline = keySpline;
}
public SplineEasing()
{
_internalKeySpline = new KeySpline();
}
/// <inheritdoc/>
public override double Ease(double progress) =>
_internalKeySpline.GetSplineProgress(progress);
}
}

35
src/Avalonia.Animation/KeySpline.cs

@ -81,7 +81,10 @@ namespace Avalonia.Animation
/// <returns>A <see cref="KeySpline"/> with the appropriate values set</returns>
public static KeySpline Parse(string value, CultureInfo culture)
{
using (var tokenizer = new StringTokenizer((string)value, CultureInfo.InvariantCulture, exceptionMessage: "Invalid KeySpline."))
if (culture is null)
culture = CultureInfo.InvariantCulture;
using (var tokenizer = new StringTokenizer((string)value, culture, exceptionMessage: $"Invalid KeySpline string: \"{value}\"."))
{
return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble());
}
@ -98,6 +101,7 @@ namespace Avalonia.Animation
if (IsValidXValue(value))
{
_controlPointX1 = value;
_isDirty = true;
}
else
{
@ -112,7 +116,11 @@ namespace Avalonia.Animation
public double ControlPointY1
{
get => _controlPointY1;
set => _controlPointY1 = value;
set
{
_controlPointY1 = value;
_isDirty = true;
}
}
/// <summary>
@ -126,6 +134,7 @@ namespace Avalonia.Animation
if (IsValidXValue(value))
{
_controlPointX2 = value;
_isDirty = true;
}
else
{
@ -140,7 +149,11 @@ namespace Avalonia.Animation
public double ControlPointY2
{
get => _controlPointY2;
set => _controlPointY2 = value;
set
{
_controlPointY2 = value;
_isDirty = true;
}
}
/// <summary>
@ -330,20 +343,4 @@ namespace Avalonia.Animation
}
}
}
/// <summary>
/// Converts string values to <see cref="KeySpline"/> values
/// </summary>
public class KeySplineTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return KeySpline.Parse((string)value, culture);
}
}
}

25
src/Avalonia.Animation/KeySplineTypeConverter.cs

@ -0,0 +1,25 @@
using System;
using System.ComponentModel;
using System.Globalization;
// Ported from WPF open-source code.
// https://github.com/dotnet/wpf/blob/ae1790531c3b993b56eba8b1f0dd395a3ed7de75/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySpline.cs
namespace Avalonia.Animation
{
/// <summary>
/// Converts string values to <see cref="KeySpline"/> values
/// </summary>
public class KeySplineTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return KeySpline.Parse((string)value, culture);
}
}
}

36
src/Avalonia.Animation/TransitionInstance.cs

@ -4,6 +4,7 @@ using System.Reactive.Linq;
using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
using Avalonia.Reactive;
using Avalonia.Utilities;
namespace Avalonia.Animation
{
@ -13,31 +14,56 @@ namespace Avalonia.Animation
internal class TransitionInstance : SingleSubscriberObservableBase<double>
{
private IDisposable _timerSubscription;
private TimeSpan _delay;
private TimeSpan _duration;
private readonly IClock _baseClock;
private IClock _clock;
public TransitionInstance(IClock clock, TimeSpan Duration)
public TransitionInstance(IClock clock, TimeSpan delay, TimeSpan duration)
{
clock = clock ?? throw new ArgumentNullException(nameof(clock));
_duration = Duration;
_delay = delay;
_duration = duration;
_baseClock = clock;
}
private void TimerTick(TimeSpan t)
{
var interpVal = _duration.Ticks == 0 ? 1d : (double)t.Ticks / _duration.Ticks;
// [<------------- normalizedTotalDur ------------------>]
// [<---- Delay ---->][<---------- Duration ------------>]
// ^- normalizedDelayEnd
// [<---- normalizedInterpVal --->]
var normalizedInterpVal = 1d;
if (!MathUtilities.AreClose(_duration.TotalSeconds, 0d))
{
var normalizedTotalDur = _delay + _duration;
var normalizedDelayEnd = _delay.TotalSeconds / normalizedTotalDur.TotalSeconds;
var normalizedPresentationTime = t.TotalSeconds / normalizedTotalDur.TotalSeconds;
if (normalizedPresentationTime < normalizedDelayEnd
|| MathUtilities.AreClose(normalizedPresentationTime, normalizedDelayEnd))
{
normalizedInterpVal = 0d;
}
else
{
normalizedInterpVal = (t.TotalSeconds - _delay.TotalSeconds) / _duration.TotalSeconds;
}
}
// Clamp interpolation value.
if (interpVal >= 1d | interpVal < 0d)
if (normalizedInterpVal >= 1d || normalizedInterpVal < 0d)
{
PublishNext(1d);
PublishCompleted();
}
else
{
PublishNext(interpVal);
PublishNext(normalizedInterpVal);
}
}

9
src/Avalonia.Animation/Transition`1.cs

@ -13,10 +13,15 @@ namespace Avalonia.Animation
private AvaloniaProperty _prop;
/// <summary>
/// Gets the duration of the animation.
/// Gets or sets the duration of the transition.
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets delay before starting the transition.
/// </summary>
public TimeSpan Delay { get; set; } = TimeSpan.Zero;
/// <summary>
/// Gets the easing class to be used.
/// </summary>
@ -47,7 +52,7 @@ namespace Avalonia.Animation
/// <inheritdocs/>
public virtual IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue)
{
var transition = DoTransition(new TransitionInstance(clock, Duration), (T)oldValue, (T)newValue);
var transition = DoTransition(new TransitionInstance(clock, Delay, Duration), (T)oldValue, (T)newValue);
return control.Bind<T>((AvaloniaProperty<T>)Property, transition, Data.BindingPriority.Animation);
}
}

12
src/Avalonia.Base/AvaloniaObject.cs

@ -421,8 +421,7 @@ namespace Avalonia
throw new ArgumentException($"The property {property.Name} is readonly.");
}
Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property,
Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
this,
"Bound {Property} to {Binding} with priority LocalValue",
property,
@ -501,8 +500,7 @@ namespace Avalonia
if (change.IsEffectiveValueChange)
{
Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property,
Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
this,
"{Property} changed from {$Old} to {$Value} with priority {Priority}",
property,
@ -586,8 +584,7 @@ namespace Avalonia
/// <param name="e">The binding error.</param>
protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e)
{
Logger.TryGet(LogEventLevel.Warning)?.Log(
LogArea.Binding,
Logger.TryGet(LogEventLevel.Warning, LogArea.Binding)?.Log(
this,
"Error in binding to {Target}.{Property}: {Message}",
this,
@ -857,8 +854,7 @@ namespace Avalonia
/// <param name="priority">The priority.</param>
private void LogPropertySet<T>(AvaloniaProperty<T> property, T value, BindingPriority priority)
{
Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property,
Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
this,
"Set {Property} to {$Value} with priority {Priority}",
property,

4
src/Avalonia.Base/AvaloniaProperty.cs

@ -159,8 +159,6 @@ namespace Avalonia
/// </summary>
internal int Id { get; }
internal bool HasChangedSubscriptions => _changed?.HasObservers ?? false;
/// <summary>
/// Provides access to a property's binding via the <see cref="AvaloniaObject"/>
/// indexer.
@ -512,7 +510,7 @@ namespace Avalonia
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
internal abstract IDisposable? RouteSetValue(
internal abstract IDisposable RouteSetValue(
IAvaloniaObject o,
object value,
BindingPriority priority);

4
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -362,7 +362,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TOwner, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
/// method.
/// </remarks>
public void Register(Type type, AvaloniaProperty property)
@ -413,7 +413,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{THost, TValue, TValue})"/>
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue})"/>
/// method.
/// </remarks>
public void RegisterAttached(Type type, AvaloniaProperty property)

1
src/Avalonia.Base/Collections/AvaloniaListExtensions.cs

@ -140,6 +140,7 @@ namespace Avalonia.Collections
}
}
[Obsolete("Causes memory leaks. Use DynamicData or similar instead.")]
public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select)

2
src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs

@ -13,9 +13,11 @@ namespace Avalonia.Collections.Pooled
public interface IReadOnlyPooledList<T> : IReadOnlyList<T>
{
#pragma warning disable CS0419
/// <summary>
/// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection.
/// </summary>
#pragma warning restore CS0419
ReadOnlySpan<T> Span { get; }
}
}

3
src/Avalonia.Base/Collections/Pooled/PooledList.cs

@ -138,7 +138,6 @@ namespace Avalonia.Collections.Pooled
/// initially empty, but will have room for the given number of elements
/// before any reallocations are required.
/// </summary>
/// <param name="sizeToCapacity">If true, Count of list equals capacity. Depending on ClearMode, rented items may or may not hold dirty values.</param>
public PooledList(int capacity, ClearMode clearMode, ArrayPool<T> customPool, bool sizeToCapacity)
{
if (capacity < 0)
@ -499,11 +498,13 @@ namespace Avalonia.Collections.Pooled
public void AddRange(T[] array)
=> AddRange(array.AsSpan());
#pragma warning disable CS0419
/// <summary>
/// Adds the elements of the given <see cref="ReadOnlySpan{T}"/> to the end of this list. If
/// required, the capacity of the list is increased to twice the previous
/// capacity or the new size, whichever is larger.
/// </summary>
#pragma warning restore CS0419
public void AddRange(ReadOnlySpan<T> span)
{
var newSpan = InsertSpan(_size, span.Length, false);

34
src/Avalonia.Base/Data/BindingValue.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Utilities;
#nullable enable
@ -81,14 +82,14 @@ namespace Avalonia.Data
/// </remarks>
public readonly struct BindingValue<T>
{
private readonly T _value;
[AllowNull] private readonly T _value;
/// <summary>
/// Initializes a new instance of the <see cref="BindingValue{T}"/> struct with a type of
/// <see cref="BindingValueType.Value"/>
/// </summary>
/// <param name="value">The value.</param>
public BindingValue(T value)
public BindingValue([AllowNull] T value)
{
ValidateValue(value);
_value = value;
@ -96,7 +97,7 @@ namespace Avalonia.Data
Error = null;
}
private BindingValue(BindingValueType type, T value, Exception? error)
private BindingValue(BindingValueType type, [AllowNull] T value, Exception? error)
{
_value = value;
Type = type;
@ -154,7 +155,7 @@ namespace Avalonia.Data
BindingValueType.UnsetValue => AvaloniaProperty.UnsetValue,
BindingValueType.DoNothing => BindingOperations.DoNothing,
BindingValueType.Value => _value,
BindingValueType.BindingError =>
BindingValueType.BindingError =>
new BindingNotification(Error, BindingErrorType.Error),
BindingValueType.BindingErrorWithFallback =>
new BindingNotification(Error, BindingErrorType.Error, Value),
@ -175,7 +176,7 @@ namespace Avalonia.Data
/// The binding type is <see cref="BindingValueType.UnsetValue"/> or
/// <see cref="BindingValueType.DoNothing"/>.
/// </exception>
public BindingValue<T> WithValue(T value)
public BindingValue<T> WithValue([AllowNull] T value)
{
if (Type == BindingValueType.DoNothing)
{
@ -190,6 +191,7 @@ namespace Avalonia.Data
/// Gets the value of the binding value if present, otherwise the default value.
/// </summary>
/// <returns>The value.</returns>
[return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary>
@ -206,6 +208,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>()
{
return HasValue ?
@ -222,7 +225,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present.
/// </returns>
public TResult GetValueOrDefault<TResult>(TResult defaultValue)
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@ -242,7 +246,7 @@ namespace Avalonia.Data
UnsetValueType _ => Unset,
DoNothingType _ => DoNothing,
BindingNotification n => n.ToBindingValue().Cast<T>(),
_ => (T)value
_ => new BindingValue<T>((T)value)
};
}
@ -250,7 +254,7 @@ namespace Avalonia.Data
/// Creates a binding value from an instance of the underlying value type.
/// </summary>
/// <param name="value">The value.</param>
public static implicit operator BindingValue<T>(T value) => new BindingValue<T>(value);
public static implicit operator BindingValue<T>([AllowNull] T value) => new BindingValue<T>(value);
/// <summary>
/// Creates a binding value from an <see cref="Optional{T}"/>.
@ -278,7 +282,7 @@ namespace Avalonia.Data
/// <param name="e">The binding error.</param>
public static BindingValue<T> BindingError(Exception e)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.BindingError, default, e);
}
@ -290,7 +294,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> BindingError(Exception e, T fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.BindingErrorWithFallback, fallbackValue, e);
}
@ -303,7 +307,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> BindingError(Exception e, Optional<T> fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(
fallbackValue.HasValue ?
@ -319,7 +323,7 @@ namespace Avalonia.Data
/// <param name="e">The data validation error.</param>
public static BindingValue<T> DataValidationError(Exception e)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.DataValidationError, default, e);
}
@ -331,7 +335,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> DataValidationError(Exception e, T fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(BindingValueType.DataValidationErrorWithFallback, fallbackValue, e);
}
@ -344,7 +348,7 @@ namespace Avalonia.Data
/// <param name="fallbackValue">The fallback value.</param>
public static BindingValue<T> DataValidationError(Exception e, Optional<T> fallbackValue)
{
e = e ?? throw new ArgumentNullException("e");
e = e ?? throw new ArgumentNullException(nameof(e));
return new BindingValue<T>(
fallbackValue.HasValue ?
@ -354,7 +358,7 @@ namespace Avalonia.Data
e);
}
private static void ValidateValue(T value)
private static void ValidateValue([AllowNull] T value)
{
if (value is UnsetValueType)
{

3
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -168,8 +168,7 @@ namespace Avalonia.Data.Core
}
else
{
Logger.TryGet(LogEventLevel.Error)?.Log(
LogArea.Binding,
Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log(
this,
"Could not convert FallbackValue {FallbackValue} to {Type}",
_fallbackValue,

36
src/Avalonia.Base/Data/Core/LogicalNotNode.cs

@ -12,8 +12,19 @@ namespace Avalonia.Data.Core
base.NextValueChanged(Negate(value));
}
private static object Negate(object v)
private static object Negate(object value)
{
var notification = value as BindingNotification;
var v = BindingNotification.ExtractValue(value);
BindingNotification GenerateError(Exception e)
{
notification ??= new BindingNotification(AvaloniaProperty.UnsetValue);
notification.AddError(e, BindingErrorType.Error);
notification.ClearValue();
return notification;
}
if (v != AvaloniaProperty.UnsetValue)
{
var s = v as string;
@ -28,9 +39,7 @@ namespace Avalonia.Data.Core
}
else
{
return new BindingNotification(
new InvalidCastException($"Unable to convert '{s}' to bool."),
BindingErrorType.Error);
return GenerateError(new InvalidCastException($"Unable to convert '{s}' to bool."));
}
}
else
@ -38,24 +47,31 @@ namespace Avalonia.Data.Core
try
{
var boolean = Convert.ToBoolean(v, CultureInfo.InvariantCulture);
return !boolean;
if (notification is object)
{
notification.SetValue(!boolean);
return notification;
}
else
{
return !boolean;
}
}
catch (InvalidCastException)
{
// The error message here is "Unable to cast object of type 'System.Object'
// to type 'System.IConvertible'" which is kinda useless so provide our own.
return new BindingNotification(
new InvalidCastException($"Unable to convert '{v}' to bool."),
BindingErrorType.Error);
return GenerateError(new InvalidCastException($"Unable to convert '{v}' to bool."));
}
catch (Exception e)
{
return new BindingNotification(e, BindingErrorType.Error);
return GenerateError(e);
}
}
}
return AvaloniaProperty.UnsetValue;
return notification ?? AvaloniaProperty.UnsetValue;
}
public object Transform(object value)

15
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -12,7 +12,7 @@ namespace Avalonia.Data.Core.Plugins
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{
/// <inheritdoc/>
public bool Match(object obj, string propertyName) => true;
public bool Match(object obj, string propertyName) => GetPropertyWithName(obj.GetType(), propertyName) != null;
/// <summary>
/// Starts monitoring the value of a property on an object.
@ -30,10 +30,7 @@ namespace Avalonia.Data.Core.Plugins
reference.TryGetTarget(out object instance);
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance;
var p = instance.GetType().GetProperty(propertyName, bindingFlags);
var p = GetPropertyWithName(instance.GetType(), propertyName);
if (p != null)
{
@ -47,6 +44,14 @@ namespace Avalonia.Data.Core.Plugins
}
}
private static PropertyInfo GetPropertyWithName(Type type, string propertyName)
{
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance;
return type.GetProperty(propertyName, bindingFlags);
}
private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>
{
private readonly WeakReference<object> _reference;

18
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core
@ -49,6 +50,18 @@ namespace Avalonia.Data.Core
var accessor = plugin?.Start(reference, PropertyName);
// We need to handle accessor fallback before handling validation. Validators do not support null accessors.
if (accessor == null)
{
reference.TryGetTarget(out object instance);
var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'";
var exception = new MissingMemberException(message);
accessor = new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
if (_enableValidation && Next == null)
{
foreach (var validator in ExpressionObserver.DataValidators)
@ -60,10 +73,9 @@ namespace Avalonia.Data.Core
}
}
if (accessor == null)
if (accessor is null)
{
throw new NotSupportedException(
$"Could not find a matching property accessor for {PropertyName}.");
throw new AvaloniaInternalException("Data validators must return non-null accessor.");
}
_accessor = accessor;

16
src/Avalonia.Base/Data/Optional.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#nullable enable
@ -22,13 +23,13 @@ namespace Avalonia.Data
/// </remarks>
public readonly struct Optional<T> : IEquatable<Optional<T>>
{
private readonly T _value;
[AllowNull] private readonly T _value;
/// <summary>
/// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
/// </summary>
/// <param name="value">The value.</param>
public Optional(T value)
public Optional([AllowNull] T value)
{
_value = value;
HasValue = true;
@ -48,7 +49,7 @@ namespace Avalonia.Data
public T Value => HasValue ? _value : throw new InvalidOperationException("Optional has no value.");
/// <inheritdoc/>
public override bool Equals(object obj) => obj is Optional<T> o && this == o;
public override bool Equals(object? obj) => obj is Optional<T> o && this == o;
/// <inheritdoc/>
public bool Equals(Optional<T> other) => this == other;
@ -69,6 +70,7 @@ namespace Avalonia.Data
/// Gets the value if present, otherwise the default value.
/// </summary>
/// <returns>The value.</returns>
[return: MaybeNull]
public T GetValueOrDefault() => HasValue ? _value : default;
/// <summary>
@ -85,6 +87,7 @@ namespace Avalonia.Data
/// The value if present and of the correct type, `default(TResult)` if the value is
/// not present or of an incorrect type.
/// </returns>
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>()
{
return HasValue ?
@ -101,7 +104,8 @@ namespace Avalonia.Data
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
/// value is not present.
/// </returns>
public TResult GetValueOrDefault<TResult>(TResult defaultValue)
[return: MaybeNull]
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
{
return HasValue ?
_value is TResult result ? result : default
@ -112,7 +116,7 @@ namespace Avalonia.Data
/// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type.
/// </summary>
/// <param name="value">The value.</param>
public static implicit operator Optional<T>(T value) => new Optional<T>(value);
public static implicit operator Optional<T>([AllowNull] T value) => new Optional<T>(value);
/// <summary>
/// Compares two <see cref="Optional{T}"/>s for inequality.
@ -128,7 +132,7 @@ namespace Avalonia.Data
/// <param name="x">The first value.</param>
/// <param name="y">The second value.</param>
/// <returns>True if the values are equal; otherwise false.</returns>
public static bool operator==(Optional<T> x, Optional<T> y)
public static bool operator ==(Optional<T> x, Optional<T> y)
{
if (!x.HasValue && !y.HasValue)
{

2
src/Avalonia.Base/DirectPropertyBase.cs

@ -120,7 +120,7 @@ namespace Avalonia
return o.GetValue<TValue>(this);
}
internal override object RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
internal override object? RouteGetBaseValue(IAvaloniaObject o, BindingPriority maxPriority)
{
return o.GetValue<TValue>(this);
}

2
src/Avalonia.Base/IStyledPropertyMetadata.cs

@ -1,5 +1,3 @@
using System;
namespace Avalonia
{
/// <summary>

179
src/Avalonia.Base/Logging/DebugLogSink.cs

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Avalonia.Utilities;
namespace Avalonia.Logging
{
public class DebugLogSink : ILogSink
{
private readonly LogEventLevel _level;
private readonly IList<string> _areas;
public DebugLogSink(
LogEventLevel minimumLevel,
IList<string> areas = null)
{
_level = minimumLevel;
_areas = areas?.Count > 0 ? areas : null;
}
public bool IsEnabled(LogEventLevel level, string area)
{
return level >= _level && (_areas?.Contains(area) ?? true);
}
public void Log(LogEventLevel level, string area, object source, string messageTemplate)
{
if (IsEnabled(level, area))
{
Debug.WriteLine(Format<object, object, object>(area, messageTemplate, source));
}
}
public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
{
if (IsEnabled(level, area))
{
Debug.WriteLine(Format<T0, object, object>(area, messageTemplate, source, propertyValue0));
}
}
public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
{
if (IsEnabled(level, area))
{
Debug.WriteLine(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1));
}
}
public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
{
if (IsEnabled(level, area))
{
Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2));
}
}
public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
{
if (IsEnabled(level, area))
{
Debug.WriteLine(Format(area, messageTemplate, source, propertyValues));
}
}
private static string Format<T0, T1, T2>(
string area,
string template,
object source,
T0 v0 = default,
T1 v1 = default,
T2 v2 = default)
{
var result = new StringBuilder(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
result.Append('[');
result.Append(area);
result.Append("] ");
while (!r.End)
{
var c = r.Take();
if (c != '{')
{
result.Append(c);
}
else
{
if (r.Peek != '{')
{
result.Append('\'');
result.Append(i++ switch
{
0 => v0,
1 => v1,
2 => v2,
_ => null
});
result.Append('\'');
r.TakeUntil('}');
r.Take();
}
else
{
result.Append('{');
r.Take();
}
}
}
if (source is object)
{
result.Append(" (");
result.Append(source.GetType().Name);
result.Append(" #");
result.Append(source.GetHashCode());
result.Append(')');
}
return result.ToString();
}
private static string Format(
string area,
string template,
object source,
object[] v)
{
var result = new StringBuilder(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
result.Append('[');
result.Append(area);
result.Append(']');
while (!r.End)
{
var c = r.Take();
if (c != '{')
{
result.Append(c);
}
else
{
if (r.Peek != '{')
{
result.Append('\'');
result.Append(i < v.Length ? v[i++] : null);
result.Append('\'');
r.TakeUntil('}');
r.Take();
}
else
{
result.Append('{');
r.Take();
}
}
}
if (source is object)
{
result.Append('(');
result.Append(source.GetType().Name);
result.Append(" #");
result.Append(source.GetHashCode());
result.Append(')');
}
return result.ToString();
}
}
}

5
src/Avalonia.Base/Logging/ILogSink.cs

@ -6,11 +6,12 @@ namespace Avalonia.Logging
public interface ILogSink
{
/// <summary>
/// Checks if given log level is enabled.
/// Checks if given log level and area is enabled.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The log area.</param>
/// <returns><see langword="true"/> if given log level is enabled.</returns>
bool IsEnabled(LogEventLevel level);
bool IsEnabled(LogEventLevel level, string area);
/// <summary>
/// Logs an event.

17
src/Avalonia.Base/Logging/Logger.cs

@ -14,36 +14,39 @@ namespace Avalonia.Logging
/// Checks if given log level is enabled.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The log area.</param>
/// <returns><see langword="true"/> if given log level is enabled.</returns>
public static bool IsEnabled(LogEventLevel level)
public static bool IsEnabled(LogEventLevel level, string area)
{
return Sink?.IsEnabled(level) == true;
return Sink?.IsEnabled(level, area) == true;
}
/// <summary>
/// Returns parametrized logging sink if given log level is enabled.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates from.</param>
/// <returns>Log sink or <see langword="null"/> if log level is not enabled.</returns>
public static ParametrizedLogger? TryGet(LogEventLevel level)
public static ParametrizedLogger? TryGet(LogEventLevel level, string area)
{
if (!IsEnabled(level))
if (!IsEnabled(level, area))
{
return null;
}
return new ParametrizedLogger(Sink, level);
return new ParametrizedLogger(Sink, level, area);
}
/// <summary>
/// Returns parametrized logging sink if given log level is enabled.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates from.</param>
/// <param name="outLogger">Log sink that is valid only if method returns <see langword="true"/>.</param>
/// <returns><see langword="true"/> if logger was obtained successfully.</returns>
public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
public static bool TryGet(LogEventLevel level, string area, out ParametrizedLogger outLogger)
{
ParametrizedLogger? logger = TryGet(level);
ParametrizedLogger? logger = TryGet(level, area);
outLogger = logger.GetValueOrDefault();

32
src/Avalonia.Base/Logging/ParametrizedLogger.cs

@ -9,11 +9,13 @@ namespace Avalonia.Logging
{
private readonly ILogSink _sink;
private readonly LogEventLevel _level;
private readonly string _area;
public ParametrizedLogger(ILogSink sink, LogEventLevel level)
public ParametrizedLogger(ILogSink sink, LogEventLevel level, string area)
{
_sink = sink;
_level = level;
_area = area;
}
/// <summary>
@ -24,58 +26,51 @@ namespace Avalonia.Logging
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(
string area,
object source,
string messageTemplate)
{
_sink.Log(_level, area, source, messageTemplate);
_sink.Log(_level, _area, source, messageTemplate);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0>(
string area,
object source,
string messageTemplate,
T0 propertyValue0)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0, propertyValue1);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
@ -83,20 +78,18 @@ namespace Avalonia.Logging
/// <param name="propertyValue2">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
@ -105,7 +98,6 @@ namespace Avalonia.Logging
/// <param name="propertyValue3">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
@ -113,13 +105,12 @@ namespace Avalonia.Logging
T2 propertyValue2,
T3 propertyValue3)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
@ -129,7 +120,6 @@ namespace Avalonia.Logging
/// <param name="propertyValue4">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3, T4>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
@ -138,13 +128,12 @@ namespace Avalonia.Logging
T3 propertyValue3,
T4 propertyValue4)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
@ -155,7 +144,6 @@ namespace Avalonia.Logging
/// <param name="propertyValue5">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3, T4, T5>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
@ -165,7 +153,7 @@ namespace Avalonia.Logging
T4 propertyValue4,
T5 propertyValue5)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4, propertyValue5);
_sink.Log(_level, _area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4, propertyValue5);
}
}
}

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

Loading…
Cancel
Save