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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Benchmarks", "tests\Avalonia.Benchmarks\Avalonia.Benchmarks.csproj", "{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Benchmarks", "tests\Avalonia.Benchmarks\Avalonia.Benchmarks.csproj", "{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport", "src\Avalonia.DesignerSupport\Avalonia.DesignerSupport.csproj", "{799A7BB5-3C2C-48B6-85A7-406A12C420DA}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{D0A739B9-3C68-4BA6-A328-41606954B6BD}" 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\ReactiveUI.props = build\ReactiveUI.props
build\Rx.props = build\Rx.props build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props build\SampleApp.props = build\SampleApp.props
build\Serilog.props = build\Serilog.props
build\SharpDX.props = build\SharpDX.props build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props build\SkiaSharp.props = build\SkiaSharp.props
build\System.Memory.props = build\System.Memory.props build\System.Memory.props = build\System.Memory.props
@ -206,14 +203,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src
EndProject 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}" 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 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5 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*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4 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\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*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13 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|iPhone.Build.0 = Release|Any CPU
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = 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 {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.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|Any CPU.Build.0 = Release|Any CPU
{799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = 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|iPhone.Build.0 = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = 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 {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.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|Any CPU.Build.0 = Debug|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = 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} {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} 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, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE 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.Input/Avalonia.Input.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Interactivity/Avalonia.Interactivity.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Interactivity/Avalonia.Interactivity.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Layout/Avalonia.Layout.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.Visuals/Avalonia.Visuals.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Styling/Avalonia.Styling.csproj" /> <ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Styling/Avalonia.Styling.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.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"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="Moq" Version="4.7.99" /> <PackageReference Include="Moq" Version="4.14.1" />
</ItemGroup> </ItemGroup>
</Project> </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\Moq.props" />
<Import Project="..\..\build\NetCore.props" /> <Import Project="..\..\build\NetCore.props" />
<Import Project="..\..\build\Rx.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\SharpDX.props" />
<Import Project="..\..\build\SkiaSharp.Desktop.props" /> <Import Project="..\..\build\SkiaSharp.Desktop.props" />
<Import Project="..\..\build\SkiaSharp.props" /> <Import Project="..\..\build\SkiaSharp.props" />
@ -22,4 +20,4 @@
```XML ```XML
<Import Project="..\..\build\UnitTests.NetCore.targets" /> <Import Project="..\..\build\UnitTests.NetCore.targets" />
``` ```

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

@ -26,7 +26,8 @@ struct IAvnStringArray;
struct IAvnDndResultCallback; struct IAvnDndResultCallback;
struct IAvnGCHandleDeallocatorCallback; struct IAvnGCHandleDeallocatorCallback;
struct IAvnMenuEvents; struct IAvnMenuEvents;
struct IAvnNativeControlHost;
struct IAvnNativeControlHostTopLevelAttachment;
enum SystemDecorations { enum SystemDecorations {
SystemDecorationsNone = 0, SystemDecorationsNone = 0,
SystemDecorationsBorderOnly = 1, SystemDecorationsBorderOnly = 1,
@ -256,6 +257,7 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0; virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandle(void** retOut) = 0; virtual HRESULT ObtainNSViewHandle(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0; virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) = 0;
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0; IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0;
virtual HRESULT SetBlurEnabled (bool enable) = 0; virtual HRESULT SetBlurEnabled (bool enable) = 0;
@ -276,6 +278,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
virtual HRESULT SetTitleBarColor (AvnColor color) = 0; virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0; virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0; virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
virtual HRESULT TakeFocusFromChildren() = 0;
}; };
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
@ -295,6 +298,7 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0; virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
virtual void ScalingChanged(double scaling) = 0; virtual void ScalingChanged(double scaling) = 0;
virtual void RunRenderPriorityJobs() = 0; virtual void RunRenderPriorityJobs() = 0;
virtual void LostFocus() = 0;
virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position, virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers, AvnDragDropEffects effects, AvnInputModifiers modifiers, AvnDragDropEffects effects,
IAvnClipboard* clipboard, void* dataObjectHandle) = 0; IAvnClipboard* clipboard, void* dataObjectHandle) = 0;
@ -478,4 +482,22 @@ AVNCOM(IAvnGCHandleDeallocatorCallback, 22) : IUnknown
virtual void FreeGCHandle(void* handle) = 0; 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(); 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 */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; }; 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 */; }; 1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; }; 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 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; }; 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>"; }; 1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
@ -86,11 +90,13 @@
AB7A61E62147C814003C5833 = { AB7A61E62147C814003C5833 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */, 1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */, 37DDA9B121933371002E132B /* AvnString.h */,
37DDA9AF219330F8002E132B /* AvnString.mm */, 37DDA9AF219330F8002E132B /* AvnString.mm */,
37A4E71A2178846A00EACBCD /* headers */, 37A4E71A2178846A00EACBCD /* headers */,
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */, 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */,
1AFD334023E03C4F0042899B /* controlhost.mm */,
5BF943652167AD1D009CAE35 /* cursor.h */, 5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */, 5B21A981216530F500CEE36E /* cursor.mm */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */, 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
@ -191,6 +197,7 @@
files = ( files = (
1A002B9E232135EE00021753 /* app.mm in Sources */, 1A002B9E232135EE00021753 /* app.mm in Sources */,
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */, 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */,
1A1852DC23E05814008F0DED /* deadlock.mm in Sources */,
5B21A982216530F500CEE36E /* cursor.mm in Sources */, 5B21A982216530F500CEE36E /* cursor.mm in Sources */,
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
@ -199,6 +206,7 @@
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */, 520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */,
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,
1A465D10246AB61600C5858B /* dnd.mm in Sources */, 1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,

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

@ -29,9 +29,43 @@ NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivati
@end @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() extern void InitializeAvnApp()
{ {
NSApplication* app = [NSApplication sharedApplication]; NSApplication* app = [AvnApplication sharedApplication];
id delegate = [AvnAppDelegate new]; id delegate = [AvnAppDelegate new];
[app setDelegate:delegate]; [app setDelegate:delegate];
} }

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

@ -24,6 +24,7 @@ extern IAvnGlDisplay* GetGlDisplay();
extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events); extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events);
extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItem();
extern IAvnMenuItem* CreateAppMenuItemSeperator(); extern IAvnMenuItem* CreateAppMenuItemSeperator();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu);
extern IAvnMenu* GetAppMenu (); extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem (); extern NSMenuItem* GetAppMenuItem ();
@ -54,4 +55,12 @@ template<typename T> inline T* objc_cast(id from) {
- (void) action; - (void) action;
@end @end
class AvnInsidePotentialDeadlock
{
public:
static bool IsInside();
AvnInsidePotentialDeadlock();
~AvnInsidePotentialDeadlock();
};
#endif #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); SetPosition(lastPositionSet);
UpdateStyle(); UpdateStyle();
if(ShouldTakeFocusOnShow())
[Window makeKeyAndOrderFront:Window]; {
[NSApp activateIgnoringOtherApps:YES]; [Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
}
else
{
[Window orderFront: Window];
}
[Window setTitle:_lastTitle]; [Window setTitle:_lastTitle];
_shown = true; _shown = true;
@ -128,6 +133,11 @@ public:
} }
} }
virtual bool ShouldTakeFocusOnShow()
{
return true;
}
virtual HRESULT Hide () override virtual HRESULT Hide () override
{ {
@autoreleasepool @autoreleasepool
@ -390,6 +400,14 @@ public:
return *ppv == nil ? E_FAIL : S_OK; 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 virtual HRESULT SetBlurEnabled (bool enable) override
{ {
[Window setContentView: enable ? VisualEffect : View]; [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 () void EnterFullScreenMode ()
{ {
_fullScreenActive = true; _fullScreenActive = true;
@ -1060,9 +1087,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
} }
- (void)updateLayer - (void)updateLayer
{ {
AvnInsidePotentialDeadlock deadlock;
if (_parent == nullptr) if (_parent == nullptr)
{ {
return; return;
@ -1107,7 +1134,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_lastPixelSize.Width = (int)fsize.width; _lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height; _lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget]; [self updateRenderTarget];
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
if(_parent != nullptr)
{
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
}
[super viewDidChangeBackingProperties]; [super viewDidChangeBackingProperties];
} }
@ -1138,7 +1169,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return; return;
} }
[self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
auto avnPoint = [self toAvnPoint:localPoint]; auto avnPoint = [self toAvnPoint:localPoint];
auto point = [self translateLocalPoint:avnPoint]; auto point = [self translateLocalPoint:avnPoint];
@ -1165,11 +1195,31 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
auto timestamp = [event timestamp] * 1000; auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]]; auto modifiers = [self getModifiers:[event modifierFlags]];
[self becomeFirstResponder]; if(type != AvnRawMouseEventType::Move ||
_parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); (
[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]; [super mouseMoved:event];
} }
- (BOOL) resignFirstResponder
{
_parent->BaseEvents->LostFocus();
return YES;
}
- (void)mouseMoved:(NSEvent *)event - (void)mouseMoved:(NSEvent *)event
{ {
[self mouseEvent:event withType:Move]; [self mouseEvent:event withType:Move];
@ -1290,7 +1340,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
auto timestamp = [event timestamp] * 1000; auto timestamp = [event timestamp] * 1000;
auto modifiers = [self getModifiers:[event modifierFlags]]; 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 - (BOOL)performKeyEquivalent:(NSEvent *)event
@ -1381,7 +1434,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{ {
if(!_lastKeyHandled) 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]; [self showWindowMenuWithAppMenu];
_parent->BaseEvents->Activated(); if(_parent != nullptr)
{
_parent->BaseEvents->Activated();
}
[super becomeKeyWindow]; [super becomeKeyWindow];
} }
} }
@ -1794,8 +1854,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)windowDidMove:(NSNotification *)notification - (void)windowDidMove:(NSNotification *)notification
{ {
AvnPoint position; AvnPoint position;
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position); if(_parent != nullptr)
{
_parent->GetPosition(&position);
_parent->BaseEvents->PositionChanged(position);
}
} }
@end @end
@ -1813,7 +1877,6 @@ private:
WindowEvents = events; WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel]; [Window setLevel:NSPopUpMenuWindowLevel];
} }
protected: protected:
virtual NSWindowStyleMask GetStyle() override virtual NSWindowStyleMask GetStyle() override
{ {
@ -1831,6 +1894,11 @@ protected:
return S_OK; return S_OK;
} }
} }
public:
virtual bool ShouldTakeFocusOnShow() override
{
return false;
}
}; };
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)

