Browse Source

Merge branch 'master' into fixes/DevTools/WithoutApplicationLifetime

pull/10510/head
Max Katz 3 years ago
committed by GitHub
parent
commit
506d05bac2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      .editorconfig
  2. 6
      Avalonia.Desktop.slnf
  3. 24
      Avalonia.sln
  4. 2
      build/DevAnalyzers.props
  5. 32
      build/ExternalConsumers.props
  6. 6
      native/Avalonia.Native/src/OSX/AvnView.mm
  7. 2
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  8. 1
      nukebuild/Build.cs
  9. 24
      nukebuild/RefAssemblyGenerator.cs
  10. 5
      nukebuild/numerge.config
  11. 21
      packages/Avalonia/Avalonia.csproj
  12. 2
      packages/Avalonia/Avalonia.props
  13. 63
      readme.md
  14. 2
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  15. 6
      samples/ControlCatalog.NetCore/Properties/launchSettings.json
  16. 2
      samples/ControlCatalog/App.xaml
  17. 48
      samples/ControlCatalog/MainView.xaml.cs
  18. 11
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  19. 1
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  20. 2
      samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj
  21. 4
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  22. 2
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  23. 10
      samples/Sandbox/MainWindow.axaml.cs
  24. 2
      samples/Sandbox/Sandbox.csproj
  25. 6
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  26. 12
      src/Avalonia.Base/Animation/Animation.cs
  27. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  28. 2
      src/Avalonia.Base/ClassBindingManager.cs
  29. 2
      src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
  30. 2
      src/Avalonia.Base/Controls/IResourceDictionary.cs
  31. 21
      src/Avalonia.Base/Controls/IThemeVariantProvider.cs
  32. 12
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  33. 46
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  34. 3
      src/Avalonia.Base/Data/BindingPriority.cs
  35. 3
      src/Avalonia.Base/Data/InstancedBinding.cs
  36. 2
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  37. 13
      src/Avalonia.Base/Input/Cursor.cs
  38. 3
      src/Avalonia.Base/Input/DataFormats.cs
  39. 3
      src/Avalonia.Base/Input/DataObjectExtensions.cs
  40. 3
      src/Avalonia.Base/Input/DragEventArgs.cs
  41. 3
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  42. 16
      src/Avalonia.Base/Input/PointerEventArgs.cs
  43. 3
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  44. 10
      src/Avalonia.Base/Layout/LayoutManager.cs
  45. 18
      src/Avalonia.Base/Layout/LayoutQueue.cs
  46. 40
      src/Avalonia.Base/Layout/Layoutable.cs
  47. 14
      src/Avalonia.Base/Media/Color.cs
  48. 2
      src/Avalonia.Base/Media/DashStyle.cs
  49. 7
      src/Avalonia.Base/Media/DrawingContext.cs
  50. 2
      src/Avalonia.Base/Media/GeometryDrawing.cs
  51. 2
      src/Avalonia.Base/Media/GradientBrush.cs
  52. 36
      src/Avalonia.Base/Media/HslColor.cs
  53. 1
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  54. 16
      src/Avalonia.Base/Media/PlatformDrawingContext.cs
  55. 2
      src/Avalonia.Base/Media/PolyLineSegment.cs
  56. 4
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  57. 33
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  58. 2
      src/Avalonia.Base/Media/TransformGroup.cs
  59. 23
      src/Avalonia.Base/Metadata/AvaloniaListAttribute.cs
  60. 22
      src/Avalonia.Base/Metadata/UnstableAttribute.cs
  61. 295
      src/Avalonia.Base/Platform/AssetLoader.cs
  62. 2
      src/Avalonia.Base/Platform/IAssetLoader.cs
  63. 7
      src/Avalonia.Base/Platform/IDrawingContextImpl.cs
  64. 255
      src/Avalonia.Base/Platform/StandardAssetLoader.cs
  65. 2
      src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs
  66. 27
      src/Avalonia.Base/Platform/Storage/NoopStorageProvider.cs
  67. 5
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  68. 14
      src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs
  69. 1
      src/Avalonia.Base/StyledElement.cs
  70. 1
      src/Avalonia.Base/Styling/IThemeVariantHost.cs
  71. 4
      src/Avalonia.Base/Styling/ThemeVariant.cs
  72. 89
      src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
  73. 57
      src/Avalonia.Base/Threading/DispatcherFrame.cs
  74. 3
      src/Avalonia.Base/Threading/DispatcherPriority.cs
  75. 5
      src/Avalonia.Base/Utilities/SpanHelpers.cs
  76. 1
      src/Avalonia.Base/Visual.cs
  77. 5
      src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
  78. 1
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  79. 26
      src/Avalonia.Build.Tasks/SpanCompat.cs
  80. 39
      src/Avalonia.Controls/AppBuilder.cs
  81. 2
      src/Avalonia.Controls/Application.cs
  82. 2
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  83. 2
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
  84. 6
      src/Avalonia.Controls/Avalonia.Controls.csproj
  85. 2
      src/Avalonia.Controls/ColumnDefinition.cs
  86. 6
      src/Avalonia.Controls/ContextMenu.cs
  87. 12
      src/Avalonia.Controls/DefinitionBase.cs
  88. 2
      src/Avalonia.Controls/DefinitionList.cs
  89. 2
      src/Avalonia.Controls/Documents/Span.cs
  90. 4
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  91. 2
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
  92. 48
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  93. 7
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  94. 3
      src/Avalonia.Controls/ItemsControl.cs
  95. 4
      src/Avalonia.Controls/LayoutTransformControl.cs
  96. 8
      src/Avalonia.Controls/ListBox.cs
  97. 6
      src/Avalonia.Controls/MenuItem.cs
  98. 3
      src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
  99. 3
      src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
  100. 36
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs

17
.editorconfig

@ -186,6 +186,23 @@ csharp_wrap_before_ternary_opsigns = false
# Avalonia DevAnalyzer preferences # Avalonia DevAnalyzer preferences
dotnet_diagnostic.AVADEV2001.severity = error dotnet_diagnostic.AVADEV2001.severity = error
# Avalonia PublicAnalyzer preferences
dotnet_diagnostic.AVP1000.severity = error
dotnet_diagnostic.AVP1001.severity = error
dotnet_diagnostic.AVP1002.severity = error
dotnet_diagnostic.AVP1010.severity = error
dotnet_diagnostic.AVP1011.severity = error
dotnet_diagnostic.AVP1012.severity = warning
dotnet_diagnostic.AVP1013.severity = error
dotnet_diagnostic.AVP1020.severity = error
dotnet_diagnostic.AVP1021.severity = error
dotnet_diagnostic.AVP1022.severity = error
dotnet_diagnostic.AVP1030.severity = error
dotnet_diagnostic.AVP1031.severity = error
dotnet_diagnostic.AVP1032.severity = error
dotnet_diagnostic.AVP1040.severity = error
dotnet_diagnostic.AVA2001.severity = error
# Xaml files # Xaml files
[*.{xaml,axaml}] [*.{xaml,axaml}]
indent_size = 2 indent_size = 2

6
Avalonia.Desktop.slnf

@ -24,8 +24,6 @@
"src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj", "src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
"src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj", "src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
"src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj", "src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
"src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj", "src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
"src\\Avalonia.Native\\Avalonia.Native.csproj", "src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj", "src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
@ -34,6 +32,8 @@
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj", "src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
"src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj", "src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
"src\\Avalonia.X11\\Avalonia.X11.csproj", "src\\Avalonia.X11\\Avalonia.X11.csproj",
"src\\Headless\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Headless\\Avalonia.Headless\\Avalonia.Headless.csproj",
"src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj", "src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
"src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj", "src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj", "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
@ -66,4 +66,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj" "tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
] ]
} }
} }

24
Avalonia.sln

@ -181,9 +181,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.
EndProject 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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Headless\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject EndProject
@ -233,7 +233,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
@ -261,6 +261,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Desktop", "sam
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -600,6 +606,14 @@ Global
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -695,6 +709,10 @@ Global
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098} {A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}

2
build/DevAnalyzers.props

@ -5,7 +5,7 @@
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
OutputItemType="Analyzer" OutputItemType="Analyzer"
SetTargetFramework="TargetFramework=netstandard2.0"/> SetTargetFramework="TargetFramework=netstandard2.0"/>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj" <ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
PrivateAssets="all" PrivateAssets="all"
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
OutputItemType="Analyzer" OutputItemType="Analyzer"

32
build/ExternalConsumers.props

@ -0,0 +1,32 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<InternalsVisibleTo Include="AvaloniaUI.Xpf.WinApiShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
<InternalsVisibleTo Include="System.Windows.Forms, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="System.Windows.Forms.Primitives, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework-SystemData, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework.Aero, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="System.Xaml, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework-SystemDrawing, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework-SystemCore, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="WindowsFormsIntegration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework.AeroLite, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework.Aero2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="UIAutomationClient, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework.Luna, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="UIAutomationTypes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework.Royale, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="System.Windows.Input.Manipulations, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="System.Windows.Controls.Ribbon, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationFramework-SystemXml, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="ReachFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="System.Printing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework-SystemXmlLinq, PublicKey=00000000000000000400000000000000" />
<InternalsVisibleTo Include="PresentationUI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="Atlantis, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
<InternalsVisibleTo Include="WindowsBase, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationFramework.Classic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="UIAutomationProvider, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
<InternalsVisibleTo Include="PresentationCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
</ItemGroup>
</Project>

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

@ -275,7 +275,7 @@
delta.Y = [event deltaY]; delta.Y = [event deltaY];
} }
uint32 timestamp = static_cast<uint32>([event timestamp] * 1000); uint64_t timestamp = static_cast<uint64_t>([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]]; auto modifiers = [self getModifiers:[event modifierFlags]];
if(type != Move || if(type != Move ||
@ -444,7 +444,7 @@
auto key = s_KeyMap[[event keyCode]]; auto key = s_KeyMap[[event keyCode]];
uint32_t timestamp = static_cast<uint32_t>([event timestamp] * 1000); uint64_t timestamp = static_cast<uint64_t>([event timestamp] * 1000);
auto modifiers = [self getModifiers:[event modifierFlags]]; auto modifiers = [self getModifiers:[event modifierFlags]];
if(_parent != nullptr) if(_parent != nullptr)
@ -657,7 +657,7 @@
[self unmarkText]; [self unmarkText];
uint32_t timestamp = static_cast<uint32_t>([NSDate timeIntervalSinceReferenceDate] * 1000); uint64_t timestamp = static_cast<uint64_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]); _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);

2
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -460,7 +460,7 @@
auto point = [self translateLocalPoint:avnPoint]; auto point = [self translateLocalPoint:avnPoint];
AvnVector delta = { 0, 0 }; AvnVector delta = { 0, 0 };
_parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta); _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint64>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
} }
if(!_isTransitioningToFullScreen) if(!_isTransitioningToFullScreen)

1
nukebuild/Build.cs

@ -212,6 +212,7 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.Headless.UnitTests");
}); });
Target RunRenderTests => _ => _ Target RunRenderTests => _ => _

24
nukebuild/RefAssemblyGenerator.cs

@ -96,7 +96,7 @@ public class RefAssemblyGenerator
| MethodAttributes.HideBySig, type.Module.TypeSystem.Void)); | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
} }
var forceUnstable = type.CustomAttributes.Any(a => var forceUnstable = type.CustomAttributes.FirstOrDefault(a =>
a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute"); a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
foreach (var m in type.Methods) foreach (var m in type.Methods)
@ -109,22 +109,28 @@ public class RefAssemblyGenerator
} }
} }
static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force) static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute unstableAttribute)
{ {
if (!force && ( if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
def.HasCustomAttributes == false
|| def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")))
return; return;
if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute")) unstableAttribute = def.CustomAttributes.FirstOrDefault(a =>
a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute") ?? unstableAttribute;
if (unstableAttribute is null)
return; return;
var message = unstableAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
if (string.IsNullOrEmpty(message))
{
message = "This is a part of unstable API and can be changed in minor releases. Consider replacing it with alternatives or reach out developers on GitHub.";
}
def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor) def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor)
{ {
ConstructorArguments = ConstructorArguments =
{ {
new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, message)
"This is a part of unstable API and can be changed in minor releases. You have been warned")
} }
}); });
} }
@ -168,4 +174,4 @@ public class RefAssemblyGenerator
} }
} }
} }
} }

5
nukebuild/numerge.config

@ -16,6 +16,11 @@
"Id": "Avalonia.Generators", "Id": "Avalonia.Generators",
"IgnoreMissingFrameworkBinaries": true, "IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true "DoNotMergeDependencies": true
},
{
"Id": "Avalonia.Analyzers",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
} }
] ]
} }

21
packages/Avalonia/Avalonia.csproj

@ -5,6 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia.BuildServices" Version="0.0.12" />
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj"> <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@ -15,6 +16,10 @@
ReferenceOutputAssembly="false" ReferenceOutputAssembly="false"
PrivateAssets="all" PrivateAssets="all"
OutputItemType="Analyzer" /> OutputItemType="Analyzer" />
<ProjectReference Include="..\..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
OutputItemType="Analyzer" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
@ -60,4 +65,20 @@
<Import Project="..\..\build\NetFX.props" /> <Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\CoreLibraries.props" /> <Import Project="..\..\build\CoreLibraries.props" />
<Import Project="..\..\build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" /> <Import Project="..\..\build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" />
<Target Name="WriteCurrentPackageVersionProps" BeforeTargets="_GetPackageFiles">
<PropertyGroup>
<PackageVersionPropsPath>$(IntermediateOutputPath)/AvaloniaVersion.props</PackageVersionPropsPath>
</PropertyGroup>
<WriteLinesToFile
File="$(PackageVersionPropsPath)"
Overwrite="true"
Lines="&lt;Project&gt;&lt;PropertyGroup&gt;&lt;AvaloniaMainPackageVersion&gt;$(PackageVersion)&lt;/AvaloniaMainPackageVersion&gt;&lt;/PropertyGroup&gt;&lt;/Project&gt;" />
<ItemGroup>
<Content Include="$(PackageVersionPropsPath)">
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</Content>
</ItemGroup>
</Target>
</Project> </Project>