4
nukebuild/Build.cs

@ -101,7 +101,7 @@ partial class Build : NukeBuild
.SetProjectFile(projectFile) .SetProjectFile(projectFile)
// This is required for VS2019 image on Azure Pipelines // This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows && .When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, c => c Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64"))) .AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("PackageVersion", Parameters.Version) .AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true) .AddProperty("iOSRoslynPathHackRequired", true)
@ -176,7 +176,7 @@ partial class Build : NukeBuild
.SetFramework(fw) .SetFramework(fw)
.EnableNoBuild() .EnableNoBuild()
.EnableNoRestore() .EnableNoRestore()
.When(Parameters.PublishTestResults, c => c .When(Parameters.PublishTestResults, _ => _
.SetLogger("trx") .SetLogger("trx")
.SetResultsDirectory(Parameters.TestResultsRoot))); .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> <Pack>true</Pack>
<PackagePath>build\</PackagePath> <PackagePath>build\</PackagePath>
</Content> </Content>
<Content Include="AvaloniaItemSchema.xaml">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\SharedVersion.props" /> <Import Project="..\..\build\SharedVersion.props" />
<Import Project="..\..\build\NetFX.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"> <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> </Project>

22
packages/Avalonia/AvaloniaBuildTasks.targets

@ -4,6 +4,20 @@
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild> <_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
<AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance> <AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance>
</PropertyGroup> </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" <UsingTask TaskName="GenerateAvaloniaResourcesTask"
AssemblyFile="$(AvaloniaBuildTasksLocation)" AssemblyFile="$(AvaloniaBuildTasksLocation)"
@ -31,9 +45,12 @@
<Target Name="GenerateAvaloniaResources" <Target Name="GenerateAvaloniaResources"
BeforeTargets="CoreCompile;CoreResGen" BeforeTargets="CoreCompile;CoreResGen"
Inputs="@(AvaloniaResource);$(MSBuildAllProjects)" Inputs="@(AvaloniaResource);@(AvaloniaXaml);$(MSBuildAllProjects)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)" Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)"> DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)">
<ItemGroup>
<AvaloniaResource Include="@(AvaloniaXaml)"/>
</ItemGroup>
<GenerateAvaloniaResourcesTask <GenerateAvaloniaResourcesTask
Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'" Condition="'$(_AvaloniaUseExternalMSBuild)' != 'true'"
Output="$(AvaloniaResourcesTemporaryFilePath)" Output="$(AvaloniaResourcesTemporaryFilePath)"
@ -79,5 +96,6 @@
<ItemGroup> <ItemGroup>
<UpToDateCheckInput Include="@(AvaloniaResource)" /> <UpToDateCheckInput Include="@(AvaloniaResource)" />
<UpToDateCheckInput Include="@(AvaloniaXaml)" />
</ItemGroup> </ItemGroup>
</Project> </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 | ## 📖 About AvaloniaUI
|---|---|---|---|---|
| [![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 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. ## 🚀 Getting Started
[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
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). 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 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 ## 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. 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 ## 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> <a href="https://github.com/AvaloniaUI/Avalonia/graphs/contributors"><img src="https://opencollective.com/Avalonia/contributors.svg?width=890&button=false" /></a>
### Backers ### Backers

2
samples/BindingDemo/App.xaml.cs

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

1
samples/BindingDemo/BindingDemo.csproj

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

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

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

2
samples/ControlCatalog.Desktop/Program.cs

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

7
samples/ControlCatalog.NetCore/Program.cs

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

1
samples/ControlCatalog/ControlCatalog.csproj

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

9
samples/ControlCatalog/MainView.xaml

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

6
samples/ControlCatalog/Models/Person.cs

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

7
samples/ControlCatalog/Pages/BorderPage.xaml

@ -29,6 +29,13 @@
Padding="16"> Padding="16">
<TextBlock>Rounded Corners</TextBlock> <TextBlock>Rounded Corners</TextBlock>
</Border> </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>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

4
samples/ControlCatalog/Pages/ButtonPage.xaml

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

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

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

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

@ -4,17 +4,17 @@ using System;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
public class DatePickerPage : UserControl public class CalendarDatePickerPage : UserControl
{ {
public DatePickerPage() public CalendarDatePickerPage()
{ {
InitializeComponent(); InitializeComponent();
var dp1 = this.FindControl<DatePicker>("DatePicker1"); var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
var dp2 = this.FindControl<DatePicker>("DatePicker2"); var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
var dp3 = this.FindControl<DatePicker>("DatePicker3"); var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
var dp4 = this.FindControl<DatePicker>("DatePicker4"); var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
var dp5 = this.FindControl<DatePicker>("DatePicker5"); var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today; dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10); 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="DecoratedWindowDialog">Decorated window (dialog)</Button>
<Button Name="Dialog">Dialog</Button> <Button Name="Dialog">Dialog</Button>
<Button Name="DialogNoTaskbar">Dialog (No taskbar icon)</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> </StackPanel>
</UserControl> </UserControl>

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

@ -93,6 +93,21 @@ namespace ControlCatalog.Pages
window.ShowInTaskbar = false; window.ShowInTaskbar = false;
window.ShowDialog(GetWindow()); 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() private Window CreateSampleWindow()

7
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

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

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

@ -5,23 +5,32 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.VisualTree;
using ControlCatalog.ViewModels; using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
public class ItemsRepeaterPage : UserControl public class ItemsRepeaterPage : UserControl
{ {
private readonly ItemsRepeaterPageViewModel _viewModel;
private ItemsRepeater _repeater; private ItemsRepeater _repeater;
private ScrollViewer _scroller; private ScrollViewer _scroller;
private Button _scrollToLast;
private Button _scrollToRandom;
private Random _random = new Random(0);
public ItemsRepeaterPage() public ItemsRepeaterPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
_repeater = this.FindControl<ItemsRepeater>("repeater"); _repeater = this.FindControl<ItemsRepeater>("repeater");
_scroller = this.FindControl<ScrollViewer>("scroller"); _scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom");
_repeater.PointerPressed += RepeaterClick; _repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown; _repeater.KeyDown += RepeaterOnKeyDown;
DataContext = new ItemsRepeaterPageViewModel(); _scrollToLast.Click += scrollToLast_Click;
_scrollToRandom.Click += scrollToRandom_Click;
DataContext = _viewModel = new ItemsRepeaterPageViewModel();
} }
private void InitializeComponent() 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) private void RepeaterClick(object sender, PointerPressedEventArgs e)
{ {
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item; var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item; _viewModel.SelectedItem = item;
} }
private void RepeaterOnKeyDown(object sender, KeyEventArgs e) private void RepeaterOnKeyDown(object sender, KeyEventArgs e)
{ {
if (e.Key == Key.F5) 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> <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" /> <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding #upDown.Text}" VerticalAlignment="Center" Margin="2" />
</Grid> </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> <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}" <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> <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}" <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> <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" <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> <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" <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>
</Grid> </Grid>
@ -73,7 +73,7 @@
<StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10"> <StackPanel Margin="2,10,2,2" Orientation="Horizontal" Spacing="10">
<TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock> <TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center">Usage of NumericUpDown:</TextBlock>
<NumericUpDown Name="upDown" Minimum="0" Maximum="10" Increment="0.5" <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}"/> Watermark="Enter text" FormatString="{Binding SelectedFormat.Value}"/>
</StackPanel> </StackPanel>

26
samples/ControlCatalog/Pages/ProgressBarPage.xaml

@ -1,29 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.Pages.ProgressBarPage">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ProgressBarPage">
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">ProgressBar</TextBlock> <TextBlock Classes="h1">ProgressBar</TextBlock>
<TextBlock Classes="h2">A progress bar control</TextBlock> <TextBlock Classes="h2">A progress bar control</TextBlock>
<StackPanel> <StackPanel>
<CheckBox <CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" />
x:Name="showProgress" <CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" />
Margin="10,16,0,0" <StackPanel Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16">
Content="Show Progress Text" />
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<StackPanel Spacing="16"> <StackPanel Spacing="16">
<ProgressBar ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" /> <ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #hprogress.Value}" />
<ProgressBar IsIndeterminate="True"/>
</StackPanel> </StackPanel>
<ProgressBar ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" /> <ProgressBar IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="{Binding #vprogress.Value}" Orientation="Vertical" />
<ProgressBar Orientation="Vertical" IsIndeterminate="True" />
</StackPanel> </StackPanel>
<StackPanel Margin="16"> <StackPanel Margin="16">
<Slider Name="hprogress" Maximum="100" Value="40"/> <Slider Name="hprogress" Maximum="100" Value="40" />
<Slider Name="vprogress" Maximum="100" Value="60"/> <Slider Name="vprogress" Maximum="100" Value="60" />
</StackPanel> </StackPanel>
</StackPanel> </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> <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"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 16 0 0" Spacing="16">
<Slider Value="0" <StackPanel Orientation="Vertical"
Minimum="0" HorizontalAlignment="Center">
Maximum="100" <Slider Value="0"
TickFrequency="10" Minimum="0"
Width="300"/> 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" <Slider Value="0"
Minimum="0" Minimum="0"
Maximum="100" 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"> ToolTip.Tip="This is a ToolTip">
<TextBlock>Hover Here</TextBlock> <TextBlock>Hover Here</TextBlock>
</Border> </Border>
<CheckBox Grid.Column="1" <ToggleSwitch Grid.Column="1"
Margin="5" Margin="5"
Grid.Row="0" Grid.Row="0"
IsChecked="{Binding ElementName=Border, Path=(ToolTip.IsOpen)}" 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.HorizontalScrollBarVisibility)" Value="Auto"/>
<Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Auto"/> <Setter Property="(ScrollViewer.VerticalScrollBarVisibility)" Value="Auto"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover"> <Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>

13
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Avalonia.Media;
using ReactiveUI; using ReactiveUI;
namespace ControlCatalog.ViewModels namespace ControlCatalog.ViewModels
@ -27,7 +28,7 @@ namespace ControlCatalog.ViewModels
public void AddItem() public void AddItem()
{ {
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1; 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() public void RandomizeHeights()
@ -52,7 +53,7 @@ namespace ControlCatalog.ViewModels
_newGenerationIndex++; _newGenerationIndex++;
return new ObservableCollection<Item>( 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}" Text = $"Item {i.ToString()} {suffix}"
})); }));
@ -61,6 +62,12 @@ namespace ControlCatalog.ViewModels
public class Item : ReactiveObject public class Item : ReactiveObject
{ {
private double _height = double.NaN; private double _height = double.NaN;
private int _index;
public Item(int index)
{
_index = index;
}
public string Text { get; set; } public string Text { get; set; }
@ -69,6 +76,8 @@ namespace ControlCatalog.ViewModels
get => _height; get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value); 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;
using Avalonia.Logging.Serilog;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
@ -22,6 +21,5 @@ namespace RenderDemo
.UsePlatformDetect() .UsePlatformDetect()
.UseReactiveUI() .UseReactiveUI()
.LogToDebug(); .LogToDebug();
} }
} }