2
packages/Avalonia/Avalonia.props

@ -4,9 +4,11 @@
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath> <AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation> <AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild> <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
<UsedAvaloniaProducts>$(UsedAvaloniaProducts);AvaloniaUI</UsedAvaloniaProducts>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/> <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
<Import Project="$(MSBuildThisFileDirectory)\Avalonia.Generators.props"/> <Import Project="$(MSBuildThisFileDirectory)\Avalonia.Generators.props"/>
<Import Project="$(MSBuildThisFileDirectory)..\build\AvaloniaVersion.props" />
<!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package --> <!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package -->
<ItemGroup> <ItemGroup>

63
readme.md

@ -1,26 +1,43 @@
[![GH_Banner](https://user-images.githubusercontent.com/552074/218457976-92e76834-9e22-4e35-acfa-aa50281bc0f9.png)](https://avaloniaui.net/xpf) ![Star our repo to show support](https://user-images.githubusercontent.com/552074/235945895-1b896994-a0b6-4e7c-a522-c5688c4ec1b9.png)
![Header](https://user-images.githubusercontent.com/552074/235865745-2a8e7274-4f66-4f77-8f05-feeb76e7d478.png)
[![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia) [![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia)
[![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) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![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) [![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) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![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 /> <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) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) [![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) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)
# ⚠️ **v11 Update - Pausing community contributions** ⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)**
for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
## 📖 About ## 📖 About
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM. [Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github).
Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements.
![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png) For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform.
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! #### Roadmap
To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239).
#### Breaking Changes
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
[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
## 🚀 Getting Started ## 🚀 Getting Started
See our [Get Started](https://avaloniaui.net/GettingStarted) guide to begin developing apps with Avalonia UI.
### Visual Studio
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 starter guide see our [documentation](https://docs.avaloniaui.net/docs/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 starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started).
### JetBrains Rider
[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
### Avalonia Packages
Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/ Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/
Use these commands in the Package Manager console to install Avalonia manually: Use these commands in the Package Manager console to install Avalonia manually:
@ -30,31 +47,26 @@ Install-Package Avalonia.Desktop
``` ```
## Showcase ## Showcase
[![Showcase_Banner](https://user-images.githubusercontent.com/552074/235946124-bf6fda52-0c9f-4730-868b-0de957e5b97b.png)](https://avaloniaui.net/showcase)
Examples of UIs built with Avalonia
<video src="https://user-images.githubusercontent.com/4672627/152325602-28df36ec-6444-44a6-aebe-90ad52c8f27a.mp4"></video>
([Lunacy](https://icons8.com/lunacy))
![image](https://user-images.githubusercontent.com/4672627/152325740-261c27a3-e6f0-4662-bff7-4796d4940e04.png)
([PlasticSCM](https://www.plasticscm.com/))
![image](https://user-images.githubusercontent.com/4672627/152326453-14944c4d-33da-4d50-a268-b87f80927adb.png)
([WasabiWallet](https://www.wasabiwallet.io/))
See what others have built with Avalonia UI on our [Showcase](https://avaloniaui.net/Showcase). We welcome submissions!
## JetBrains Rider
[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
## Bleeding Edge Builds ## Bleeding Edge Builds
We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed! We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed!
## Documentation ## Learning
Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers. ### Documentation
Documentation can be found at https://docs.avaloniaui.net.
### Tutorials
We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
### Samples
We have a [range of samples](https://github.com/AvaloniaUI/Avalonia.Samples) to help you get started.
## Building and Using ## Building and Using
@ -116,3 +128,8 @@ We have a range of [support plans available](https://avaloniaui.net/support) for
## .NET Foundation ## .NET Foundation
This project is supported by the [.NET Foundation](https://dotnetfoundation.org). This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
## Avalonia XPF
Unleash the full potential of your existing WPF apps with our cross-platform UI framework, enabling WPF apps to run on macOS and Linux without requiring expensive and risky rewrites.
[![GH_Banner](https://user-images.githubusercontent.com/552074/218457976-92e76834-9e22-4e35-acfa-aa50281bc0f9.png)](https://avaloniaui.net/xpf)

2
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -26,7 +26,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" /> <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />

6
samples/ControlCatalog.NetCore/Properties/launchSettings.json

@ -6,6 +6,10 @@
"Dxgi": { "Dxgi": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--dxgi" "commandLineArgs": "--dxgi"
},
"VNC": {
"commandName": "Project",
"commandLineArgs": "--vnc"
} }
} }
} }

2
samples/ControlCatalog/App.xaml

@ -26,8 +26,6 @@
<Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color> <Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
<Color x:Key="SystemAccentColor">#FF0078D7</Color>
<Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
<!-- Styles attached dynamically depending on current theme (simple or fluent) --> <!-- Styles attached dynamically depending on current theme (simple or fluent) -->
<StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" /> <StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />

48
samples/ControlCatalog/MainView.xaml.cs

@ -19,13 +19,9 @@ namespace ControlCatalog
{ {
public class MainView : UserControl public class MainView : UserControl
{ {
private readonly IPlatformSettings _platformSettings;
public MainView() public MainView()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
_platformSettings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>();
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
var sideBar = this.Get<TabControl>("Sidebar"); var sideBar = this.Get<TabControl>("Sidebar");
@ -141,50 +137,6 @@ namespace ControlCatalog
ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true; ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
}; };
} }
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
_platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
}
private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
{
Application.Current!.Resources["SystemAccentColor"] = e.AccentColor1;
Application.Current.Resources["SystemAccentColorDark1"] = ChangeColorLuminosity(e.AccentColor1, -0.3);
Application.Current.Resources["SystemAccentColorDark2"] = ChangeColorLuminosity(e.AccentColor1, -0.5);
Application.Current.Resources["SystemAccentColorDark3"] = ChangeColorLuminosity(e.AccentColor1, -0.7);
Application.Current.Resources["SystemAccentColorLight1"] = ChangeColorLuminosity(e.AccentColor1, 0.3);
Application.Current.Resources["SystemAccentColorLight2"] = ChangeColorLuminosity(e.AccentColor1, 0.5);
Application.Current.Resources["SystemAccentColorLight3"] = ChangeColorLuminosity(e.AccentColor1, 0.7);
static Color ChangeColorLuminosity(Color color, double luminosityFactor)
{
var red = (double)color.R;
var green = (double)color.G;
var blue = (double)color.B;
if (luminosityFactor < 0)
{
luminosityFactor = 1 + luminosityFactor;
red *= luminosityFactor;
green *= luminosityFactor;
blue *= luminosityFactor;
}
else if (luminosityFactor >= 0)
{
red = (255 - red) * luminosityFactor + red;
green = (255 - green) * luminosityFactor + green;
blue = (255 - blue) * luminosityFactor + blue;
}
return new Color(color.A, (byte)red, (byte)green, (byte)blue);
}
} }
} }
} }

11
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@ -19,18 +19,16 @@ namespace ControlCatalog.Pages
public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d); public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); } public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation)); public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation),
coerce: (_, val) => val % (Math.PI * 2));
/// <summary> /// <summary>
/// Rotation, measured in Radians! /// Rotation, measured in Radians!
/// </summary> /// </summary>
public double Rotation public double Rotation
{ {
get => GetValue(RotationProperty); get => GetValue(RotationProperty);
set set => SetValue(RotationProperty, value);
{
double valueToUse = value % (Math.PI * 2);
SetValue(RotationProperty, valueToUse);
}
} }
public static readonly StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d); public static readonly StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
@ -213,5 +211,6 @@ namespace ControlCatalog.Pages
return workingPoint; return workingPoint;
} }
} }
} }

1
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -3,6 +3,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<NoWarn>$(NoWarn);AVP1012</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

2
samples/MobileSandbox.Desktop/MobileSandbox.Desktop.csproj

@ -19,7 +19,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" /> <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" /> <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\MobileSandbox\MobileSandbox.csproj" /> <ProjectReference Include="..\MobileSandbox\MobileSandbox.csproj" />

4
samples/RenderDemo/Pages/CustomSkiaPage.cs

@ -44,9 +44,9 @@ namespace RenderDemo.Pages
public bool HitTest(Point p) => false; public bool HitTest(Point p) => false;
public bool Equals(ICustomDrawOperation other) => false; public bool Equals(ICustomDrawOperation other) => false;
static Stopwatch St = Stopwatch.StartNew(); static Stopwatch St = Stopwatch.StartNew();
public void Render(IDrawingContextImpl context) public void Render(ImmediateDrawingContext context)
{ {
var leaseFeature = context.GetFeature<ISkiaSharpApiLeaseFeature>(); var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
if (leaseFeature == null) if (leaseFeature == null)
context.DrawGlyphRun(Brushes.Black, _noSkia.PlatformImpl); context.DrawGlyphRun(Brushes.Black, _noSkia.PlatformImpl);
else else

2
samples/RenderDemo/Pages/WriteableBitmapPage.cs

@ -59,7 +59,7 @@ namespace RenderDemo.Pages
color = new Color(fillAlpha, r, g, b); color = new Color(fillAlpha, r, g, b);
} }
data[y * fb.Size.Width + x] = (int) color.ToUint32(); data[y * fb.Size.Width + x] = (int) color.ToUInt32();
} }
} }

10
samples/Sandbox/MainWindow.axaml.cs

@ -6,17 +6,11 @@ using Avalonia.Win32.WinRT.Composition;
namespace Sandbox namespace Sandbox
{ {
public class MainWindow : Window public partial class MainWindow : Window
{ {
public MainWindow() public MainWindow()
{ {
this.InitializeComponent(); InitializeComponent();
this.AttachDevTools();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
} }
} }
} }

2
samples/Sandbox/Sandbox.csproj

@ -4,6 +4,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -17,4 +18,5 @@
<Import Project="..\..\build\SampleApp.props" /> <Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> <Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\BuildTargets.targets" /> <Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SourceGenerators.props" />
</Project> </Project>

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

@ -91,7 +91,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public Action<Rect> Paint { get; set; } public Action<Rect> Paint { get; set; }
public Action<Size, PlatformResizeReason> Resized { get; set; } public Action<Size, WindowResizeReason> Resized { get; set; }
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
@ -156,12 +156,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform
protected virtual void OnResized(Size size) protected virtual void OnResized(Size size)
{ {
Resized?.Invoke(size, PlatformResizeReason.Unspecified); Resized?.Invoke(size, WindowResizeReason.Unspecified);
} }
internal void Resize(Size size) internal void Resize(Size size)
{ {
Resized?.Invoke(size, PlatformResizeReason.Layout); Resized?.Invoke(size, WindowResizeReason.Layout);
} }
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo

12
src/Avalonia.Base/Animation/Animation.cs

@ -200,7 +200,7 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
/// <param name="setter">The animation setter.</param> /// <param name="setter">The animation setter.</param>
/// <param name="value">The property animator value.</param> /// <param name="value">The property animator value.</param>
public static void SetAnimator(IAnimationSetter setter, public static void SetAnimator(IAnimationSetter setter,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
Type value) Type value)
{ {
@ -319,7 +319,7 @@ namespace Avalonia.Animation
if (animators.Count == 1) if (animators.Count == 1)
{ {
var subscription = animators[0].Apply(this, control, clock, match, onComplete); var subscription = animators[0].Apply(this, control, clock, match, onComplete);
if (subscription is not null) if (subscription is not null)
{ {
subscriptions.Add(subscription); subscriptions.Add(subscription);
@ -348,9 +348,11 @@ namespace Avalonia.Animation
if (onComplete != null) if (onComplete != null)
{ {
Task.WhenAll(completionTasks!).ContinueWith( Task.WhenAll(completionTasks!)
(_, state) => ((Action)state!).Invoke(), .ContinueWith((_, state) => ((Action)state!).Invoke()
onComplete); , onComplete
, TaskScheduler.FromCurrentSynchronizationContext()
);
} }
} }
return new CompositeDisposable(subscriptions); return new CompositeDisposable(subscriptions);

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

@ -22,6 +22,7 @@
<Import Project="..\..\build\SourceGenerators.props" /> <Import Project="..\..\build\SourceGenerators.props" />
<ItemGroup> <ItemGroup>
<Compile Include="..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" /> <Compile Include="..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />
<Compile Include="..\Shared\ModuleInitializer.cs" Link="ModuleInitializer.cs" />
<Compile Include="..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" /> <Compile Include="..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" />
</ItemGroup> </ItemGroup>

2
src/Avalonia.Base/ClassBindingManager.cs

@ -17,6 +17,8 @@ namespace Avalonia
return target.Bind(prop, source, anchor); return target.Bind(prop, source, anchor);
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1001:The same AvaloniaProperty should not be registered twice",
Justification = "Classes.attr binding feature is implemented using intermediate avalonia properties for each class")]
private static AvaloniaProperty RegisterClassProxyProperty(string className) private static AvaloniaProperty RegisterClassProxyProperty(string className)
{ {
var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className); var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);

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

@ -35,7 +35,7 @@ namespace Avalonia.Collections
/// Indicates if a weak subscription should be used to track changes to the collection. /// Indicates if a weak subscription should be used to track changes to the collection.
/// </param> /// </param>
/// <returns>A disposable used to terminate the subscription.</returns> /// <returns>A disposable used to terminate the subscription.</returns>
internal static IDisposable ForEachItem<TKey, TValue>( public static IDisposable ForEachItem<TKey, TValue>(
this IAvaloniaReadOnlyDictionary<TKey, TValue> collection, this IAvaloniaReadOnlyDictionary<TKey, TValue> collection,
Action<TKey, TValue> added, Action<TKey, TValue> added,
Action<TKey, TValue> removed, Action<TKey, TValue> removed,

2
src/Avalonia.Base/Controls/IResourceDictionary.cs

@ -18,6 +18,6 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios. /// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios.
/// </summary> /// </summary>
IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries { get; } IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries { get; }
} }
} }

21
src/Avalonia.Base/Controls/IThemeVariantProvider.cs

@ -0,0 +1,21 @@
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia.Controls;
/// <summary>
/// Resource provider with theme variant awareness.
/// Can be used with <see cref="IResourceDictionary.ThemeDictionaries"/>.
/// </summary>
/// <remarks>
/// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions.
/// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
/// </remarks>
[Unstable("This XAML-only API might be removed in the future minor updates.")]
public interface IThemeVariantProvider : IResourceProvider
{
/// <summary>
/// Key property set by the compiler.
/// </summary>
ThemeVariant? Key { get; set; }
}

12
src/Avalonia.Base/Controls/ResourceDictionary.cs

@ -13,13 +13,13 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// An indexed dictionary of resources. /// An indexed dictionary of resources.
/// </summary> /// </summary>
public class ResourceDictionary : IResourceDictionary public class ResourceDictionary : IResourceDictionary, IThemeVariantProvider
{ {
private object? lastDeferredItemKey; private object? lastDeferredItemKey;
private Dictionary<object, object?>? _inner; private Dictionary<object, object?>? _inner;
private IResourceHost? _owner; private IResourceHost? _owner;
private AvaloniaList<IResourceProvider>? _mergedDictionaries; private AvaloniaList<IResourceProvider>? _mergedDictionaries;
private AvaloniaDictionary<ThemeVariant, IResourceProvider>? _themeDictionary; private AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>? _themeDictionary;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResourceDictionary"/> class. /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
@ -93,13 +93,13 @@ namespace Avalonia.Controls
} }
} }
public IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries public IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries
{ {
get get
{ {
if (_themeDictionary == null) if (_themeDictionary == null)
{ {
_themeDictionary = new AvaloniaDictionary<ThemeVariant, IResourceProvider>(2); _themeDictionary = new AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>(2);
_themeDictionary.ForEachItem( _themeDictionary.ForEachItem(
(_, x) => (_, x) =>
{ {
@ -120,6 +120,8 @@ namespace Avalonia.Controls
return _themeDictionary; return _themeDictionary;
} }
} }
ThemeVariant? IThemeVariantProvider.Key { get; set; }
bool IResourceNode.HasResources bool IResourceNode.HasResources
{ {
@ -192,7 +194,7 @@ namespace Avalonia.Controls
if (_themeDictionary is not null) if (_themeDictionary is not null)
{ {
IResourceProvider? themeResourceProvider; IThemeVariantProvider? themeResourceProvider;
if (theme is not null && theme != ThemeVariant.Default) if (theme is not null && theme != ThemeVariant.Default)
{ {
if (_themeDictionary.TryGetValue(theme, out themeResourceProvider) if (_themeDictionary.TryGetValue(theme, out themeResourceProvider)

46
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -119,7 +119,19 @@ namespace Avalonia.Controls
resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider)); resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
key = key ?? throw new ArgumentNullException(nameof(key)); key = key ?? throw new ArgumentNullException(nameof(key));
return new FloatingResourceObservable(resourceProvider, key, converter); return new FloatingResourceObservable(resourceProvider, key, null, converter);
}
public static IObservable<object?> GetResourceObservable(
this IResourceProvider resourceProvider,
object key,
ThemeVariant? defaultThemeVariant,
Func<object?, object?>? converter = null)
{
resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
key = key ?? throw new ArgumentNullException(nameof(key));
return new FloatingResourceObservable(resourceProvider, key, defaultThemeVariant, converter);
} }
private class ResourceObservable : LightweightObservableBase<object?> private class ResourceObservable : LightweightObservableBase<object?>
@ -128,7 +140,10 @@ namespace Avalonia.Controls
private readonly object _key; private readonly object _key;
private readonly Func<object?, object?>? _converter; private readonly Func<object?, object?>? _converter;
public ResourceObservable(IResourceHost target, object key, Func<object?, object?>? converter) public ResourceObservable(
IResourceHost target,
object key,
Func<object?, object?>? converter)
{ {
_target = target; _target = target;
_key = key; _key = key;
@ -170,11 +185,8 @@ namespace Avalonia.Controls
private object? GetValue() private object? GetValue()
{ {
if (_target is not IThemeVariantHost themeVariantHost var theme = (_target as IThemeVariantHost)?.ActualThemeVariant;
|| !_target.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value)) var value = _target.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
{
value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
return _converter?.Invoke(value) ?? value; return _converter?.Invoke(value) ?? value;
} }
@ -183,14 +195,20 @@ namespace Avalonia.Controls
private class FloatingResourceObservable : LightweightObservableBase<object?> private class FloatingResourceObservable : LightweightObservableBase<object?>
{ {
private readonly IResourceProvider _target; private readonly IResourceProvider _target;
private readonly ThemeVariant? _overrideThemeVariant;
private readonly object _key; private readonly object _key;
private readonly Func<object?, object?>? _converter; private readonly Func<object?, object?>? _converter;
private IResourceHost? _owner; private IResourceHost? _owner;
public FloatingResourceObservable(IResourceProvider target, object key, Func<object?, object?>? converter) public FloatingResourceObservable(
IResourceProvider target,
object key,
ThemeVariant? overrideThemeVariant,
Func<object?, object?>? converter)
{ {
_target = target; _target = target;
_key = key; _key = key;
_overrideThemeVariant = overrideThemeVariant;
_converter = converter; _converter = converter;
} }
@ -233,7 +251,7 @@ namespace Avalonia.Controls
{ {
_owner.ResourcesChanged -= ResourcesChanged; _owner.ResourcesChanged -= ResourcesChanged;
} }
if (_owner is IThemeVariantHost themeVariantHost) if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost)
{ {
themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged; themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
} }
@ -244,12 +262,11 @@ namespace Avalonia.Controls
{ {
_owner.ResourcesChanged += ResourcesChanged; _owner.ResourcesChanged += ResourcesChanged;
} }
if (_owner is IThemeVariantHost themeVariantHost2) if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost2)
{ {
themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged; themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged;
} }
PublishNext(); PublishNext();
} }
@ -265,11 +282,8 @@ namespace Avalonia.Controls
private object? GetValue() private object? GetValue()
{ {
if (!(_target.Owner is IThemeVariantHost themeVariantHost) var theme = _overrideThemeVariant ?? (_target.Owner as IThemeVariantHost)?.ActualThemeVariant;
|| !_target.Owner.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value)) var value = _target.Owner?.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
{
value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
}
return _converter?.Invoke(value) ?? value; return _converter?.Invoke(value) ?? value;
} }

3
src/Avalonia.Base/Data/BindingPriority.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.Data namespace Avalonia.Data
{ {
@ -47,7 +48,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
Unset = int.MaxValue, Unset = int.MaxValue,
[Obsolete("Use Template priority")] [Obsolete("Use Template priority"), EditorBrowsable(EditorBrowsableState.Never)]
TemplatedParent = Template, TemplatedParent = Template,
} }
} }

3
src/Avalonia.Base/Data/InstancedBinding.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using Avalonia.Reactive; using Avalonia.Reactive;
using ObservableEx = Avalonia.Reactive.Observable; using ObservableEx = Avalonia.Reactive.Observable;
@ -49,7 +50,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
public IObservable<object?> Source { get; } public IObservable<object?> Source { get; }
[Obsolete("Use Source property")] [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
public IObservable<object?> Observable => Source; public IObservable<object?> Observable => Source;
/// <summary> /// <summary>

2
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -176,7 +176,7 @@ namespace Avalonia.Input
{ {
bool menuIsOpen = MainMenu?.IsOpen == true; bool menuIsOpen = MainMenu?.IsOpen == true;
if (e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) || menuIsOpen) if (e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && !e.KeyModifiers.HasAllFlags(KeyModifiers.Control) || menuIsOpen)
{ {
// If any other key is pressed with the Alt key held down, or the main menu is open, // If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key. // find all controls who have registered that access key.

13
src/Avalonia.Base/Input/Cursor.cs

@ -42,19 +42,21 @@ namespace Avalonia.Input
public class Cursor : IDisposable public class Cursor : IDisposable
{ {
public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow); public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow);
private string _name;
internal Cursor(ICursorImpl platformImpl) private Cursor(ICursorImpl platformImpl, string name)
{ {
PlatformImpl = platformImpl; PlatformImpl = platformImpl;
_name = name;
} }
public Cursor(StandardCursorType cursorType) public Cursor(StandardCursorType cursorType)
: this(GetCursorFactory().GetCursor(cursorType)) : this(GetCursorFactory().GetCursor(cursorType), cursorType.ToString())
{ {
} }
public Cursor(IBitmap cursor, PixelPoint hotSpot) public Cursor(IBitmap cursor, PixelPoint hotSpot)
: this(GetCursorFactory().CreateCursor(cursor.PlatformImpl.Item, hotSpot)) : this(GetCursorFactory().CreateCursor(cursor.PlatformImpl.Item, hotSpot), "BitmapCursor")
{ {
} }
@ -73,5 +75,10 @@ namespace Avalonia.Input
{ {
return AvaloniaLocator.Current.GetRequiredService<ICursorFactory>(); return AvaloniaLocator.Current.GetRequiredService<ICursorFactory>();
} }
public override string ToString()
{
return _name;
}
} }
} }

3
src/Avalonia.Base/Input/DataFormats.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -17,7 +18,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Dataformat for one or more filenames /// Dataformat for one or more filenames
/// </summary> /// </summary>
[Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms.")] [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly string FileNames = nameof(FileNames); public static readonly string FileNames = nameof(FileNames);
} }
} }

3
src/Avalonia.Base/Input/DataObjectExtensions.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
@ -25,7 +26,7 @@ namespace Avalonia.Input
/// <returns> /// <returns>
/// Collection of file names. If format isn't available, returns null. /// Collection of file names. If format isn't available, returns null.
/// </returns> /// </returns>
[System.Obsolete("Use GetFiles, this method is supported only on desktop platforms.")] [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
public static IEnumerable<string>? GetFileNames(this IDataObject dataObject) public static IEnumerable<string>? GetFileNames(this IDataObject dataObject)
{ {
return (dataObject.Get(DataFormats.FileNames) as IEnumerable<string>) return (dataObject.Get(DataFormats.FileNames) as IEnumerable<string>)

3
src/Avalonia.Base/Input/DragEventArgs.cs

@ -25,8 +25,7 @@ namespace Avalonia.Input
return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0); return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0);
} }
[Unstable] [Unstable("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
[Obsolete("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers) public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers)
: base(routedEvent) : base(routedEvent)
{ {

3
src/Avalonia.Base/Input/PointerDeltaEventArgs.cs

@ -9,8 +9,7 @@ namespace Avalonia.Input
{ {
public Vector Delta { get; } public Vector Delta { get; }
[Unstable] [Unstable("This constructor might be removed in 12.0.")]
[Obsolete("This constructor might be removed in 12.0.")]
public PointerDeltaEventArgs(RoutedEvent routedEvent, object? source, public PointerDeltaEventArgs(RoutedEvent routedEvent, object? source,
IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp, IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta) PointerPointProperties properties, KeyModifiers modifiers, Vector delta)

16
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -14,8 +14,7 @@ namespace Avalonia.Input
private readonly PointerPointProperties _properties; private readonly PointerPointProperties _properties;
private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints; private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
[Unstable] [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
[Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
public PointerEventArgs(RoutedEvent routedEvent, public PointerEventArgs(RoutedEvent routedEvent,
object? source, object? source,
IPointer pointer, IPointer pointer,
@ -77,14 +76,14 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Gets the pointer position relative to a control. /// Gets the pointer position relative to a control.
/// </summary> /// </summary>
/// <param name="relativeTo">The control.</param> /// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns>The pointer position in the control's coordinates.</returns> /// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
/// <summary> /// <summary>
/// Returns the PointerPoint associated with the current event /// Returns the PointerPoint associated with the current event
/// </summary> /// </summary>
/// <param name="relativeTo">The visual which coordinate system to use. Pass null for toplevel coordinate system</param> /// <param name="relativeTo">The visual whose coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns></returns> /// <returns></returns>
public PointerPoint GetCurrentPoint(Visual? relativeTo) public PointerPoint GetCurrentPoint(Visual? relativeTo)
=> new PointerPoint(Pointer, GetPosition(relativeTo), _properties); => new PointerPoint(Pointer, GetPosition(relativeTo), _properties);
@ -129,8 +128,7 @@ namespace Avalonia.Input
public class PointerPressedEventArgs : PointerEventArgs public class PointerPressedEventArgs : PointerEventArgs
{ {
[Unstable] [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
[Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
public PointerPressedEventArgs( public PointerPressedEventArgs(
object source, object source,
IPointer pointer, IPointer pointer,
@ -150,8 +148,7 @@ namespace Avalonia.Input
public class PointerReleasedEventArgs : PointerEventArgs public class PointerReleasedEventArgs : PointerEventArgs
{ {
[Unstable] [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
[Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")]
public PointerReleasedEventArgs( public PointerReleasedEventArgs(
object source, IPointer pointer, object source, IPointer pointer,
Visual rootVisual, Point rootVisualPosition, ulong timestamp, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
@ -173,8 +170,7 @@ namespace Avalonia.Input
{ {
public IPointer Pointer { get; } public IPointer Pointer { get; }
[Unstable] [Unstable("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")]
[Obsolete("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")]
public PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent) public PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
{ {
Pointer = pointer; Pointer = pointer;

3
src/Avalonia.Base/Input/PointerWheelEventArgs.cs

@ -9,8 +9,7 @@ namespace Avalonia.Input
{ {
public Vector Delta { get; } public Vector Delta { get; }
[Unstable] [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow.MouseWheel.")]
[Obsolete("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow.MouseWheel.")]
public PointerWheelEventArgs(object source, IPointer pointer, Visual rootVisual, public PointerWheelEventArgs(object source, IPointer pointer, Visual rootVisual,
Point rootVisualPosition, ulong timestamp, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta) PointerPointProperties properties, KeyModifiers modifiers, Vector delta)

10
src/Avalonia.Base/Layout/LayoutManager.cs

@ -17,7 +17,7 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
public class LayoutManager : ILayoutManager, IDisposable public class LayoutManager : ILayoutManager, IDisposable
{ {
private const int MaxPasses = 3; private const int MaxPasses = 10;
private readonly Layoutable _owner; private readonly Layoutable _owner;
private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid); private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid); private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid);
@ -249,10 +249,12 @@ namespace Avalonia.Layout
{ {
var control = _toMeasure.Dequeue(); var control = _toMeasure.Dequeue();
if (!control.IsMeasureValid && control.IsAttachedToVisualTree) if (!control.IsMeasureValid)
{ {
Measure(control); Measure(control);
} }
_toArrange.Enqueue(control);
} }
} }
@ -262,7 +264,7 @@ namespace Avalonia.Layout
{ {
var control = _toArrange.Dequeue(); var control = _toArrange.Dequeue();
if (!control.IsArrangeValid && control.IsAttachedToVisualTree) if (!control.IsArrangeValid)
{ {
Arrange(control); Arrange(control);
} }
@ -297,8 +299,6 @@ namespace Avalonia.Layout
{ {
control.Measure(control.PreviousMeasure.Value); control.Measure(control.PreviousMeasure.Value);
} }
_toArrange.Enqueue(control);
} }
return true; return true;

18
src/Avalonia.Base/Layout/LayoutQueue.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Logging;
namespace Avalonia.Layout namespace Avalonia.Layout
{ {
@ -48,10 +49,21 @@ namespace Avalonia.Layout
{ {
_loopQueueInfo.TryGetValue(item, out var info); _loopQueueInfo.TryGetValue(item, out var info);
if (!info.Active && info.Count < _maxEnqueueCountPerLoop) if (!info.Active)
{ {
_inner.Enqueue(item); if (info.Count < _maxEnqueueCountPerLoop)
_loopQueueInfo[item] = new Info() { Active = true, Count = info.Count + 1 }; {
_inner.Enqueue(item);
_loopQueueInfo[item] = new Info() { Active = true, Count = info.Count + 1 };
}
else
{
Logger.TryGet(LogEventLevel.Warning, LogArea.Layout)?.Log(
this,
"Layout cycle detected. Item {Item} was enqueued {Count} times.",
item,
info.Count);
}
} }
} }

40
src/Avalonia.Base/Layout/Layoutable.cs

@ -776,10 +776,24 @@ namespace Avalonia.Layout
// All changes to visibility cause the parent element to be notified. // All changes to visibility cause the parent element to be notified.
this.GetVisualParent<Layoutable>()?.ChildDesiredSizeChanged(this); this.GetVisualParent<Layoutable>()?.ChildDesiredSizeChanged(this);
// We only invalidate outselves when visibility is changed to true.
if (change.GetNewValue<bool>()) if (change.GetNewValue<bool>())
{ {
// We only invalidate ourselves when visibility is changed to true.
InvalidateMeasure(); InvalidateMeasure();
// If any descendant had its measure/arrange invalidated while we were hidden,
// they will need to to be registered with the layout manager now that they
// are again effectively visible. If IsEffectivelyVisible becomes an observable
// property then we can piggy-pack on that; for the moment we do this manually.
if (VisualRoot is ILayoutRoot layoutRoot)
{
var count = VisualChildren.Count;
for (var i = 0; i < count; ++i)
{
(VisualChildren[i] as Layoutable)?.AncestorBecameVisible(layoutRoot.LayoutManager);
}
}
} }
} }
} }
@ -804,6 +818,30 @@ namespace Avalonia.Layout
InvalidateMeasure(); InvalidateMeasure();
} }
private void AncestorBecameVisible(ILayoutManager layoutManager)
{
if (!IsVisible)
return;
if (!IsMeasureValid)
{
layoutManager.InvalidateMeasure(this);
InvalidateVisual();
}
else if (!IsArrangeValid)
{
layoutManager.InvalidateArrange(this);
InvalidateVisual();
}
var count = VisualChildren.Count;
for (var i = 0; i < count; ++i)
{
(VisualChildren[i] as Layoutable)?.AncestorBecameVisible(layoutManager);
}
}
/// <summary> /// <summary>
/// Called when the layout manager raises a LayoutUpdated event. /// Called when the layout manager raises a LayoutUpdated event.
/// </summary> /// </summary>

14
src/Avalonia.Base/Media/Color.cs

@ -6,11 +6,12 @@
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
using System; using System;
using System.ComponentModel;
using System.Globalization; using System.Globalization;
#if !BUILDTASK #if !BUILDTASK
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
using static Avalonia.Utilities.SpanHelpers;
#endif #endif
using static Avalonia.Utilities.SpanHelpers;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -449,7 +450,7 @@ namespace Avalonia.Media
/// </returns> /// </returns>
public override string ToString() public override string ToString()
{ {
uint rgb = ToUint32(); uint rgb = ToUInt32();
return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb.ToString("x8", CultureInfo.InvariantCulture)}"; return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb.ToString("x8", CultureInfo.InvariantCulture)}";
} }
@ -459,11 +460,18 @@ namespace Avalonia.Media
/// <returns> /// <returns>
/// The integer representation of the color. /// The integer representation of the color.
/// </returns> /// </returns>
public uint ToUint32() public uint ToUInt32()
{ {
return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B; return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
} }
/// <inheritdoc cref="Color.ToUInt32"/>
[Obsolete("Use Color.ToUInt32() instead."), EditorBrowsable(EditorBrowsableState.Never)]
public uint ToUint32()
{
return ToUInt32();
}
/// <summary> /// <summary>
/// Returns the HSL color model equivalent of this RGB color. /// Returns the HSL color model equivalent of this RGB color.
/// </summary> /// </summary>

2
src/Avalonia.Base/Media/DashStyle.cs

@ -44,6 +44,8 @@ namespace Avalonia.Media
/// </summary> /// </summary>
/// <param name="dashes">The dashes collection.</param> /// <param name="dashes">The dashes collection.</param>
/// <param name="offset">The dash sequence offset.</param> /// <param name="offset">The dash sequence offset.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public DashStyle(IEnumerable<double>? dashes, double offset) public DashStyle(IEnumerable<double>? dashes, double offset)
{ {
Dashes = (dashes as AvaloniaList<double>) ?? new AvaloniaList<double>(dashes ?? Array.Empty<double>()); Dashes = (dashes as AvaloniaList<double>) ?? new AvaloniaList<double>(dashes ?? Array.Empty<double>());

7
src/Avalonia.Base/Media/DrawingContext.cs

@ -5,6 +5,7 @@ using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using System.ComponentModel;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -417,11 +418,11 @@ namespace Avalonia.Media
return new PushedState(this); return new PushedState(this);
} }
[Obsolete("Use PushTransform")] [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix); public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix);
[Obsolete("Use PushTransform")] [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPostTransform(Matrix matrix) => PushTransform(matrix); public PushedState PushPostTransform(Matrix matrix) => PushTransform(matrix);
[Obsolete("Use PushTransform")] [Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushTransformContainer() => PushTransform(Matrix.Identity); public PushedState PushTransformContainer() => PushTransform(Matrix.Identity);

2
src/Avalonia.Base/Media/GeometryDrawing.cs

@ -10,7 +10,7 @@ namespace Avalonia.Media
public class GeometryDrawing : Drawing public class GeometryDrawing : Drawing
{ {
// Adding the Pen's stroke thickness here could yield wrong results due to transforms. // Adding the Pen's stroke thickness here could yield wrong results due to transforms.
private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0); private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUInt32(), 0);
/// <summary> /// <summary>
/// Defines the <see cref="Geometry"/> property. /// Defines the <see cref="Geometry"/> property.

2
src/Avalonia.Base/Media/GradientBrush.cs

@ -38,6 +38,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GradientBrush"/> class. /// Initializes a new instance of the <see cref="GradientBrush"/> class.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public GradientBrush() public GradientBrush()
{ {
this.GradientStops = new GradientStops(); this.GradientStops = new GradientStops();

36
src/Avalonia.Base/Media/HslColor.cs

@ -98,43 +98,13 @@ namespace Avalonia.Media
L = hsl.L; L = hsl.L;
} }
/// <summary> /// <inheritdoc cref="HsvColor.A"/>
/// Gets the Alpha (transparency) component in the range from 0..1 (percentage).
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>0 is fully transparent.</item>
/// <item>1 is fully opaque.</item>
/// </list>
/// </remarks>
public double A { get; } public double A { get; }
/// <summary> /// <inheritdoc cref="HsvColor.H"/>
/// Gets the Hue component in the range from 0..360 (degrees).
/// This is the color's location, in degrees, on a color wheel/circle from 0 to 360.
/// Note that 360 is equivalent to 0 and will be adjusted automatically.
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>0/360 degrees is Red.</item>
/// <item>60 degrees is Yellow.</item>
/// <item>120 degrees is Green.</item>
/// <item>180 degrees is Cyan.</item>
/// <item>240 degrees is Blue.</item>
/// <item>300 degrees is Magenta.</item>
/// </list>
/// </remarks>
public double H { get; } public double H { get; }
/// <summary> /// <inheritdoc cref="HsvColor.S"/>
/// Gets the Saturation component in the range from 0..1 (percentage).
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>0 is a shade of gray (no color).</item>
/// <item>1 is the full color.</item>
/// </list>
/// </remarks>
public double S { get; } public double S { get; }
/// <summary> /// <summary>

1
src/Avalonia.Base/Media/ImmediateDrawingContext.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;

16
src/Avalonia.Base/Media/PlatformDrawingContext.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Logging;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
@ -41,8 +42,19 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) => BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) =>
_impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode); _impl.DrawBitmap(source, opacity, sourceRect, destRect, bitmapInterpolationMode);
public override void Custom(ICustomDrawOperation custom) => public override void Custom(ICustomDrawOperation custom)
custom.Render(_impl); {
using var immediateDrawingContext = new ImmediateDrawingContext(_impl, false);
try
{
custom.Render(immediateDrawingContext);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(custom, $"Exception in {custom.GetType().Name}.{nameof(ICustomDrawOperation.Render)} {{0}}", e);
}
}
public override void DrawGlyphRun(IBrush? foreground, GlyphRun glyphRun) public override void DrawGlyphRun(IBrush? foreground, GlyphRun glyphRun)
{ {

2
src/Avalonia.Base/Media/PolyLineSegment.cs

@ -28,6 +28,8 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PolyLineSegment"/> class. /// Initializes a new instance of the <see cref="PolyLineSegment"/> class.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public PolyLineSegment() public PolyLineSegment()
{ {
Points = new Points(); Points = new Points();

4
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@ -185,7 +185,9 @@ namespace Avalonia.Media.TextFormatting
} }
//Stop at the first missing glyph //Stop at the first missing glyph
if (!currentCodepoint.IsBreakChar && !glyphTypeface.TryGetGlyph(currentCodepoint, out _)) if (!currentCodepoint.IsBreakChar &&
currentCodepoint.GeneralCategory != GeneralCategory.Control &&
!glyphTypeface.TryGetGlyph(currentCodepoint, out _))
{ {
break; break;
} }

33
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -83,7 +83,7 @@ namespace Avalonia.Media.TextFormatting
/// <inheritdoc/> /// <inheritdoc/>
public override void Draw(DrawingContext drawingContext, Point lineOrigin) public override void Draw(DrawingContext drawingContext, Point lineOrigin)
{ {
var (currentX, currentY) = lineOrigin; var (currentX, currentY) = lineOrigin + new Point(Start, 0);
foreach (var textRun in _textRuns) foreach (var textRun in _textRuns)
{ {
@ -698,7 +698,7 @@ namespace Avalonia.Media.TextFormatting
i = lastRunIndex; i = lastRunIndex;
//Possible overlap at runs of different direction //Possible overlap at runs of different direction
if (directionalWidth == 0) if (directionalWidth == 0 && i < _textRuns.Length - 1)
{ {
//In case a run only contains a linebreak we don't want to skip it. //In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped) if (currentRun is ShapedTextRun shaped)
@ -844,7 +844,7 @@ namespace Avalonia.Media.TextFormatting
i = firstRunIndex; i = firstRunIndex;
//Possible overlap at runs of different direction //Possible overlap at runs of different direction
if (directionalWidth == 0) if (directionalWidth == 0 && i > 0)
{ {
//In case a run only contains a linebreak we don't want to skip it. //In case a run only contains a linebreak we don't want to skip it.
if (currentRun is ShapedTextRun shaped) if (currentRun is ShapedTextRun shaped)
@ -860,8 +860,8 @@ namespace Avalonia.Media.TextFormatting
} }
} }
TextBounds? textBounds = null;
int coveredLength; int coveredLength;
TextBounds? textBounds;
switch (currentDirection) switch (currentDirection)
{ {
@ -942,6 +942,13 @@ namespace Avalonia.Media.TextFormatting
new TextRunBounds( new TextRunBounds(
new Rect(startX, 0, drawableTextRun.Size.Width, Height), currentPosition, currentRun.Length, currentRun)); new Rect(startX, 0, drawableTextRun.Size.Width, Height), currentPosition, currentRun.Length, currentRun));
} }
else
{
//Add potential TextEndOfParagraph
textRunBounds.Add(
new TextRunBounds(
new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
}
currentPosition += currentRun.Length; currentPosition += currentRun.Length;
@ -1007,6 +1014,13 @@ namespace Avalonia.Media.TextFormatting
endX += drawableTextRun.Size.Width; endX += drawableTextRun.Size.Width;
} }
else
{
//Add potential TextEndOfParagraph
textRunBounds.Add(
new TextRunBounds(
new Rect(endX, 0, 0, Height), currentPosition, currentRun.Length, currentRun));
}
currentPosition += currentRun.Length; currentPosition += currentRun.Length;
@ -1409,8 +1423,6 @@ namespace Avalonia.Media.TextFormatting
var fontMetrics = _paragraphProperties.DefaultTextRunProperties.CachedGlyphTypeface.Metrics; var fontMetrics = _paragraphProperties.DefaultTextRunProperties.CachedGlyphTypeface.Metrics;
var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize;
var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight; var scale = fontRenderingEmSize / fontMetrics.DesignEmHeight;
var width = 0d;
var widthIncludingWhitespace = 0d; var widthIncludingWhitespace = 0d;
var trailingWhitespaceLength = 0; var trailingWhitespaceLength = 0;
var newLineLength = 0; var newLineLength = 0;
@ -1422,13 +1434,6 @@ namespace Avalonia.Media.TextFormatting
var lineHeight = _paragraphProperties.LineHeight; var lineHeight = _paragraphProperties.LineHeight;
var lastRunIndex = _textRuns.Length - 1;
if (lastRunIndex > 0 && _textRuns[lastRunIndex] is TextEndOfLine)
{
lastRunIndex--;
}
for (var index = 0; index < _textRuns.Length; index++) for (var index = 0; index < _textRuns.Length; index++)
{ {
switch (_textRuns[index]) switch (_textRuns[index])
@ -1486,7 +1491,7 @@ namespace Avalonia.Media.TextFormatting
} }
} }
width = widthIncludingWhitespace; var width = widthIncludingWhitespace;
for (var i = _textRuns.Length - 1; i >= 0; i--) for (var i = _textRuns.Length - 1; i >= 0; i--)
{ {

2
src/Avalonia.Base/Media/TransformGroup.cs

@ -11,6 +11,8 @@ namespace Avalonia.Media
public static readonly StyledProperty<Transforms> ChildrenProperty = public static readonly StyledProperty<Transforms> ChildrenProperty =
AvaloniaProperty.Register<TransformGroup, Transforms>(nameof(Children)); AvaloniaProperty.Register<TransformGroup, Transforms>(nameof(Children));
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public TransformGroup() public TransformGroup()
{ {
Children = new Transforms(); Children = new Transforms();

23
src/Avalonia.Base/Metadata/AvaloniaListAttribute.cs

@ -0,0 +1,23 @@
using System;
namespace Avalonia.Metadata;
/// <summary>
/// Defines how compiler should split avalonia list string value before parsing individual items.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class AvaloniaListAttribute : Attribute
{
/// <summary>
/// Separator used to split input string.
/// Default value is ','.
/// </summary>
public string[]? Separators { get; init; }
/// <summary>
/// Split options used to split input string.
/// Default value is RemoveEmptyEntries with TrimEntries.
/// </summary>
// StringSplitOptions.TrimEntries = 2, but only on net6 target.
public StringSplitOptions SplitOptions { get; init; } = StringSplitOptions.RemoveEmptyEntries | (StringSplitOptions)2;
}

22
src/Avalonia.Base/Metadata/UnstableAttribute.cs

@ -1,4 +1,4 @@
using System; using System;
namespace Avalonia.Metadata namespace Avalonia.Metadata
{ {
@ -9,5 +9,25 @@ namespace Avalonia.Metadata
[AttributeUsage(AttributeTargets.All)] [AttributeUsage(AttributeTargets.All)]
public sealed class UnstableAttribute : Attribute public sealed class UnstableAttribute : Attribute
{ {
/// <summary>
/// Initializes a new instance of the <see cref="UnstableAttribute"/> class.
/// </summary>
public UnstableAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UnstableAttribute"/> class.
/// </summary>
/// <param name="message">The text string that describes alternative workarounds.</param>
public UnstableAttribute(string? message)
{
Message = message;
}
/// <summary>
/// Gets a value that indicates whether the compiler will treat usage of the obsolete program element as an error.
/// </summary>
public string? Message { get; }
} }
} }

295
src/Avalonia.Base/Platform/AssetLoader.cs

@ -1,281 +1,48 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
#if !BUILDTASK
using Avalonia.Platform.Internal;
using Avalonia.Utilities;
#endif
namespace Avalonia.Platform namespace Avalonia.Platform;
{
/// <summary>
/// Loads assets compiled into the application binary.
/// </summary>
public class AssetLoader
#if !BUILDTASK #if !BUILDTASK
: IAssetLoader /// <inheritdoc cref="IAssetLoader"/>
#endif #endif
{ public static class AssetLoader
{
#if !BUILDTASK #if !BUILDTASK
private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver(); private static IAssetLoader GetAssetLoader() => AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
private AssemblyDescriptor? _defaultResmAssembly;
/// <remarks>
/// Introduced for tests.
/// </remarks>
internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) =>
s_assemblyDescriptorResolver = resolver;
/// <summary>
/// Initializes a new instance of the <see cref="AssetLoader"/> class.
/// </summary>
/// <param name="assembly">
/// The default assembly from which to load resm: assets for which no assembly is specified.
/// </param>
public AssetLoader(Assembly? assembly = null)
{
if (assembly == null)
assembly = Assembly.GetEntryAssembly();
if (assembly != null)
_defaultResmAssembly = new AssemblyDescriptor(assembly);
}
/// <summary>
/// Sets the default assembly from which to load assets for which no assembly is specified.
/// </summary>
/// <param name="assembly">The default assembly.</param>
public void SetDefaultAssembly(Assembly assembly)
{
_defaultResmAssembly = new AssemblyDescriptor(assembly);
}
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri, Uri? baseUri = null)
{
return TryGetAsset(uri, baseUri, out _);
}
/// <summary>
/// Opens the asset with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the asset contents.</returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
/// <summary>
/// Opens the asset with the requested URI and returns the asset stream and the
/// assembly containing the asset.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>
/// The stream containing the resource contents together with the assembly.
/// </returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
{
if (TryGetAsset(uri, baseUri, out var assetDescriptor))
{
return (assetDescriptor.GetStream(), assetDescriptor.Assembly);
}
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
public Assembly? GetAssembly(Uri uri, Uri? baseUri)
{
if (!uri.IsAbsoluteUri && baseUri != null)
{
uri = new Uri(baseUri, uri);
}
if (TryGetAssembly(uri, out var assemblyDescriptor))
{
return assemblyDescriptor.Assembly;
}
return null;
}
/// <summary>
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
{
if (uri.IsAbsoluteResm())
{
if (!TryGetAssembly(uri, out var assembly))
{
assembly = _defaultResmAssembly;
}
return assembly?.Resources?
.Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath()))
.Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
Enumerable.Empty<Uri>();
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
if (!TryGetResAsmAndPath(uri, out var assembly, out var path))
{
return Enumerable.Empty<Uri>();
}
if (assembly?.AvaloniaResources == null) /// <inheritdoc cref="IAssetLoader.SetDefaultAssembly"/>
{ public static void SetDefaultAssembly(Assembly assembly) => GetAssetLoader().SetDefaultAssembly(assembly);
return Enumerable.Empty<Uri>();
}
if (path.Length > 0 && path[path.Length - 1] != '/') /// <inheritdoc cref="IAssetLoader.Exists"/>
{ public static bool Exists(Uri uri, Uri? baseUri = null) => GetAssetLoader().Exists(uri, baseUri);
path += '/';
}
return assembly.AvaloniaResources /// <inheritdoc cref="IAssetLoader.Open"/>
.Where(r => r.Key.StartsWith(path, StringComparison.Ordinal)) public static Stream Open(Uri uri, Uri? baseUri = null) => GetAssetLoader().Open(uri, baseUri);
.Select(x => new Uri($"avares://{assembly.Name}{x.Key}"));
}
return Enumerable.Empty<Uri>(); /// <inheritdoc cref="IAssetLoader.OpenAndGetAssembly"/>
} public static (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
=> GetAssetLoader().OpenAndGetAssembly(uri, baseUri);
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor) /// <inheritdoc cref="IAssetLoader.GetAssembly"/>
{ public static Assembly? GetAssembly(Uri uri, Uri? baseUri = null)
assetDescriptor = null; => GetAssetLoader().GetAssembly(uri, baseUri);
if (uri.IsAbsoluteResm()) /// <inheritdoc cref="IAssetLoader.GetAssets"/>
{ public static IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly)) => GetAssetLoader().GetAssets(uri, baseUri);
{
assembly = _defaultResmAssembly;
}
if (assembly?.Resources != null)
{
var resourceKey = uri.AbsolutePath;
if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor))
{
return true;
}
}
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
if (TryGetResAsmAndPath(uri, out var assembly, out var path))
{
if (assembly.AvaloniaResources == null)
{
return false;
}
if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor))
{
return true;
}
}
}
return false;
}
private static bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path)
{
path = uri.GetUnescapeAbsolutePath();
if (TryLoadAssembly(uri.Authority, out assembly))
{
return true;
}
return false;
}
private static bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
{
assembly = null;
if (uri != null)
{
if (!uri.IsAbsoluteUri)
{
return false;
}
if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _))
{
return true;
}
if (uri.IsResm())
{
var assemblyName = uri.GetAssemblyNameFromQuery();
if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly))
{
return true;
}
}
}
return false;
}
private static bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
{
assembly = null;
try
{
assembly = s_assemblyDescriptorResolver.GetAssembly(assemblyName);
return true;
}
catch (Exception) { }
return false;
}
#endif #endif
public static void RegisterResUriParsers() internal static void RegisterResUriParsers()
{ {
if (!UriParser.IsKnownScheme("avares")) if (!UriParser.IsKnownScheme("avares"))
UriParser.Register(new GenericUriParser( UriParser.Register(new GenericUriParser(
GenericUriParserOptions.GenericAuthority | GenericUriParserOptions.GenericAuthority |
GenericUriParserOptions.NoUserInfo | GenericUriParserOptions.NoUserInfo |
GenericUriParserOptions.NoPort | GenericUriParserOptions.NoPort |
GenericUriParserOptions.NoQuery | GenericUriParserOptions.NoQuery |
GenericUriParserOptions.NoFragment), "avares", -1); GenericUriParserOptions.NoFragment), "avares", -1);
}
} }
} }

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

@ -9,7 +9,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Loads assets compiled into the application binary. /// Loads assets compiled into the application binary.
/// </summary> /// </summary>
[Unstable] [Unstable("IAssetLoader interface and AvaloniaLocator usage is considered unstable. Please use AssetLoader static class instead.")]
public interface IAssetLoader public interface IAssetLoader
{ {
/// <summary> /// <summary>

7
src/Avalonia.Base/Platform/IDrawingContextImpl.cs

@ -1,6 +1,5 @@
using System; using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -168,12 +167,6 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
void PopBitmapBlendMode(); void PopBitmapBlendMode();
/// <summary>
/// Adds a custom draw operation
/// </summary>
/// <param name="custom">Custom draw operation</param>
void Custom(ICustomDrawOperation custom);
/// <summary> /// <summary>
/// Attempts to get an optional feature from the drawing context implementation /// Attempts to get an optional feature from the drawing context implementation
/// </summary> /// </summary>

255
src/Avalonia.Base/Platform/StandardAssetLoader.cs

@ -0,0 +1,255 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using Avalonia.Platform.Internal;
using Avalonia.Utilities;
namespace Avalonia.Platform;
/// <summary>
/// Loads assets compiled into the application binary.
/// </summary>
internal class StandardAssetLoader : IAssetLoader
{
private readonly IAssemblyDescriptorResolver _assemblyDescriptorResolver;
private AssemblyDescriptor? _defaultResmAssembly;
public StandardAssetLoader(IAssemblyDescriptorResolver resolver, Assembly? assembly = null)
{
if (assembly == null)
assembly = Assembly.GetEntryAssembly();
if (assembly != null)
_defaultResmAssembly = new AssemblyDescriptor(assembly);
_assemblyDescriptorResolver = resolver;
}
public StandardAssetLoader(Assembly? assembly = null) : this(new AssemblyDescriptorResolver(), assembly)
{
}
/// <summary>
/// Sets the default assembly from which to load assets for which no assembly is specified.
/// </summary>
/// <param name="assembly">The default assembly.</param>
public void SetDefaultAssembly(Assembly assembly)
{
_defaultResmAssembly = new AssemblyDescriptor(assembly);
}
/// <summary>
/// Checks if an asset with the specified URI exists.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>True if the asset could be found; otherwise false.</returns>
public bool Exists(Uri uri, Uri? baseUri = null)
{
return TryGetAsset(uri, baseUri, out _);
}
/// <summary>
/// Opens the asset with the requested URI.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>A stream containing the asset contents.</returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1;
/// <summary>
/// Opens the asset with the requested URI and returns the asset stream and the
/// assembly containing the asset.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">
/// A base URI to use if <paramref name="uri"/> is relative.
/// </param>
/// <returns>
/// The stream containing the resource contents together with the assembly.
/// </returns>
/// <exception cref="FileNotFoundException">
/// The asset could not be found.
/// </exception>
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null)
{
if (TryGetAsset(uri, baseUri, out var assetDescriptor))
{
return (assetDescriptor.GetStream(), assetDescriptor.Assembly);
}
throw new FileNotFoundException($"The resource {uri} could not be found.");
}
public Assembly? GetAssembly(Uri uri, Uri? baseUri)
{
if (!uri.IsAbsoluteUri && baseUri != null)
{
uri = new Uri(baseUri, uri);
}
if (TryGetAssembly(uri, out var assemblyDescriptor))
{
return assemblyDescriptor.Assembly;
}
return null;
}
/// <summary>
/// Gets all assets of a folder and subfolders that match specified uri.
/// </summary>
/// <param name="uri">The URI.</param>
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri)
{
if (uri.IsAbsoluteResm())
{
if (!TryGetAssembly(uri, out var assembly))
{
assembly = _defaultResmAssembly;
}
return assembly?.Resources?
.Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath()))
.Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ??
Enumerable.Empty<Uri>();
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
if (!TryGetResAsmAndPath(uri, out var assembly, out var path))
{
return Enumerable.Empty<Uri>();
}
if (assembly?.AvaloniaResources == null)
{
return Enumerable.Empty<Uri>();
}
if (path.Length > 0 && path[path.Length - 1] != '/')
{
path += '/';
}
return assembly.AvaloniaResources
.Where(r => r.Key.StartsWith(path, StringComparison.Ordinal))
.Select(x => new Uri($"avares://{assembly.Name}{x.Key}"));
}
return Enumerable.Empty<Uri>();
}
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor)
{
assetDescriptor = null;
if (uri.IsAbsoluteResm())
{
if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly))
{
assembly = _defaultResmAssembly;
}
if (assembly?.Resources != null)
{
var resourceKey = uri.AbsolutePath;
if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor))
{
return true;
}
}
}
uri = uri.EnsureAbsolute(baseUri);
if (uri.IsAvares())
{
if (TryGetResAsmAndPath(uri, out var assembly, out var path))
{
if (assembly.AvaloniaResources == null)
{
return false;
}
if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor))
{
return true;
}
}
}
return false;
}
private bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path)
{
path = uri.GetUnescapeAbsolutePath();
if (TryLoadAssembly(uri.Authority, out assembly))
{
return true;
}
return false;
}
private bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
{
assembly = null;
if (uri != null)
{
if (!uri.IsAbsoluteUri)
{
return false;
}
if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _))
{
return true;
}
if (uri.IsResm())
{
var assemblyName = uri.GetAssemblyNameFromQuery();
if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly))
{
return true;
}
}
}
return false;
}
private bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly)
{
assembly = null;
try
{
assembly = _assemblyDescriptorResolver.GetAssembly(assemblyName);
return true;
}
catch (Exception) { }
return false;
}
}

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