3
samples/RenderDemo/MainWindow.xaml

@ -29,6 +29,9 @@
<TabItem Header="Animations"> <TabItem Header="Animations">
<pages:AnimationsPage/> <pages:AnimationsPage/>
</TabItem> </TabItem>
<TabItem Header="Transitions">
<pages:TransitionsPage/>
</TabItem>
<TabItem Header="Clipping"> <TabItem Header="Clipping">
<pages:ClippingPage/> <pages:ClippingPage/>
</TabItem> </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> </ItemGroup>
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\EmbedXaml.props" /> <Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" /> <Import Project="..\..\build\ReactiveUI.props" />
<Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" /> <Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.props" />

2
samples/VirtualizationDemo/Program.cs

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

1
samples/VirtualizationDemo/VirtualizationDemo.csproj

@ -10,7 +10,6 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\EmbedXaml.props" /> <Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Serilog.props" />
<Import Project="..\..\build\Rx.props" /> <Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" /> <Import Project="..\..\build\ReactiveUI.props" />
<Import Condition="'$(TargetFramework)'=='net461'" Project="..\..\build\NetFX.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.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" /> <ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Serilog.props" />
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\ReferenceCoreLibraries.props" /> <Import Project="..\..\..\build\ReferenceCoreLibraries.props" />
</Project> </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(); throw new NotSupportedException();
} }
public IEmbeddableWindowImpl CreateEmbeddableWindow() public IWindowImpl CreateEmbeddableWindow()
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }

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