@ -14,7 +14,7 @@ namespace Avalonia.Platform
AssetLoader.RegisterResUriParsers(); AssetLoader.RegisterResUriParsers();
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) .Bind<IRuntimePlatform>().ToConstant(standardPlatform)
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) .Bind<IAssetLoader>().ToConstant(new StandardAssetLoader(assembly))
.Bind<IDynamicLibraryLoader>().ToConstant( .Bind<IDynamicLibraryLoader>().ToConstant(
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
new Net6Loader() new Net6Loader()

27
src/Avalonia.Base/Platform/Storage/NoopStorageProvider.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Platform.Storage.FileIO;
namespace Avalonia.Platform.Storage;
internal class NoopStorageProvider : BclStorageProvider
{
public override bool CanOpen => false;
public override Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFile>>(Array.Empty<IStorageFile>());
}
public override bool CanSave => false;
public override Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
{
return Task.FromResult<IStorageFile?>(null);
}
public override bool CanPickFolder => false;
public override Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
{
return Task.FromResult<IReadOnlyList<IStorageFolder>>(Array.Empty<IStorageFolder>());
}
}

5
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@ -143,11 +143,6 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.PopBitmapBlendMode(); _impl.PopBitmapBlendMode();
} }
public void Custom(ICustomDrawOperation custom)
{
_impl.Custom(custom);
}
public object? GetFeature(Type t) => _impl.GetFeature(t); public object? GetFeature(Type t) => _impl.GetFeature(t);

14
src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
@ -17,7 +18,16 @@ namespace Avalonia.Rendering.SceneGraph
public override void Render(IDrawingContextImpl context) public override void Render(IDrawingContextImpl context)
{ {
Custom.Render(context); using var immediateDrawingContext = new ImmediateDrawingContext(context, false);
try
{
Custom.Render(immediateDrawingContext);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.Visual)
?.Log(Custom, $"Exception in {Custom.GetType().Name}.{nameof(ICustomDrawOperation.Render)} {{0}}", e);
}
} }
public override void Dispose() => Custom.Dispose(); public override void Dispose() => Custom.Dispose();
@ -48,6 +58,6 @@ namespace Avalonia.Rendering.SceneGraph
/// Renders the node to a drawing context. /// Renders the node to a drawing context.
/// </summary> /// </summary>
/// <param name="context">The drawing context.</param> /// <param name="context">The drawing context.</param>
void Render(IDrawingContextImpl context); void Render(ImmediateDrawingContext context);
} }
} }

1
src/Avalonia.Base/StyledElement.cs

@ -289,6 +289,7 @@ namespace Avalonia
public StyledElement? Parent { get; private set; } public StyledElement? Parent { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030:StyledProperty accessors should not have side effects", Justification = "False positive?")]
public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty); public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty);
/// <summary> /// <summary>