@ -33,10 +33,8 @@ namespace Avalonia.Android
return _view.View.DispatchKeyEvent(e); return _view.View.DispatchKeyEvent(e);
} }
class ViewImpl : TopLevelImpl, IEmbeddableWindowImpl class ViewImpl : TopLevelImpl
{ {
public event Action LostFocus;
public ViewImpl(Context context) : base(context) public ViewImpl(Context context) : base(context)
{ {
View.Focusable = true; 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 IPopupImpl CreatePopup() => null;
public Action LostFocus { get; set; }
ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface); ILockedFramebuffer IFramebufferPlatformSurface.Lock()=>new AndroidFramebuffer(_view.Holder.Surface);
} }

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

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

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

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
namespace Avalonia.Animation.Easings namespace Avalonia.Animation.Easings
@ -25,6 +26,11 @@ namespace Avalonia.Animation.Easings
/// <returns>Returns the instance of the parsed type.</returns> /// <returns>Returns the instance of the parsed type.</returns>
public static Easing Parse(string e) public static Easing Parse(string e)
{ {
if (e.Contains(','))
{
return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture));
}
if (_easingTypes == null) if (_easingTypes == null)
{ {
_easingTypes = new Dictionary<string, Type>(); _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> /// <returns>A <see cref="KeySpline"/> with the appropriate values set</returns>
public static KeySpline Parse(string value, CultureInfo culture) 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()); return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble());
} }
@ -98,6 +101,7 @@ namespace Avalonia.Animation
if (IsValidXValue(value)) if (IsValidXValue(value))
{ {
_controlPointX1 = value; _controlPointX1 = value;
_isDirty = true;
} }
else else
{ {
@ -112,7 +116,11 @@ namespace Avalonia.Animation
public double ControlPointY1 public double ControlPointY1
{ {
get => _controlPointY1; get => _controlPointY1;
set => _controlPointY1 = value; set
{
_controlPointY1 = value;
_isDirty = true;
}
} }
/// <summary> /// <summary>
@ -126,6 +134,7 @@ namespace Avalonia.Animation
if (IsValidXValue(value)) if (IsValidXValue(value))
{ {
_controlPointX2 = value; _controlPointX2 = value;
_isDirty = true;
} }
else else
{ {
@ -140,7 +149,11 @@ namespace Avalonia.Animation
public double ControlPointY2 public double ControlPointY2
{ {
get => _controlPointY2; get => _controlPointY2;
set => _controlPointY2 = value; set
{
_controlPointY2 = value;
_isDirty = true;
}
} }
/// <summary> /// <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.Easings;
using Avalonia.Animation.Utils; using Avalonia.Animation.Utils;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Utilities;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
@ -13,31 +14,56 @@ namespace Avalonia.Animation
internal class TransitionInstance : SingleSubscriberObservableBase<double> internal class TransitionInstance : SingleSubscriberObservableBase<double>
{ {
private IDisposable _timerSubscription; private IDisposable _timerSubscription;
private TimeSpan _delay;
private TimeSpan _duration; private TimeSpan _duration;
private readonly IClock _baseClock; private readonly IClock _baseClock;
private IClock _clock; private IClock _clock;
public TransitionInstance(IClock clock, TimeSpan Duration) public TransitionInstance(IClock clock, TimeSpan delay, TimeSpan duration)
{ {
clock = clock ?? throw new ArgumentNullException(nameof(clock)); clock = clock ?? throw new ArgumentNullException(nameof(clock));
_duration = Duration; _delay = delay;
_duration = duration;
_baseClock = clock; _baseClock = clock;
} }
private void TimerTick(TimeSpan t) 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. // Clamp interpolation value.
if (interpVal >= 1d | interpVal < 0d) if (normalizedInterpVal >= 1d || normalizedInterpVal < 0d)
{ {
PublishNext(1d); PublishNext(1d);
PublishCompleted(); PublishCompleted();
} }
else else
{ {
PublishNext(interpVal); PublishNext(normalizedInterpVal);
} }
} }

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

@ -13,10 +13,15 @@ namespace Avalonia.Animation
private AvaloniaProperty _prop; private AvaloniaProperty _prop;
/// <summary> /// <summary>
/// Gets the duration of the animation. /// Gets or sets the duration of the transition.
/// </summary> /// </summary>
public TimeSpan Duration { get; set; } public TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets delay before starting the transition.
/// </summary>
public TimeSpan Delay { get; set; } = TimeSpan.Zero;
/// <summary> /// <summary>
/// Gets the easing class to be used. /// Gets the easing class to be used.
/// </summary> /// </summary>
@ -47,7 +52,7 @@ namespace Avalonia.Animation
/// <inheritdocs/> /// <inheritdocs/>
public virtual IDisposable Apply(Animatable control, IClock clock, object oldValue, object newValue) 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); 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."); throw new ArgumentException($"The property {property.Name} is readonly.");
} }
Logger.TryGet(LogEventLevel.Verbose)?.Log( Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
LogArea.Property,
this, this,
"Bound {Property} to {Binding} with priority LocalValue", "Bound {Property} to {Binding} with priority LocalValue",
property, property,
@ -501,8 +500,7 @@ namespace Avalonia
if (change.IsEffectiveValueChange) if (change.IsEffectiveValueChange)
{ {
Logger.TryGet(LogEventLevel.Verbose)?.Log( Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
LogArea.Property,
this, this,
"{Property} changed from {$Old} to {$Value} with priority {Priority}", "{Property} changed from {$Old} to {$Value} with priority {Priority}",
property, property,
@ -586,8 +584,7 @@ namespace Avalonia
/// <param name="e">The binding error.</param> /// <param name="e">The binding error.</param>
protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e) protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e)
{ {
Logger.TryGet(LogEventLevel.Warning)?.Log( Logger.TryGet(LogEventLevel.Warning, LogArea.Binding)?.Log(
LogArea.Binding,
this, this,
"Error in binding to {Target}.{Property}: {Message}", "Error in binding to {Target}.{Property}: {Message}",
this, this,
@ -857,8 +854,7 @@ namespace Avalonia
/// <param name="priority">The priority.</param> /// <param name="priority">The priority.</param>
private void LogPropertySet<T>(AvaloniaProperty<T> property, T value, BindingPriority priority) private void LogPropertySet<T>(AvaloniaProperty<T> property, T value, BindingPriority priority)
{ {
Logger.TryGet(LogEventLevel.Verbose)?.Log( Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log(
LogArea.Property,
this, this,
"Set {Property} to {$Value} with priority {Priority}", "Set {Property} to {$Value} with priority {Priority}",
property, property,

4
src/Avalonia.Base/AvaloniaProperty.cs

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

4
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -362,7 +362,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <remarks> /// <remarks>
/// You won't usually want to call this method directly, instead use the /// 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. /// method.
/// </remarks> /// </remarks>
public void Register(Type type, AvaloniaProperty property) public void Register(Type type, AvaloniaProperty property)
@ -413,7 +413,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <remarks> /// <remarks>
/// You won't usually want to call this method directly, instead use the /// 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. /// method.
/// </remarks> /// </remarks>
public void RegisterAttached(Type type, AvaloniaProperty property) 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>( public static IAvaloniaReadOnlyList<TDerived> CreateDerivedList<TSource, TDerived>(
this IAvaloniaReadOnlyList<TSource> collection, this IAvaloniaReadOnlyList<TSource> collection,
Func<TSource, TDerived> select) 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> public interface IReadOnlyPooledList<T> : IReadOnlyList<T>
{ {
#pragma warning disable CS0419
/// <summary> /// <summary>
/// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection. /// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection.
/// </summary> /// </summary>
#pragma warning restore CS0419
ReadOnlySpan<T> Span { get; } 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 /// initially empty, but will have room for the given number of elements
/// before any reallocations are required. /// before any reallocations are required.
/// </summary> /// </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) public PooledList(int capacity, ClearMode clearMode, ArrayPool<T> customPool, bool sizeToCapacity)
{ {
if (capacity < 0) if (capacity < 0)
@ -499,11 +498,13 @@ namespace Avalonia.Collections.Pooled
public void AddRange(T[] array) public void AddRange(T[] array)
=> AddRange(array.AsSpan()); => AddRange(array.AsSpan());
#pragma warning disable CS0419
/// <summary> /// <summary>
/// Adds the elements of the given <see cref="ReadOnlySpan{T}"/> to the end of this list. If /// 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 /// required, the capacity of the list is increased to twice the previous
/// capacity or the new size, whichever is larger. /// capacity or the new size, whichever is larger.
/// </summary> /// </summary>
#pragma warning restore CS0419
public void AddRange(ReadOnlySpan<T> span) public void AddRange(ReadOnlySpan<T> span)
{ {
var newSpan = InsertSpan(_size, span.Length, false); var newSpan = InsertSpan(_size, span.Length, false);

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

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

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

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

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

@ -12,8 +12,19 @@ namespace Avalonia.Data.Core
base.NextValueChanged(Negate(value)); 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) if (v != AvaloniaProperty.UnsetValue)
{ {
var s = v as string; var s = v as string;
@ -28,9 +39,7 @@ namespace Avalonia.Data.Core
} }
else else
{ {
return new BindingNotification( return GenerateError(new InvalidCastException($"Unable to convert '{s}' to bool."));
new InvalidCastException($"Unable to convert '{s}' to bool."),
BindingErrorType.Error);
} }
} }
else else
@ -38,24 +47,31 @@ namespace Avalonia.Data.Core
try try
{ {
var boolean = Convert.ToBoolean(v, CultureInfo.InvariantCulture); var boolean = Convert.ToBoolean(v, CultureInfo.InvariantCulture);
return !boolean;
if (notification is object)
{
notification.SetValue(!boolean);
return notification;
}
else
{
return !boolean;
}
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
// The error message here is "Unable to cast object of type 'System.Object' // 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. // to type 'System.IConvertible'" which is kinda useless so provide our own.
return new BindingNotification( return GenerateError(new InvalidCastException($"Unable to convert '{v}' to bool."));
new InvalidCastException($"Unable to convert '{v}' to bool."),
BindingErrorType.Error);
} }
catch (Exception e) catch (Exception e)
{ {
return new BindingNotification(e, BindingErrorType.Error); return GenerateError(e);
} }
} }
} }
return AvaloniaProperty.UnsetValue; return notification ?? AvaloniaProperty.UnsetValue;
} }
public object Transform(object value) 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 public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Match(object obj, string propertyName) => true; public bool Match(object obj, string propertyName) => GetPropertyWithName(obj.GetType(), propertyName) != null;
/// <summary> /// <summary>
/// Starts monitoring the value of a property on an object. /// Starts monitoring the value of a property on an object.
@ -30,10 +30,7 @@ namespace Avalonia.Data.Core.Plugins
reference.TryGetTarget(out object instance); reference.TryGetTarget(out object instance);
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | var p = GetPropertyWithName(instance.GetType(), propertyName);
BindingFlags.Static | BindingFlags.Instance;
var p = instance.GetType().GetProperty(propertyName, bindingFlags);
if (p != null) 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 class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>
{ {
private readonly WeakReference<object> _reference; private readonly WeakReference<object> _reference;

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

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
@ -49,6 +50,18 @@ namespace Avalonia.Data.Core
var accessor = plugin?.Start(reference, PropertyName); 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) if (_enableValidation && Next == null)
{ {
foreach (var validator in ExpressionObserver.DataValidators) foreach (var validator in ExpressionObserver.DataValidators)
@ -60,10 +73,9 @@ namespace Avalonia.Data.Core
} }
} }
if (accessor == null) if (accessor is null)
{ {
throw new NotSupportedException( throw new AvaloniaInternalException("Data validators must return non-null accessor.");
$"Could not find a matching property accessor for {PropertyName}.");
} }
_accessor = accessor; _accessor = accessor;

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

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

2
src/Avalonia.Base/DirectPropertyBase.cs

@ -120,7 +120,7 @@ namespace Avalonia
return o.GetValue<TValue>(this); 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); return o.GetValue<TValue>(this);
} }

2
src/Avalonia.Base/IStyledPropertyMetadata.cs

@ -1,5 +1,3 @@
using System;
namespace Avalonia namespace Avalonia
{ {
/// <summary> /// <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 public interface ILogSink
{ {
/// <summary> /// <summary>
/// Checks if given log level is enabled. /// Checks if given log level and area is enabled.
/// </summary> /// </summary>
/// <param name="level">The log event level.</param> /// <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> /// <returns><see langword="true"/> if given log level is enabled.</returns>
bool IsEnabled(LogEventLevel level); bool IsEnabled(LogEventLevel level, string area);
/// <summary> /// <summary>
/// Logs an event. /// Logs an event.

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

@ -14,36 +14,39 @@ namespace Avalonia.Logging
/// Checks if given log level is enabled. /// Checks if given log level is enabled.
/// </summary> /// </summary>
/// <param name="level">The log event level.</param> /// <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> /// <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> /// <summary>
/// Returns parametrized logging sink if given log level is enabled. /// Returns parametrized logging sink if given log level is enabled.
/// </summary> /// </summary>
/// <param name="level">The log event level.</param> /// <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> /// <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 null;
} }
return new ParametrizedLogger(Sink, level); return new ParametrizedLogger(Sink, level, area);
} }
/// <summary> /// <summary>
/// Returns parametrized logging sink if given log level is enabled. /// Returns parametrized logging sink if given log level is enabled.
/// </summary> /// </summary>
/// <param name="level">The log event level.</param> /// <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> /// <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> /// <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(); outLogger = logger.GetValueOrDefault();

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

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