1
src/Avalonia.Base/Styling/IThemeVariantHost.cs

@ -7,7 +7,6 @@ namespace Avalonia.Styling;
/// <summary> /// <summary>
/// Interface for the host element with a theme variant. /// Interface for the host element with a theme variant.
/// </summary> /// </summary>
[Unstable]
public interface IThemeVariantHost : IResourceHost public interface IThemeVariantHost : IResourceHost
{ {
/// <summary> /// <summary>

4
src/Avalonia.Base/Styling/ThemeVariant.cs

@ -9,6 +9,10 @@ namespace Avalonia.Styling;
/// Specifies a UI theme variant that should be used for the Control and Application types. /// Specifies a UI theme variant that should be used for the Control and Application types.
/// </summary> /// </summary>
[TypeConverter(typeof(ThemeVariantTypeConverter))] [TypeConverter(typeof(ThemeVariantTypeConverter))]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010:AvaloniaProperty objects should be owned by the type in which they are stored",
Justification = "ActualThemeVariant and RequestedThemeVariant properties are shared Avalonia.Base and Avalonia.Controls projects," +
"but shouldn't be visible on the StyledElement class." +
"Ideally we woould introduce readonly styled properties.")]
public sealed record ThemeVariant public sealed record ThemeVariant
{ {
/// <summary> /// <summary>

89
src/Avalonia.Base/Threading/Dispatcher.Invoke.cs

@ -1,5 +1,4 @@
using System; using System;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -118,11 +117,11 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> synchronously on the /// Executes the specified Func&lt;TResult&gt; synchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <returns> /// <returns>
/// The return value from the delegate being invoked. /// The return value from the delegate being invoked.
@ -136,11 +135,11 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> synchronously on the /// Executes the specified Func&lt;TResult&gt; synchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -156,11 +155,11 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> synchronously on the /// Executes the specified Func&lt;TResult&gt; synchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -183,11 +182,11 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> synchronously on the /// Executes the specified Func&lt;TResult&gt; synchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -249,11 +248,11 @@ public partial class Dispatcher
/// An operation representing the queued delegate to be invoked. /// An operation representing the queued delegate to be invoked.
/// </returns> /// </returns>
/// <remarks> /// <remarks>
/// Note that the default priority is DispatcherPriority.Normal. /// Note that the default priority is DispatcherPriority.Default.
/// </remarks> /// </remarks>
public DispatcherOperation InvokeAsync(Action callback) public DispatcherOperation InvokeAsync(Action callback)
{ {
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None); return InvokeAsync(callback, default, CancellationToken.None);
} }
/// <summary> /// <summary>
@ -317,29 +316,29 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> asynchronously on the /// Executes the specified Func&lt;TResult&gt; asynchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <returns> /// <returns>
/// An operation representing the queued delegate to be invoked. /// An operation representing the queued delegate to be invoked.
/// </returns> /// </returns>
/// <remarks> /// <remarks>
/// Note that the default priority is DispatcherPriority.Normal. /// Note that the default priority is DispatcherPriority.Default.
/// </remarks> /// </remarks>
public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback) public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
{ {
return InvokeAsync(callback, DispatcherPriority.Normal, CancellationToken.None); return InvokeAsync(callback, DispatcherPriority.Default, CancellationToken.None);
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> asynchronously on the /// Executes the specified Func&lt;TResult&gt; asynchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -355,11 +354,11 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<TResult> asynchronously on the /// Executes the specified Func&lt;TResult&gt; asynchronously on the
/// thread that the Dispatcher was created on. /// thread that the Dispatcher was created on.
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<TResult> delegate to invoke through the dispatcher. /// A Func&lt;TResult&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -479,7 +478,7 @@ public partial class Dispatcher
// operation has already started when the timeout expires, // operation has already started when the timeout expires,
// we still wait for it to complete. This is different // we still wait for it to complete. This is different
// than simply waiting on the operation with a timeout // than simply waiting on the operation with a timeout
// because we are the ones queueing the dispatcher // because we are the ones queuing the dispatcher
// operation, not the caller. We can't leave the operation // operation, not the caller. We can't leave the operation
// in a state that it might execute if we return that it did not // in a state that it might execute if we return that it did not
// invoke. // invoke.
@ -492,12 +491,12 @@ public partial class Dispatcher
// Old async semantics return from Wait without // Old async semantics return from Wait without
// throwing an exception if the operation was aborted. // throwing an exception if the operation was aborted.
// There is no need to test the timout condition, since // There is no need to test the timeout condition, since
// the old async semantics would just return the result, // the old async semantics would just return the result,
// which would be null. // which would be null.
// This should not block because either the operation // This should not block because either the operation
// is using the old async sematics, or the operation // is using the old async semantics, or the operation
// completed successfully. // completed successfully.
result = operation.GetResult(); result = operation.GetResult();
} }
@ -543,11 +542,23 @@ public partial class Dispatcher
} }
/// <summary> /// <summary>
/// Executes the specified Func<Task> asynchronously on the /// Executes the specified Func&lt;Task&gt; asynchronously on the
/// thread that the Dispatcher was created on /// thread that the Dispatcher was created on
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="callback">
/// A Func<Task> delegate to invoke through the dispatcher. /// A Func&lt;Task&gt; delegate to invoke through the dispatcher.
/// </param>
/// <returns>
/// An task that completes after the task returned from callback finishes.
/// </returns>
public Task InvokeAsync(Func<Task> callback) => InvokeAsync(callback, DispatcherPriority.Default);
/// <summary>
/// Executes the specified Func&lt;Task&gt; asynchronously on the
/// thread that the Dispatcher was created on
/// </summary>
/// <param name="callback">
/// A Func&lt;Task&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -557,18 +568,36 @@ public partial class Dispatcher
/// <returns> /// <returns>
/// An task that completes after the task returned from callback finishes /// An task that completes after the task returned from callback finishes
/// </returns> /// </returns>
public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority = default) public Task InvokeAsync(Func<Task> callback, DispatcherPriority priority)
{ {
_ = callback ?? throw new ArgumentNullException(nameof(callback)); _ = callback ?? throw new ArgumentNullException(nameof(callback));
return InvokeAsync<Task>(callback, priority).GetTask().Unwrap(); return InvokeAsync<Task>(callback, priority).GetTask().Unwrap();
} }
/// <summary>
/// Executes the specified Func&lt;Task&lt;TResult&gt;&gt; asynchronously on the
/// thread that the Dispatcher was created on
/// </summary>
/// <param name="action">
/// A Func&lt;Task&lt;TResult&gt;&gt; delegate to invoke through the dispatcher.
/// </param>
/// <param name="priority">
/// The priority that determines in what order the specified
/// callback is invoked relative to the other pending operations
/// in the Dispatcher.
/// </param>
/// <returns>
/// An task that completes after the task returned from callback finishes
/// </returns>
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action) =>
InvokeAsync(action, DispatcherPriority.Default);
/// <summary> /// <summary>
/// Executes the specified Func<Task<TResult>> asynchronously on the /// Executes the specified Func&lt;Task&lt;TResult&gt;&gt; asynchronously on the
/// thread that the Dispatcher was created on /// thread that the Dispatcher was created on
/// </summary> /// </summary>
/// <param name="callback"> /// <param name="action">
/// A Func<Task<TResult>> delegate to invoke through the dispatcher. /// A Func&lt;Task&lt;TResult&gt;&gt; delegate to invoke through the dispatcher.
/// </param> /// </param>
/// <param name="priority"> /// <param name="priority">
/// The priority that determines in what order the specified /// The priority that determines in what order the specified
@ -578,7 +607,7 @@ public partial class Dispatcher
/// <returns> /// <returns>
/// An task that completes after the task returned from callback finishes /// An task that completes after the task returned from callback finishes
/// </returns> /// </returns>
public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority = default) public Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> action, DispatcherPriority priority)
{ {
_ = action ?? throw new ArgumentNullException(nameof(action)); _ = action ?? throw new ArgumentNullException(nameof(action));
return InvokeAsync<Task<TResult>>(action, priority).GetTask().Unwrap(); return InvokeAsync<Task<TResult>>(action, priority).GetTask().Unwrap();
@ -595,4 +624,4 @@ public partial class Dispatcher
_ = action ?? throw new ArgumentNullException(nameof(action)); _ = action ?? throw new ArgumentNullException(nameof(action));
InvokeAsyncImpl(new SendOrPostCallbackDispatcherOperation(this, priority, action, arg, true), CancellationToken.None); InvokeAsyncImpl(new SendOrPostCallbackDispatcherOperation(this, priority, action, arg, true), CancellationToken.None);
} }
} }

57
src/Avalonia.Base/Threading/DispatcherFrame.cs

@ -91,31 +91,44 @@ public class DispatcherFrame
internal void Run(IControlledDispatcherImpl impl) internal void Run(IControlledDispatcherImpl impl)
{ {
// Since the actual platform run loop is controlled by a Cancellation token, we are restarting Dispatcher.VerifyAccess();
// it if frame still needs to run
while (Continue) // Since the actual platform run loop is controlled by a Cancellation token, we have an
RunCore(impl); // outer loop that restarts the platform one in case Continue was set to true after being set to false
} while (true)
private void RunCore(IControlledDispatcherImpl impl)
{
if (_isRunning)
throw new InvalidOperationException("This frame is already running");
_isRunning = true;
try
{
_cancellationTokenSource = new CancellationTokenSource();
// Wake up the dispatcher in case it has pending jobs
Dispatcher.RequestProcessing();
impl.RunLoop(_cancellationTokenSource.Token);
}
finally
{ {
_isRunning = false; // Take the instance lock since `Continue` is changed from one too
_cancellationTokenSource?.Cancel(); lock (Dispatcher.InstanceLock)
_cancellationTokenSource = null; {
if (!Continue)
return;
if (_isRunning)
throw new InvalidOperationException("This frame is already running");
_cancellationTokenSource = new CancellationTokenSource();
_isRunning = true;
}
try
{
// Wake up the dispatcher in case it has pending jobs
Dispatcher.RequestProcessing();
impl.RunLoop(_cancellationTokenSource.Token);
}
finally
{
lock (Dispatcher.InstanceLock)
{
_isRunning = false;
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
}
} }
} }
internal void MaybeExitOnDispatcherRequest() internal void MaybeExitOnDispatcherRequest()
{ {

3
src/Avalonia.Base/Threading/DispatcherPriority.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.Threading namespace Avalonia.Threading
{ {
@ -100,7 +101,7 @@ namespace Avalonia.Threading
/// <summary> /// <summary>
/// The job will be processed with the same priority as data binding. /// The job will be processed with the same priority as data binding.
/// </summary> /// </summary>
[Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = new(Layout); [Obsolete("WPF compatibility"), EditorBrowsable(EditorBrowsableState.Never)] public static readonly DispatcherPriority DataBind = new(Layout);
/// <summary> /// <summary>
/// The job will be processed with normal priority. /// The job will be processed with normal priority.

5
src/Avalonia.Base/Utilities/SpanHelpers.cs

@ -4,7 +4,10 @@ using System.Runtime.CompilerServices;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public static class SpanHelpers #if !BUILDTASK
public
#endif
static class SpanHelpers
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseUInt(this ReadOnlySpan<char> span, NumberStyles style, IFormatProvider provider, out uint value) public static bool TryParseUInt(this ReadOnlySpan<char> span, NumberStyles style, IFormatProvider provider, out uint value)

1
src/Avalonia.Base/Visual.cs

@ -329,6 +329,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Gets the control's parent visual. /// Gets the control's parent visual.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "GetVisualParent extension method is supposed to be used instead.")]
internal Visual? VisualParent => _visualParent; internal Visual? VisualParent => _visualParent;
/// <summary> /// <summary>

5
src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs

@ -1,14 +1,15 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.VisualTree namespace Avalonia.VisualTree
{ {
[Obsolete("Internal API, will be removed in future versions, you've been warned")] [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
public interface IVisualWithRoundRectClip public interface IVisualWithRoundRectClip
{ {
/// <summary> /// <summary>
/// Gets a value indicating the corner radius of control's clip bounds /// Gets a value indicating the corner radius of control's clip bounds
/// </summary> /// </summary>
[Obsolete("Internal API, will be removed in future versions, you've been warned")] [Obsolete("Internal API, will be removed in future versions, you've been warned"), EditorBrowsable(EditorBrowsableState.Never)]
CornerRadius ClipToBoundsRadius { get; } CornerRadius ClipToBoundsRadius { get; }
} }

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

@ -115,6 +115,7 @@
</Compile> </Compile>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> <Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
<Compile Include="..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" /> <Compile Include="..\Avalonia.Base\Compatibility\TrimmingAttributes.cs" Link="TrimmingAttributes.cs" Visible="False" />
<Compile Include="..\Avalonia.Base\Utilities\SpanHelpers.cs" Link="Utilities\SpanHelpers.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" /> <PackageReference Include="Mono.Cecil" Version="0.11.4" />

26
src/Avalonia.Build.Tasks/SpanCompat.cs

@ -85,31 +85,7 @@ namespace System
{ {
return TrimStart().TrimEnd(); return TrimStart().TrimEnd();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseUInt(NumberStyles style, IFormatProvider provider, out uint value)
{
return uint.TryParse(ToString(), style, provider, out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseInt(out int value)
{
return int.TryParse(ToString(), out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseDouble(NumberStyles style, IFormatProvider provider, out double value)
{
return double.TryParse(ToString(), style, provider, out value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryParseByte(NumberStyles style, IFormatProvider provider, out byte value)
{
return byte.TryParse(ToString(), style, provider, out value);
}
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
internal int IndexOf(ReadOnlySpan<char> v, StringComparison ordinal, int start = 0) internal int IndexOf(ReadOnlySpan<char> v, StringComparison ordinal, int start = 0)

39
src/Avalonia.Controls/AppBuilder.cs

@ -118,6 +118,43 @@ namespace Avalonia
}; };
} }
/// <summary>
/// Begin configuring an <see cref="Application"/>.
/// Should only be used for testing and design purposes, as it relies on dynamic code.
/// </summary>
/// <param name="entryPointType">
/// Parameter from which <see cref="AppBuilder"/> should be created.
/// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
/// </param>
/// <returns>An <see cref="AppBuilder"/> instance. If can't be created, thrown an exception.</returns>
internal static AppBuilder Configure(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
Type entryPointType)
{
var appBuilderObj = entryPointType
.GetMethod(
"BuildAvaloniaApp",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,
null,
Array.Empty<Type>(),
null)?
.Invoke(null, Array.Empty<object?>());
if (appBuilderObj is AppBuilder appBuilder)
{
return appBuilder;
}
if (typeof(Application).IsAssignableFrom(entryPointType))
{
return Configure(() => (Application)Activator.CreateInstance(entryPointType)!);
}
throw new InvalidOperationException(
$"Unable to create AppBuilder from type {entryPointType.Name}." +
$"Input type either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application type.");
}
protected AppBuilder Self => this; protected AppBuilder Self => this;
public AppBuilder AfterSetup(Action<AppBuilder> callback) public AppBuilder AfterSetup(Action<AppBuilder> callback)
@ -206,7 +243,7 @@ namespace Avalonia
_optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToFunc(options); }; _optionsInitializers += () => { AvaloniaLocator.CurrentMutable.Bind<T>().ToFunc(options); };
return Self; return Self;
} }
/// <summary> /// <summary>
/// Registers an action that is executed with the current font manager. /// Registers an action that is executed with the current font manager.
/// </summary> /// </summary>

2
src/Avalonia.Controls/Application.cs

@ -94,6 +94,8 @@ namespace Avalonia
} }
/// <inheritdoc /> /// <inheritdoc />
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty); public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
/// <summary> /// <summary>

2
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -2042,6 +2042,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Identifies the Value dependency property. /// Identifies the Value dependency property.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002:AvaloniaProperty objects should not be owned by a generic type",
Justification = "This property is not supposed to be used from XAML.")]
public static readonly StyledProperty<T> ValueProperty = public static readonly StyledProperty<T> ValueProperty =
AvaloniaProperty.Register<BindingEvaluator<T>, T>(nameof(Value)); AvaloniaProperty.Register<BindingEvaluator<T>, T>(nameof(Value));

2
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Specifies how text in the text box portion of the <see cref="AutoCompleteBox" /> /// Specifies how text in the text box portion of the <see cref="AutoCompleteBox" />
/// control is used to filter items specified by the <see cref="AutoCompleteBox.Items" /> /// control is used to filter items specified by the <see cref="AutoCompleteBox.ItemsSource" />
/// property for display in the drop-down. /// property for display in the drop-down.
/// </summary> /// </summary>
public enum AutoCompleteFilterMode public enum AutoCompleteFilterMode

6
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -17,5 +17,11 @@
<InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)"/> <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)"/>
<InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.LeakTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Headless, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Headless.XUnit, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Native, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.DesignerSupport.Remote, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Browser, PublicKey=$(AvaloniaPublicKey)" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
src/Avalonia.Controls/ColumnDefinition.cs

@ -46,8 +46,8 @@ namespace Avalonia.Controls
/// <param name="value">The width of the column.</param> /// <param name="value">The width of the column.</param>
/// <param name="type">The width unit of the column.</param> /// <param name="type">The width unit of the column.</param>
public ColumnDefinition(double value, GridUnitType type) public ColumnDefinition(double value, GridUnitType type)
: this(new GridLength(value, type))
{ {
Width = new GridLength(value, type);
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls/ContextMenu.cs

@ -56,13 +56,15 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="Placement"/> property. /// Defines the <see cref="Placement"/> property.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
Justification = "We keep PlacementModeProperty for backward compatibility.")]
public static readonly StyledProperty<PlacementMode> PlacementProperty = public static readonly StyledProperty<PlacementMode> PlacementProperty =
Popup.PlacementProperty.AddOwner<ContextMenu>(); Popup.PlacementProperty.AddOwner<ContextMenu>();
/// <summary> /// <summary>
/// Defines the <see cref="PlacementMode"/> property. /// Defines the <see cref="PlacementMode"/> property.
/// </summary> /// </summary>
[Obsolete("Use the Placement property instead.")] [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly StyledProperty<PlacementMode> PlacementModeProperty = PlacementProperty; public static readonly StyledProperty<PlacementMode> PlacementModeProperty = PlacementProperty;
/// <summary> /// <summary>
@ -155,7 +157,7 @@ namespace Avalonia.Controls
} }
/// <inheritdoc cref="Placement"/> /// <inheritdoc cref="Placement"/>
[Obsolete("Use the Placement property instead.")] [Obsolete("Use the Placement property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public PlacementMode PlacementMode public PlacementMode PlacementMode
{ {
get => GetValue(PlacementProperty); get => GetValue(PlacementProperty);

12
src/Avalonia.Controls/DefinitionBase.cs

@ -37,7 +37,7 @@ namespace Avalonia.Controls
{ {
// start with getting SharedSizeGroup value. // start with getting SharedSizeGroup value.
// this property is NOT inherited which should result in better overall perf. // this property is NOT inherited which should result in better overall perf.
if (SharedSizeGroup is { } sharedSizeGroupId && PrivateSharedSizeScope is { } privateSharedSizeScope) if (SharedSizeGroup is { } sharedSizeGroupId && GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
{ {
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId); _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
_sharedState.AddMember(this); _sharedState.AddMember(this);
@ -333,7 +333,7 @@ namespace Avalonia.Controls
if (definition._sharedState == null if (definition._sharedState == null
&& sharedSizeGroupId != null && sharedSizeGroupId != null
&& definition.PrivateSharedSizeScope is { } privateSharedSizeScope) && definition.GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
{ {
// if definition is not registered and both: shared size group id AND private shared scope // if definition is not registered and both: shared size group id AND private shared scope
// are available, then register definition. // are available, then register definition.
@ -412,14 +412,6 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Private getter of shared state collection dynamic property.
/// </summary>
private SharedSizeScope? PrivateSharedSizeScope
{
get { return GetValue(PrivateSharedSizeScopeProperty); }
}
/// <summary> /// <summary>
/// Convenience accessor to UseSharedMinimum flag /// Convenience accessor to UseSharedMinimum flag
/// </summary> /// </summary>

2
src/Avalonia.Controls/DefinitionList.cs

@ -1,9 +1,11 @@
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Metadata;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
[AvaloniaList(Separators = new [] { ",", " " })]
public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase public abstract class DefinitionList<T> : AvaloniaList<T> where T : DefinitionBase
{ {
public DefinitionList() public DefinitionList()

2
src/Avalonia.Controls/Documents/Span.cs

@ -18,6 +18,8 @@ namespace Avalonia.Controls.Documents
AvaloniaProperty.Register<Span, InlineCollection>( AvaloniaProperty.Register<Span, InlineCollection>(
nameof(Inlines)); nameof(Inlines));
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public Span() public Span()
{ {
Inlines = new InlineCollection Inlines = new InlineCollection

4
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -47,7 +47,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
set set
{ {
_clientSize = value; _clientSize = value;
Resized?.Invoke(value, PlatformResizeReason.Unspecified); Resized?.Invoke(value, WindowResizeReason.Unspecified);
} }
} }
@ -65,7 +65,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<RawInputEventArgs>? Input { get; set; } public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect>? Paint { get; set; } public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason>? Resized { get; set; } public Action<Size, WindowResizeReason>? Resized { get; set; }
public Action<double>? ScalingChanged { get; set; } public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }

2
src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs

@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the <see cref="OverlayInputPassThroughElement"/> property /// Defines the <see cref="OverlayInputPassThroughElement"/> property
/// </summary> /// </summary>
public static readonly StyledProperty<IInputElement?> OverlayInputPassThroughElementProperty = public static readonly StyledProperty<IInputElement?> OverlayInputPassThroughElementProperty =
Popup.OverlayInputPassThroughElementProperty.AddOwner<FlyoutBase>(); Popup.OverlayInputPassThroughElementProperty.AddOwner<PopupFlyoutBase>();
private readonly Lazy<Popup> _popupLazy; private readonly Lazy<Popup> _popupLazy;
private Rect? _enlargedPopupRect; private Rect? _enlargedPopupRect;

48
src/Avalonia.Controls/Generators/ItemContainerGenerator.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.Controls.Generators namespace Avalonia.Controls.Generators
{ {
@ -7,8 +8,8 @@ namespace Avalonia.Controls.Generators
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// When creating a container for an item from a <see cref="VirtualizingPanel"/>, the following /// When creating a container for an item from a <see cref="VirtualizingPanel"/>, the following
/// method order should be followed: /// process should be followed:
/// ///
/// - <see cref="IsItemItsOwnContainer(Control)"/> should first be called if the item is /// - <see cref="IsItemItsOwnContainer(Control)"/> should first be called if the item is
/// derived from the <see cref="Control"/> class. If this method returns true then the /// derived from the <see cref="Control"/> class. If this method returns true then the
/// item itself should be used as the container. /// item itself should be used as the container.
@ -19,9 +20,29 @@ namespace Avalonia.Controls.Generators
/// - The container should then be added to the panel using /// - The container should then be added to the panel using
/// <see cref="VirtualizingPanel.AddInternalChild(Control)"/> /// <see cref="VirtualizingPanel.AddInternalChild(Control)"/>
/// - Finally, <see cref="ItemContainerPrepared(Control, object?, int)"/> should be called. /// - Finally, <see cref="ItemContainerPrepared(Control, object?, int)"/> should be called.
/// - When the item is ready to be recycled, <see cref="ClearItemContainer(Control)"/> should
/// be called if <see cref="IsItemItsOwnContainer(Control)"/> returned false.
/// ///
/// NOTE: If <see cref="IsItemItsOwnContainer(Control)"/> in the first step above returns true
/// then the above steps should be carried out a single time; the first time the item is
/// displayed. Otherwise the steps should be carried out each time a new container is realized
/// for an item.
///
/// When unrealizing a container, the following process should be followed:
///
/// - If <see cref="IsItemItsOwnContainer(Control)"/> for the item returned true then the item
/// cannot be unrealized or recycled.
/// - Otherwise, <see cref="ClearItemContainer(Control)"/> should be called for the container
/// - If recycling is supported then the container should be added to a recycle pool.
/// - It is assumed that recyclable containers will not be removed from the panel but instead
/// hidden from view using e.g. `container.IsVisible = false`.
///
/// When recycling an unrealized container, the following process should be followed:
///
/// - An element should be taken from the recycle pool.
/// - The container should be made visible.
/// - <see cref="PrepareItemContainer(Control, object?, int)"/> method should be called for the
/// container.
/// - <see cref="ItemContainerPrepared(Control, object?, int)"/> should be called.
///
/// NOTE: Although this class is similar to that found in WPF/UWP, in Avalonia this class only /// NOTE: Although this class is similar to that found in WPF/UWP, in Avalonia this class only
/// concerns itself with generating and clearing item containers; it does not maintain a /// concerns itself with generating and clearing item containers; it does not maintain a
/// record of the currently realized containers, that responsibility is delegated to the /// record of the currently realized containers, that responsibility is delegated to the
@ -65,7 +86,7 @@ namespace Avalonia.Controls.Generators
/// <param name="index">The index of the item to display.</param> /// <param name="index">The index of the item to display.</param>
/// <remarks> /// <remarks>
/// If <see cref="IsItemItsOwnContainer(Control)"/> is true for an item, then this method /// If <see cref="IsItemItsOwnContainer(Control)"/> is true for an item, then this method
/// only needs to be called a single time, otherwise this method should be called after the /// must only be called a single time, otherwise this method must be called after the
/// container is created, and each subsequent time the container is recycled to display a /// container is created, and each subsequent time the container is recycled to display a
/// new item. /// new item.
/// </remarks> /// </remarks>
@ -80,10 +101,11 @@ namespace Avalonia.Controls.Generators
/// <param name="item">The item being displayed.</param> /// <param name="item">The item being displayed.</param>
/// <param name="index">The index of the item being displayed.</param> /// <param name="index">The index of the item being displayed.</param>
/// <remarks> /// <remarks>
/// This method should be called when a container has been fully prepared and added /// This method must be called when a container has been fully prepared and added
/// to the logical and visual trees, but may be called before a layout pass has completed. /// to the logical and visual trees, but may be called before a layout pass has completed.
/// It should be called regardless of the result of /// It must be called regardless of the result of
/// <see cref="IsItemItsOwnContainer(Control)"/>. /// <see cref="IsItemItsOwnContainer(Control)"/> but if that method returned true then
/// must be called only a single time.
/// </remarks> /// </remarks>
public void ItemContainerPrepared(Control container, object? item, int index) => public void ItemContainerPrepared(Control container, object? item, int index) =>
_owner.ItemContainerPrepared(container, item, index); _owner.ItemContainerPrepared(container, item, index);
@ -102,12 +124,18 @@ namespace Avalonia.Controls.Generators
/// Undoes the effects of the <see cref="PrepareItemContainer(Control, object, int)"/> method. /// Undoes the effects of the <see cref="PrepareItemContainer(Control, object, int)"/> method.
/// </summary> /// </summary>
/// <param name="container">The container control.</param> /// <param name="container">The container control.</param>
/// <remarks>
/// This method must be called when a container is unrealized. The container must have
/// already have been removed from the virtualizing panel's list of realized containers before
/// this method is called. This method must not be called if
/// <see cref="IsItemItsOwnContainer"/> returned true for the item.
/// </remarks>
public void ClearItemContainer(Control container) => _owner.ClearItemContainer(container); public void ClearItemContainer(Control container) => _owner.ClearItemContainer(container);
[Obsolete("Use ItemsControl.ContainerFromIndex")] [Obsolete("Use ItemsControl.ContainerFromIndex"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index); public Control? ContainerFromIndex(int index) => _owner.ContainerFromIndex(index);
[Obsolete("Use ItemsControl.IndexFromContainer")] [Obsolete("Use ItemsControl.IndexFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container); public int IndexFromContainer(Control container) => _owner.IndexFromContainer(container);
} }
} }

7
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
namespace Avalonia.Controls.Generators namespace Avalonia.Controls.Generators
{ {
@ -20,13 +21,13 @@ namespace Avalonia.Controls.Generators
internal TreeContainerIndex(TreeView owner) => _owner = owner; internal TreeContainerIndex(TreeView owner) => _owner = owner;
[Obsolete("Use TreeView.GetRealizedTreeContainers")] [Obsolete("Use TreeView.GetRealizedTreeContainers"), EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<Control> Containers => _owner.GetRealizedTreeContainers(); public IEnumerable<Control> Containers => _owner.GetRealizedTreeContainers();
[Obsolete("Use TreeView.TreeContainerFromItem")] [Obsolete("Use TreeView.TreeContainerFromItem"), EditorBrowsable(EditorBrowsableState.Never)]
public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item); public Control? ContainerFromItem(object item) => _owner.TreeContainerFromItem(item);
[Obsolete("Use TreeView.TreeItemFromContainer")] [Obsolete("Use TreeView.TreeItemFromContainer"), EditorBrowsable(EditorBrowsableState.Never)]
public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container); public object? ItemFromContainer(Control container) => _owner.TreeItemFromContainer(container);
} }
} }

3
src/Avalonia.Controls/ItemsControl.cs

@ -2,6 +2,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
@ -626,7 +627,7 @@ namespace Avalonia.Controls
/// TreeView to be able to create a <see cref="TreeItemContainerGenerator"/>. Can be /// TreeView to be able to create a <see cref="TreeItemContainerGenerator"/>. Can be
/// removed in 12.0. /// removed in 12.0.
/// </remarks> /// </remarks>
[Obsolete] [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected virtual ItemContainerGenerator CreateItemContainerGenerator() private protected virtual ItemContainerGenerator CreateItemContainerGenerator()
{ {
return new ItemContainerGenerator(this); return new ItemContainerGenerator(this);

4
src/Avalonia.Controls/LayoutTransformControl.cs

@ -63,7 +63,7 @@ namespace Avalonia.Controls
{ {
if (TransformRoot == null || LayoutTransform == null) if (TransformRoot == null || LayoutTransform == null)
{ {
LayoutTransform = RenderTransform; SetCurrentValue(LayoutTransformProperty, RenderTransform);
return base.ArrangeOverride(finalSize); return base.ArrangeOverride(finalSize);
} }
@ -176,7 +176,7 @@ namespace Avalonia.Controls
else else
{ {
_renderTransformChangedEvent?.Dispose(); _renderTransformChangedEvent?.Dispose();
LayoutTransform = null; ClearValue(LayoutTransformProperty);
} }
} }
} }

8
src/Avalonia.Controls/ListBox.cs

@ -29,18 +29,24 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Defines the <see cref="SelectedItems"/> property. /// Defines the <see cref="SelectedItems"/> property.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new DirectProperty<SelectingItemsControl, IList?> SelectedItemsProperty = public static readonly new DirectProperty<SelectingItemsControl, IList?> SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty; SelectingItemsControl.SelectedItemsProperty;
/// <summary> /// <summary>
/// Defines the <see cref="Selection"/> property. /// Defines the <see cref="Selection"/> property.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new DirectProperty<SelectingItemsControl, ISelectionModel> SelectionProperty = public static readonly new DirectProperty<SelectingItemsControl, ISelectionModel> SelectionProperty =
SelectingItemsControl.SelectionProperty; SelectingItemsControl.SelectionProperty;
/// <summary> /// <summary>
/// Defines the <see cref="SelectionMode"/> property. /// Defines the <see cref="SelectionMode"/> property.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new StyledProperty<SelectionMode> SelectionModeProperty = public static readonly new StyledProperty<SelectionMode> SelectionModeProperty =
SelectingItemsControl.SelectionModeProperty; SelectingItemsControl.SelectionModeProperty;
@ -84,6 +90,8 @@ namespace Avalonia.Controls
/// Note that the selection mode only applies to selections made via user interaction. /// Note that the selection mode only applies to selections made via user interaction.
/// Multiple selections can be made programmatically regardless of the value of this property. /// Multiple selections can be made programmatically regardless of the value of this property.
/// </remarks> /// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public new SelectionMode SelectionMode public new SelectionMode SelectionMode
{ {
get { return base.SelectionMode; } get { return base.SelectionMode; }

6
src/Avalonia.Controls/MenuItem.cs

@ -53,12 +53,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<KeyGesture?> InputGestureProperty = public static readonly StyledProperty<KeyGesture?> InputGestureProperty =
AvaloniaProperty.Register<MenuItem, KeyGesture?>(nameof(InputGesture)); AvaloniaProperty.Register<MenuItem, KeyGesture?>(nameof(InputGesture));
/// <summary>
/// Defines the <see cref="IsSelected"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsSelectedProperty =
SelectingItemsControl.IsSelectedProperty.AddOwner<MenuItem>();
/// <summary> /// <summary>
/// Defines the <see cref="IsSubMenuOpen"/> property. /// Defines the <see cref="IsSubMenuOpen"/> property.
/// </summary> /// </summary>

3
src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Metadata; using Avalonia.Metadata;
@ -7,7 +8,7 @@ namespace Avalonia.Controls.Platform
/// <summary> /// <summary>
/// Defines a platform-specific system dialog implementation. /// Defines a platform-specific system dialog implementation.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
[Unstable] [Unstable]
public interface ISystemDialogImpl public interface ISystemDialogImpl
{ {

3
src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
@ -10,7 +11,7 @@ namespace Avalonia.Controls.Platform
/// <summary> /// <summary>
/// Defines a platform-specific system dialog implementation. /// Defines a platform-specific system dialog implementation.
/// </summary> /// </summary>
[Obsolete] [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
internal class SystemDialogImpl : ISystemDialogImpl internal class SystemDialogImpl : ISystemDialogImpl
{ {
public async Task<string[]?> ShowFileDialogAsync(FileDialog dialog, Window parent) public async Task<string[]?> ShowFileDialogAsync(FileDialog dialog, Window parent)

36
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -9,40 +9,6 @@ using Avalonia.Rendering;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
/// <summary>
/// Describes the reason for a <see cref="ITopLevelImpl.Resized"/> message.
/// </summary>
public enum PlatformResizeReason
{
/// <summary>
/// The resize reason is unknown or unspecified.
/// </summary>
Unspecified,
/// <summary>
/// The resize was due to the user resizing the window, for example by dragging the
/// window frame.
/// </summary>
User,
/// <summary>
/// The resize was initiated by the application, for example by setting one of the sizing-
/// related properties on <see cref="Window"/> such as <see cref="Layoutable.Width"/> or
/// <see cref="Layoutable.Height"/>.
/// </summary>
Application,
/// <summary>
/// The resize was initiated by the layout system.
/// </summary>
Layout,
/// <summary>
/// The resize was due to a change in DPI.
/// </summary>
DpiChange,
}
/// <summary> /// <summary>
/// Defines a platform-specific top-level window implementation. /// Defines a platform-specific top-level window implementation.
/// </summary> /// </summary>
@ -93,7 +59,7 @@ namespace Avalonia.Platform
/// <summary> /// <summary>
/// Gets or sets a method called when the toplevel is resized. /// Gets or sets a method called when the toplevel is resized.
/// </summary> /// </summary>
Action<Size, PlatformResizeReason>? Resized { get; set; } Action<Size, WindowResizeReason>? Resized { get; set; }
/// <summary> /// <summary>
/// Gets or sets a method called when the toplevel's scaling changes. /// Gets or sets a method called when the toplevel's scaling changes.

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

Loading…
Cancel
Save