Browse Source

Merge

pull/9584/head
Benedikt Stebner 3 years ago
parent
commit
4782e1bc11
  1. 17
      .editorconfig
  2. 30
      Avalonia.sln
  3. 2
      build/DevAnalyzers.props
  4. 32
      build/ExternalConsumers.props
  5. 6
      native/Avalonia.Native/src/OSX/AvnView.mm
  6. 2
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  7. 24
      nukebuild/RefAssemblyGenerator.cs
  8. 5
      nukebuild/numerge.config
  9. 21
      packages/Avalonia/Avalonia.csproj
  10. 2
      packages/Avalonia/Avalonia.props
  11. 63
      readme.md
  12. 11
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  13. 1
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  14. 4
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  15. 2
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  16. 11
      samples/Sandbox/MainWindow.axaml.cs
  17. 2
      samples/Sandbox/Sandbox.csproj
  18. 12
      src/Avalonia.Base/Animation/Animation.cs
  19. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  20. 2
      src/Avalonia.Base/ClassBindingManager.cs
  21. 3
      src/Avalonia.Base/Controls/IThemeVariantProvider.cs
  22. 3
      src/Avalonia.Base/Data/BindingPriority.cs
  23. 3
      src/Avalonia.Base/Data/InstancedBinding.cs
  24. 13
      src/Avalonia.Base/Input/Cursor.cs
  25. 3
      src/Avalonia.Base/Input/DataFormats.cs
  26. 3
      src/Avalonia.Base/Input/DataObjectExtensions.cs
  27. 3
      src/Avalonia.Base/Input/DragEventArgs.cs
  28. 3
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  29. 12
      src/Avalonia.Base/Input/PointerEventArgs.cs
  30. 3
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  31. 10
      src/Avalonia.Base/Layout/LayoutManager.cs
  32. 18
      src/Avalonia.Base/Layout/LayoutQueue.cs
  33. 40
      src/Avalonia.Base/Layout/Layoutable.cs
  34. 12
      src/Avalonia.Base/Media/Color.cs
  35. 2
      src/Avalonia.Base/Media/DashStyle.cs
  36. 7
      src/Avalonia.Base/Media/DrawingContext.cs
  37. 2
      src/Avalonia.Base/Media/GeometryDrawing.cs
  38. 2
      src/Avalonia.Base/Media/GradientBrush.cs
  39. 36
      src/Avalonia.Base/Media/HslColor.cs
  40. 1
      src/Avalonia.Base/Media/ImmediateDrawingContext.cs
  41. 16
      src/Avalonia.Base/Media/PlatformDrawingContext.cs
  42. 2
      src/Avalonia.Base/Media/PolyLineSegment.cs
  43. 4
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  44. 33
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  45. 2
      src/Avalonia.Base/Media/TransformGroup.cs
  46. 23
      src/Avalonia.Base/Metadata/AvaloniaListAttribute.cs
  47. 22
      src/Avalonia.Base/Metadata/UnstableAttribute.cs
  48. 295
      src/Avalonia.Base/Platform/AssetLoader.cs
  49. 2
      src/Avalonia.Base/Platform/IAssetLoader.cs
  50. 5
      src/Avalonia.Base/Platform/IDrawingContextImpl.cs
  51. 255
      src/Avalonia.Base/Platform/StandardAssetLoader.cs
  52. 2
      src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs
  53. 5
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  54. 14
      src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs
  55. 1
      src/Avalonia.Base/StyledElement.cs
  56. 4
      src/Avalonia.Base/Styling/ThemeVariant.cs
  57. 89
      src/Avalonia.Base/Threading/Dispatcher.Invoke.cs
  58. 57
      src/Avalonia.Base/Threading/DispatcherFrame.cs
  59. 3
      src/Avalonia.Base/Threading/DispatcherPriority.cs
  60. 1
      src/Avalonia.Base/Visual.cs
  61. 5
      src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs
  62. 2
      src/Avalonia.Controls/Application.cs
  63. 2
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  64. 2
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
  65. 2
      src/Avalonia.Controls/ColumnDefinition.cs
  66. 6
      src/Avalonia.Controls/ContextMenu.cs
  67. 12
      src/Avalonia.Controls/DefinitionBase.cs
  68. 2
      src/Avalonia.Controls/DefinitionList.cs
  69. 2
      src/Avalonia.Controls/Documents/Span.cs
  70. 2
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
  71. 48
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  72. 7
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  73. 3
      src/Avalonia.Controls/ItemsControl.cs
  74. 4
      src/Avalonia.Controls/LayoutTransformControl.cs
  75. 8
      src/Avalonia.Controls/ListBox.cs
  76. 6
      src/Avalonia.Controls/MenuItem.cs
  77. 3
      src/Avalonia.Controls/Platform/Dialogs/ISystemDialogImpl.cs
  78. 3
      src/Avalonia.Controls/Platform/Dialogs/SystemDialogImpl.cs
  79. 6
      src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs
  80. 5
      src/Avalonia.Controls/Platform/Screen.cs
  81. 9
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  82. 5
      src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs
  83. 4
      src/Avalonia.Controls/Primitives/Popup.cs
  84. 19
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  85. 2
      src/Avalonia.Controls/Primitives/UniformGrid.cs
  86. 3
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  87. 95
      src/Avalonia.Controls/ProgressBar.cs
  88. 1
      src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs
  89. 4
      src/Avalonia.Controls/RowDefinition.cs
  90. 41
      src/Avalonia.Controls/Screens.cs
  91. 6
      src/Avalonia.Controls/SplitView/SplitView.cs
  92. 6
      src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs
  93. 15
      src/Avalonia.Controls/SystemDialog.cs
  94. 2
      src/Avalonia.Controls/TabControl.cs
  95. 4
      src/Avalonia.Controls/TabItem.cs
  96. 26
      src/Avalonia.Controls/TextBlock.cs
  97. 2
      src/Avalonia.Controls/ToggleSwitch.cs
  98. 12
      src/Avalonia.Controls/TopLevel.cs
  99. 3
      src/Avalonia.Controls/TreeView.cs
  100. 6
      src/Avalonia.Controls/TreeViewItem.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

30
Avalonia.sln

@ -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
@ -262,9 +262,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -605,14 +605,6 @@ 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
@ -637,6 +629,14 @@ Global
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.0 = Release|Any CPU {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.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
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -681,6 +681,8 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {351337F5-D66F-461B-A957-4EF60BDB4BA6} = {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}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C} {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098} {11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098} {BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@ -704,10 +706,6 @@ 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}
@ -715,6 +713,8 @@ Global
{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098} {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098} {4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098} {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

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)

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)

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>

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

11
samples/Sandbox/MainWindow.axaml.cs

@ -1,22 +1,17 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Input.TextInput;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition; 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>

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

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

@ -10,9 +10,8 @@ namespace Avalonia.Controls;
/// <remarks> /// <remarks>
/// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions. /// 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. /// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
/// This API might be removed in the future minor updates.
/// </remarks> /// </remarks>
[Unstable] [Unstable("This XAML-only API might be removed in the future minor updates.")]
public interface IThemeVariantProvider : IResourceProvider public interface IThemeVariantProvider : IResourceProvider
{ {
/// <summary> /// <summary>

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>

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)

12
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,
@ -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>

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

@ -6,6 +6,7 @@
// 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;
@ -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

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading; using Avalonia.Threading;
@ -400,11 +401,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;
@ -46,8 +47,19 @@ internal sealed class PlatformDrawingContext : DrawingContext, IDrawingContextWi
internal override void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) => internal override void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect) =>
_impl.DrawBitmap(source, opacity, sourceRect, destRect); _impl.DrawBitmap(source, opacity, sourceRect, destRect);
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>

5
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.Metadata; using Avalonia.Metadata;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@ -162,10 +161,6 @@ namespace Avalonia.Platform
void PopGeometryClip(); void PopGeometryClip();
/// <summary> /// <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

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()

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

@ -138,11 +138,6 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl,
_impl.PopGeometryClip(); _impl.PopGeometryClip();
} }
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>

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.

1
src/Avalonia.Base/Visual.cs

@ -331,6 +331,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; }
} }

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

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

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)

6
src/Avalonia.Controls/Platform/ManagedDispatcherImpl.cs

@ -58,6 +58,10 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
public void RunLoop(CancellationToken token) public void RunLoop(CancellationToken token)
{ {
CancellationTokenRegistration registration = default;
if (token.CanBeCanceled)
registration = token.Register(() => _wakeup.Set());
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
bool signaled; bool signaled;
@ -105,5 +109,7 @@ public class ManagedDispatcherImpl : IControlledDispatcherImpl
else else
_wakeup.WaitOne(); _wakeup.WaitOne();
} }
registration.Dispose();
} }
} }

5
src/Avalonia.Controls/Platform/Screen.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
@ -17,7 +18,7 @@ namespace Avalonia.Platform
public double Scaling { get; } public double Scaling { get; }
/// <inheritdoc cref="Scaling"/> /// <inheritdoc cref="Scaling"/>
[Obsolete("Use the Scaling property instead.")] [Obsolete("Use the Scaling property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public double PixelDensity => Scaling; public double PixelDensity => Scaling;
/// <summary> /// <summary>
@ -43,7 +44,7 @@ namespace Avalonia.Platform
public bool IsPrimary { get; } public bool IsPrimary { get; }
/// <inheritdoc cref="IsPrimary"/> /// <inheritdoc cref="IsPrimary"/>
[Obsolete("Use the IsPrimary property instead.")] [Obsolete("Use the IsPrimary property instead."), EditorBrowsable(EditorBrowsableState.Never)]
public bool Primary => IsPrimary; public bool Primary => IsPrimary;
/// <summary> /// <summary>

9
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -1,5 +1,5 @@
using System; using System;
using Avalonia.Collections;
using Avalonia.Controls.Documents; using Avalonia.Controls.Documents;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -442,7 +442,7 @@ namespace Avalonia.Controls.Presenters
var contentTemplate = ContentTemplate; var contentTemplate = ContentTemplate;
var oldChild = Child; var oldChild = Child;
var newChild = CreateChild(content, oldChild, contentTemplate); var newChild = CreateChild(content, oldChild, contentTemplate);
var logicalChildren = Host?.LogicalChildren ?? LogicalChildren; var logicalChildren = GetEffectiveLogicalChildren();
// Remove the old child if we're not recycling it. // Remove the old child if we're not recycling it.
if (newChild != oldChild) if (newChild != oldChild)
@ -488,6 +488,9 @@ namespace Avalonia.Controls.Presenters
} }
private IAvaloniaList<ILogical> GetEffectiveLogicalChildren()
=> Host?.LogicalChildren ?? LogicalChildren;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
@ -692,7 +695,7 @@ namespace Avalonia.Controls.Presenters
else if (Child != null) else if (Child != null)
{ {
VisualChildren.Remove(Child); VisualChildren.Remove(Child);
LogicalChildren.Remove(Child); GetEffectiveLogicalChildren().Remove(Child);
((ISetInheritanceParent)Child).SetParent(Child.Parent); ((ISetInheritanceParent)Child).SetParent(Child.Parent);
Child = null; Child = null;
_recyclingDataTemplate = null; _recyclingDataTemplate = null;

5
src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs

@ -67,9 +67,12 @@ namespace Avalonia.Controls.Presenters
for (var i = 0; i < count; ++i) for (var i = 0; i < count; ++i)
{ {
var c = children[index + i]; var c = children[index + i];
if (!c.IsSet(ItemIsOwnContainerProperty)) if (!c.IsSet(ItemIsOwnContainerProperty))
{
itemsControl.RemoveLogicalChild(children[i + index]); itemsControl.RemoveLogicalChild(children[i + index]);
generator.ClearItemContainer(c); generator.ClearItemContainer(c);
}
} }
children.RemoveRange(index, count); children.RemoveRange(index, count);

4
src/Avalonia.Controls/Primitives/Popup.cs

@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives
/// <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>
@ -241,7 +241,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <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);

19
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel;
using Avalonia.Automation.Peers; using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Data; using Avalonia.Data;
@ -28,7 +29,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Defines the <see cref="Checked"/> event. /// Defines the <see cref="Checked"/> event.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChangedEvent instead.")] [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent<RoutedEventArgs> CheckedEvent = public static readonly RoutedEvent<RoutedEventArgs> CheckedEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>( RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
nameof(Checked), nameof(Checked),
@ -37,7 +38,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Defines the <see cref="Unchecked"/> event. /// Defines the <see cref="Unchecked"/> event.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChangedEvent instead.")] [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent<RoutedEventArgs> UncheckedEvent = public static readonly RoutedEvent<RoutedEventArgs> UncheckedEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>( RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
nameof(Unchecked), nameof(Unchecked),
@ -46,7 +47,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Defines the <see cref="Unchecked"/> event. /// Defines the <see cref="Unchecked"/> event.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChangedEvent instead.")] [Obsolete("Use IsCheckedChangedEvent instead."), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly RoutedEvent<RoutedEventArgs> IndeterminateEvent = public static readonly RoutedEvent<RoutedEventArgs> IndeterminateEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>( RoutedEvent.Register<ToggleButton, RoutedEventArgs>(
nameof(Indeterminate), nameof(Indeterminate),
@ -72,7 +73,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Raised when a <see cref="ToggleButton"/> is checked. /// Raised when a <see cref="ToggleButton"/> is checked.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChanged instead.")] [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler<RoutedEventArgs>? Checked public event EventHandler<RoutedEventArgs>? Checked
{ {
add => AddHandler(CheckedEvent, value); add => AddHandler(CheckedEvent, value);
@ -82,7 +83,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Raised when a <see cref="ToggleButton"/> is unchecked. /// Raised when a <see cref="ToggleButton"/> is unchecked.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChanged instead.")] [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler<RoutedEventArgs>? Unchecked public event EventHandler<RoutedEventArgs>? Unchecked
{ {
add => AddHandler(UncheckedEvent, value); add => AddHandler(UncheckedEvent, value);
@ -92,7 +93,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// Raised when a <see cref="ToggleButton"/> is neither checked nor unchecked. /// Raised when a <see cref="ToggleButton"/> is neither checked nor unchecked.
/// </summary> /// </summary>
[Obsolete("Use IsCheckedChanged instead.")] [Obsolete("Use IsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler<RoutedEventArgs>? Indeterminate public event EventHandler<RoutedEventArgs>? Indeterminate
{ {
add => AddHandler(IndeterminateEvent, value); add => AddHandler(IndeterminateEvent, value);
@ -168,7 +169,7 @@ namespace Avalonia.Controls.Primitives
/// Called when <see cref="IsChecked"/> becomes true. /// Called when <see cref="IsChecked"/> becomes true.
/// </summary> /// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param> /// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
[Obsolete("Use OnIsCheckedChanged instead.")] [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnChecked(RoutedEventArgs e) protected virtual void OnChecked(RoutedEventArgs e)
{ {
RaiseEvent(e); RaiseEvent(e);
@ -178,7 +179,7 @@ namespace Avalonia.Controls.Primitives
/// Called when <see cref="IsChecked"/> becomes false. /// Called when <see cref="IsChecked"/> becomes false.
/// </summary> /// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param> /// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
[Obsolete("Use OnIsCheckedChanged instead.")] [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnUnchecked(RoutedEventArgs e) protected virtual void OnUnchecked(RoutedEventArgs e)
{ {
RaiseEvent(e); RaiseEvent(e);
@ -188,7 +189,7 @@ namespace Avalonia.Controls.Primitives
/// Called when <see cref="IsChecked"/> becomes null. /// Called when <see cref="IsChecked"/> becomes null.
/// </summary> /// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param> /// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
[Obsolete("Use OnIsCheckedChanged instead.")] [Obsolete("Use OnIsCheckedChanged instead."), EditorBrowsable(EditorBrowsableState.Never)]
protected virtual void OnIndeterminate(RoutedEventArgs e) protected virtual void OnIndeterminate(RoutedEventArgs e)
{ {
RaiseEvent(e); RaiseEvent(e);

2
src/Avalonia.Controls/Primitives/UniformGrid.cs

@ -123,7 +123,7 @@ namespace Avalonia.Controls.Primitives
if (FirstColumn >= Columns) if (FirstColumn >= Columns)
{ {
FirstColumn = 0; SetCurrentValue(FirstColumnProperty, 0);
} }
var itemCount = FirstColumn; var itemCount = FirstColumn;

3
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -29,6 +29,9 @@ namespace Avalonia.Controls.Primitives
} }
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031",
Justification = "A hack to make ChromeOverlayLayer lazily creatable. It is expected that GetValue(ChromeOverlayLayerProperty) alone won't work.")]
public ChromeOverlayLayer ChromeOverlayLayer public ChromeOverlayLayer ChromeOverlayLayer
{ {
get get

95
src/Avalonia.Controls/ProgressBar.cs

@ -17,7 +17,13 @@ namespace Avalonia.Controls
[PseudoClasses(":vertical", ":horizontal", ":indeterminate")] [PseudoClasses(":vertical", ":horizontal", ":indeterminate")]
public class ProgressBar : RangeBase public class ProgressBar : RangeBase
{ {
public class ProgressBarTemplateProperties : AvaloniaObject /// <summary>
/// Provides calculated values for use with the <see cref="ProgressBar"/>'s control theme or template.
/// </summary>
/// <remarks>
/// This class is NOT intended for general use outside of control templates.
/// </remarks>
public class ProgressBarTemplateSettings : AvaloniaObject
{ {
private double _container2Width; private double _container2Width;
private double _containerWidth; private double _containerWidth;
@ -26,38 +32,38 @@ namespace Avalonia.Controls
private double _container2AnimationStartPosition; private double _container2AnimationStartPosition;
private double _container2AnimationEndPosition; private double _container2AnimationEndPosition;
public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerAnimationStartPositionProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerAnimationStartPositionProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(ContainerAnimationStartPosition), nameof(ContainerAnimationStartPosition),
p => p.ContainerAnimationStartPosition, p => p.ContainerAnimationStartPosition,
(p, o) => p.ContainerAnimationStartPosition = o, 0d); (p, o) => p.ContainerAnimationStartPosition = o, 0d);
public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerAnimationEndPositionProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerAnimationEndPositionProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(ContainerAnimationEndPosition), nameof(ContainerAnimationEndPosition),
p => p.ContainerAnimationEndPosition, p => p.ContainerAnimationEndPosition,
(p, o) => p.ContainerAnimationEndPosition = o, 0d); (p, o) => p.ContainerAnimationEndPosition = o, 0d);
public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2AnimationStartPositionProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2AnimationStartPositionProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(Container2AnimationStartPosition), nameof(Container2AnimationStartPosition),
p => p.Container2AnimationStartPosition, p => p.Container2AnimationStartPosition,
(p, o) => p.Container2AnimationStartPosition = o, 0d); (p, o) => p.Container2AnimationStartPosition = o, 0d);
public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2AnimationEndPositionProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2AnimationEndPositionProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(Container2AnimationEndPosition), nameof(Container2AnimationEndPosition),
p => p.Container2AnimationEndPosition, p => p.Container2AnimationEndPosition,
(p, o) => p.Container2AnimationEndPosition = o); (p, o) => p.Container2AnimationEndPosition = o);
public static readonly DirectProperty<ProgressBarTemplateProperties, double> Container2WidthProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> Container2WidthProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(Container2Width), nameof(Container2Width),
p => p.Container2Width, p => p.Container2Width,
(p, o) => p.Container2Width = o); (p, o) => p.Container2Width = o);
public static readonly DirectProperty<ProgressBarTemplateProperties, double> ContainerWidthProperty = public static readonly DirectProperty<ProgressBarTemplateSettings, double> ContainerWidthProperty =
AvaloniaProperty.RegisterDirect<ProgressBarTemplateProperties, double>( AvaloniaProperty.RegisterDirect<ProgressBarTemplateSettings, double>(
nameof(ContainerWidth), nameof(ContainerWidth),
p => p.ContainerWidth, p => p.ContainerWidth,
(p, o) => p.ContainerWidth = o); (p, o) => p.ContainerWidth = o);
@ -103,29 +109,57 @@ namespace Avalonia.Controls
private Border? _indicator; private Border? _indicator;
private IDisposable? _trackSizeChangedListener; private IDisposable? _trackSizeChangedListener;
/// <summary>
/// Defines the <see cref="IsIndeterminate"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsIndeterminateProperty = public static readonly StyledProperty<bool> IsIndeterminateProperty =
AvaloniaProperty.Register<ProgressBar, bool>(nameof(IsIndeterminate)); AvaloniaProperty.Register<ProgressBar, bool>(nameof(IsIndeterminate));
/// <summary>
/// Defines the <see cref="ShowProgressText"/> property.
/// </summary>
public static readonly StyledProperty<bool> ShowProgressTextProperty = public static readonly StyledProperty<bool> ShowProgressTextProperty =
AvaloniaProperty.Register<ProgressBar, bool>(nameof(ShowProgressText)); AvaloniaProperty.Register<ProgressBar, bool>(nameof(ShowProgressText));
/// <summary>
/// Defines the <see cref="ProgressTextFormat"/> property.
/// </summary>
public static readonly StyledProperty<string> ProgressTextFormatProperty = public static readonly StyledProperty<string> ProgressTextFormatProperty =
AvaloniaProperty.Register<ProgressBar, string>(nameof(ProgressTextFormat), "{1:0}%"); AvaloniaProperty.Register<ProgressBar, string>(nameof(ProgressTextFormat), "{1:0}%");
/// <summary>
/// Defines the <see cref="Orientation"/> property.
/// </summary>
public static readonly StyledProperty<Orientation> OrientationProperty = public static readonly StyledProperty<Orientation> OrientationProperty =
AvaloniaProperty.Register<ProgressBar, Orientation>(nameof(Orientation), Orientation.Horizontal); AvaloniaProperty.Register<ProgressBar, Orientation>(nameof(Orientation), Orientation.Horizontal);
/// <summary>
/// Defines the <see cref="Percentage"/> property.
/// </summary>
public static readonly DirectProperty<ProgressBar, double> PercentageProperty = public static readonly DirectProperty<ProgressBar, double> PercentageProperty =
AvaloniaProperty.RegisterDirect<ProgressBar, double>( AvaloniaProperty.RegisterDirect<ProgressBar, double>(
nameof(Percentage), nameof(Percentage),
o => o.Percentage); o => o.Percentage);
/// <summary>
/// Defines the <see cref="IndeterminateStartingOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> IndeterminateStartingOffsetProperty = public static readonly StyledProperty<double> IndeterminateStartingOffsetProperty =
AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateStartingOffset)); AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateStartingOffset));
/// <summary>
/// Defines the <see cref="IndeterminateEndingOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> IndeterminateEndingOffsetProperty = public static readonly StyledProperty<double> IndeterminateEndingOffsetProperty =
AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateEndingOffset)); AvaloniaProperty.Register<ProgressBar, double>(nameof(IndeterminateEndingOffset));
/// <summary>
/// Gets the overall percentage complete of the progress
/// </summary>
/// <remarks>
/// This read-only property is automatically calculated using the current <see cref="RangeBase.Value"/> and
/// the effective range (<see cref="RangeBase.Maximum"/> - <see cref="RangeBase.Minimum"/>).
/// </remarks>
public double Percentage public double Percentage
{ {
get { return _percentage; } get { return _percentage; }
@ -154,31 +188,50 @@ namespace Avalonia.Controls
OrientationProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e)); OrientationProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
} }
/// <summary>
/// Initializes a new instance of the <see cref="ProgressBar"/> class.
/// </summary>
public ProgressBar() public ProgressBar()
{ {
UpdatePseudoClasses(IsIndeterminate, Orientation); UpdatePseudoClasses(IsIndeterminate, Orientation);
} }
public ProgressBarTemplateProperties TemplateProperties { get; } = new ProgressBarTemplateProperties(); /// <summary>
/// Gets or sets the TemplateSettings for the <see cref="ProgressBar"/>.
/// </summary>
public ProgressBarTemplateSettings TemplateSettings { get; } = new ProgressBarTemplateSettings();
/// <summary>
/// Gets or sets a value indicating whether the progress bar shows the actual value or a generic,
/// continues progress indicator (indeterminate state).
/// </summary>
public bool IsIndeterminate public bool IsIndeterminate
{ {
get => GetValue(IsIndeterminateProperty); get => GetValue(IsIndeterminateProperty);
set => SetValue(IsIndeterminateProperty, value); set => SetValue(IsIndeterminateProperty, value);
} }
/// <summary>
/// Gets or sets a value indicating whether progress text will be shown.
/// </summary>
public bool ShowProgressText public bool ShowProgressText
{ {
get => GetValue(ShowProgressTextProperty); get => GetValue(ShowProgressTextProperty);
set => SetValue(ShowProgressTextProperty, value); set => SetValue(ShowProgressTextProperty, value);
} }
/// <summary>
/// Gets or sets the format string applied to the internally calculated progress text before it is shown.
/// </summary>
public string ProgressTextFormat public string ProgressTextFormat
{ {
get => GetValue(ProgressTextFormatProperty); get => GetValue(ProgressTextFormatProperty);
set => SetValue(ProgressTextFormatProperty, value); set => SetValue(ProgressTextFormatProperty, value);
} }
/// <summary>
/// Gets or sets the orientation of the <see cref="ProgressBar"/>.
/// </summary>
public Orientation Orientation public Orientation Orientation
{ {
get => GetValue(OrientationProperty); get => GetValue(OrientationProperty);
@ -193,6 +246,7 @@ namespace Avalonia.Controls
return result; return result;
} }
/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -242,15 +296,14 @@ namespace Avalonia.Controls
var barIndicatorWidth = dim * 0.4; // Indicator width at 40% of ProgressBar var barIndicatorWidth = dim * 0.4; // Indicator width at 40% of ProgressBar
var barIndicatorWidth2 = dim * 0.6; // Indicator width at 60% of ProgressBar var barIndicatorWidth2 = dim * 0.6; // Indicator width at 60% of ProgressBar
TemplateProperties.ContainerWidth = barIndicatorWidth; TemplateSettings.ContainerWidth = barIndicatorWidth;
TemplateProperties.Container2Width = barIndicatorWidth2; TemplateSettings.Container2Width = barIndicatorWidth2;
TemplateProperties.ContainerAnimationStartPosition = barIndicatorWidth * -1.8; // Position at -180%
TemplateProperties.ContainerAnimationEndPosition = barIndicatorWidth * 3.0; // Position at 300%
TemplateProperties.Container2AnimationStartPosition = barIndicatorWidth2 * -1.5; // Position at -150% TemplateSettings.ContainerAnimationStartPosition = barIndicatorWidth * -1.8; // Position at -180%
TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166% TemplateSettings.ContainerAnimationEndPosition = barIndicatorWidth * 3.0; // Position at 300%
TemplateSettings.Container2AnimationStartPosition = barIndicatorWidth2 * -1.5; // Position at -150%
TemplateSettings.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
// Remove these properties when we switch to fluent as default and removed the old one. // Remove these properties when we switch to fluent as default and removed the old one.
SetCurrentValue(IndeterminateStartingOffsetProperty,-dim); SetCurrentValue(IndeterminateStartingOffsetProperty,-dim);

1
src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs

@ -71,6 +71,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets a value that indicates the refresh state of the visualizer. /// Gets or sets a value that indicates the refresh state of the visualizer.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "False positive")]
protected RefreshVisualizerState RefreshVisualizerState protected RefreshVisualizerState RefreshVisualizerState
{ {
get get

4
src/Avalonia.Controls/RowDefinition.cs

@ -46,8 +46,8 @@ namespace Avalonia.Controls
/// <param name="value">The height of the row.</param> /// <param name="value">The height of the row.</param>
/// <param name="type">The height unit of the column.</param> /// <param name="type">The height unit of the column.</param>
public RowDefinition(double value, GridUnitType type) public RowDefinition(double value, GridUnitType type)
: this(new GridLength(value, type))
{ {
Height = new GridLength(value, type);
} }
/// <summary> /// <summary>
@ -56,7 +56,7 @@ namespace Avalonia.Controls
/// <param name="height">The height of the column.</param> /// <param name="height">The height of the column.</param>
public RowDefinition(GridLength height) public RowDefinition(GridLength height)
{ {
Height = height; SetCurrentValue(HeightProperty, height);
} }
/// <summary> /// <summary>

41
src/Avalonia.Controls/Screens.cs

@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree;
#nullable enable #nullable enable
@ -38,21 +38,58 @@ namespace Avalonia.Controls
_iScreenImpl = iScreenImpl; _iScreenImpl = iScreenImpl;
} }
/// <summary>
/// Retrieves a Screen for the display that contains the rectangle.
/// </summary>
/// <param name="bounds">Bounds that specifies the area for which to retrieve the display.</param>
/// <returns>The <see cref="Screen"/>.</returns>
public Screen? ScreenFromBounds(PixelRect bounds) public Screen? ScreenFromBounds(PixelRect bounds)
{ {
return _iScreenImpl.ScreenFromRect(bounds); return _iScreenImpl.ScreenFromRect(bounds);
} }
/// <summary>
/// Retrieves a Screen for the display that contains the specified <see cref="WindowBase"/>.
/// </summary>
/// <param name="window">The window for which to retrieve the Screen.</param>
/// <exception cref="ObjectDisposedException">Window platform implementation was already disposed.</exception>
/// <returns>The <see cref="Screen"/>.</returns>
public Screen? ScreenFromWindow(WindowBase window)
{
if (window.PlatformImpl is null)
{
throw new ObjectDisposedException("Window platform implementation was already disposed.");
}
return _iScreenImpl.ScreenFromWindow(window.PlatformImpl);
}
/// <summary>
/// Retrieves a Screen for the display that contains the specified <see cref="IWindowBaseImpl"/>.
/// </summary>
/// <param name="window">The window impl for which to retrieve the Screen.</param>
/// <returns>The <see cref="Screen"/>.</returns>
[Obsolete("Use ScreenFromWindow(WindowBase) overload."), EditorBrowsable(EditorBrowsableState.Never)]
public Screen? ScreenFromWindow(IWindowBaseImpl window) public Screen? ScreenFromWindow(IWindowBaseImpl window)
{ {
return _iScreenImpl.ScreenFromWindow(window); return _iScreenImpl.ScreenFromWindow(window);
} }
/// <summary>
/// Retrieves a Screen for the display that contains the specified point.
/// </summary>
/// <param name="point">A Point that specifies the location for which to retrieve a Screen.</param>
/// <returns>The <see cref="Screen"/>.</returns>
public Screen? ScreenFromPoint(PixelPoint point) public Screen? ScreenFromPoint(PixelPoint point)
{ {
return _iScreenImpl.ScreenFromPoint(point); return _iScreenImpl.ScreenFromPoint(point);
} }
/// <summary>
/// Retrieves a Screen for the display that contains the specified <see cref="Visual"/>.
/// </summary>
/// <param name="visual">A Visual for which to retrieve a Screen.</param>
/// <returns>The <see cref="Screen"/>.</returns>
public Screen? ScreenFromVisual(Visual visual) public Screen? ScreenFromVisual(Visual visual)
{ {
var tl = visual.PointToScreen(visual.Bounds.TopLeft); var tl = visual.PointToScreen(visual.Bounds.TopLeft);

6
src/Avalonia.Controls/SplitView/SplitView.cs

@ -217,8 +217,8 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled /// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled
/// <para>When enabled, and the pane is open in Overlay or CompactOverlay mode, /// <para>When enabled, and the pane is open in Overlay or CompactOverlay mode,
/// the contents of the splitview are darkened to visually separate the open pane /// the contents of the <see cref="SplitView"/> are darkened to visually separate the open pane
/// and the rest of the SplitView</para> /// and the rest of the <see cref="SplitView"/>.</para>
/// </summary> /// </summary>
public bool UseLightDismissOverlayMode public bool UseLightDismissOverlayMode
{ {
@ -227,7 +227,7 @@ namespace Avalonia.Controls
} }
/// <summary> /// <summary>
/// Gets or sets the TemplateSettings for the SplitView /// Gets or sets the TemplateSettings for the <see cref="SplitView"/>.
/// </summary> /// </summary>
public SplitViewTemplateSettings TemplateSettings public SplitViewTemplateSettings TemplateSettings
{ {

6
src/Avalonia.Controls/SplitView/SplitViewTemplateSettings.cs

@ -2,8 +2,10 @@
{ {
/// <summary> /// <summary>
/// Provides calculated values for use with the <see cref="SplitView"/>'s control theme or template. /// Provides calculated values for use with the <see cref="SplitView"/>'s control theme or template.
/// This class is NOT intended for general use.
/// </summary> /// </summary>
/// <remarks>
/// This class is NOT intended for general use outside of control templates.
/// </remarks>
public class SplitViewTemplateSettings : AvaloniaObject public class SplitViewTemplateSettings : AvaloniaObject
{ {
internal SplitViewTemplateSettings() { } internal SplitViewTemplateSettings() { }
@ -17,12 +19,14 @@
AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>( AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>(
nameof(PaneColumnGridLength)); nameof(PaneColumnGridLength));
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
public double ClosedPaneWidth public double ClosedPaneWidth
{ {
get => GetValue(ClosedPaneWidthProperty); get => GetValue(ClosedPaneWidthProperty);
internal set => SetValue(ClosedPaneWidthProperty, value); internal set => SetValue(ClosedPaneWidthProperty, value);
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
public GridLength PaneColumnGridLength public GridLength PaneColumnGridLength
{ {
get => GetValue(PaneColumnGridLengthProperty); get => GetValue(PaneColumnGridLengthProperty);

15
src/Avalonia.Controls/SystemDialog.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
@ -11,7 +12,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Base class for system file dialogs. /// Base class for system file dialogs.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileDialog : FileSystemDialog public abstract class FileDialog : FileSystemDialog
{ {
/// <summary> /// <summary>
@ -29,7 +30,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Base class for system file and directory dialogs. /// Base class for system file and directory dialogs.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class FileSystemDialog : SystemDialog public abstract class FileSystemDialog : SystemDialog
{ {
/// <summary> /// <summary>
@ -42,7 +43,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a system dialog that prompts the user to select a location for saving a file. /// Represents a system dialog that prompts the user to select a location for saving a file.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class SaveFileDialog : FileDialog public class SaveFileDialog : FileDialog
{ {
/// <summary> /// <summary>
@ -91,7 +92,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a system dialog that allows the user to select one or more files to open. /// Represents a system dialog that allows the user to select one or more files to open.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFileDialog : FileDialog public class OpenFileDialog : FileDialog
{ {
/// <summary> /// <summary>
@ -132,7 +133,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a system dialog that allows the user to select a directory. /// Represents a system dialog that allows the user to select a directory.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class OpenFolderDialog : FileSystemDialog public class OpenFolderDialog : FileSystemDialog
{ {
/// <summary> /// <summary>
@ -167,7 +168,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Base class for system dialogs. /// Base class for system dialogs.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public abstract class SystemDialog public abstract class SystemDialog
{ {
static SystemDialog() static SystemDialog()
@ -188,7 +189,7 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Represents a filter in an <see cref="OpenFileDialog"/> or an <see cref="SaveFileDialog"/>. /// Represents a filter in an <see cref="OpenFileDialog"/> or an <see cref="SaveFileDialog"/>.
/// </summary> /// </summary>
[Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API")] [Obsolete("Use Window.StorageProvider API or TopLevel.StorageProvider API"), EditorBrowsable(EditorBrowsableState.Never)]
public class FileDialogFilter public class FileDialogFilter
{ {
/// <summary> /// <summary>

2
src/Avalonia.Controls/TabControl.cs

@ -115,6 +115,7 @@ namespace Avalonia.Controls
/// <value> /// <value>
/// The content of the selected tab. /// The content of the selected tab.
/// </value> /// </value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
public object? SelectedContent public object? SelectedContent
{ {
get { return GetValue(SelectedContentProperty); } get { return GetValue(SelectedContentProperty); }
@ -127,6 +128,7 @@ namespace Avalonia.Controls
/// <value> /// <value>
/// The content template of the selected tab. /// The content template of the selected tab.
/// </value> /// </value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
public IDataTemplate? SelectedContentTemplate public IDataTemplate? SelectedContentTemplate
{ {
get { return GetValue(SelectedContentTemplateProperty); } get { return GetValue(SelectedContentTemplateProperty); }

4
src/Avalonia.Controls/TabItem.cs

@ -42,6 +42,8 @@ namespace Avalonia.Controls
/// <value> /// <value>
/// The tab strip placement. /// The tab strip placement.
/// </value> /// </value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031",
Justification = "This property is supposed to be inherited only and settable on parent TabControl.")]
public Dock TabStripPlacement public Dock TabStripPlacement
{ {
get { return GetValue(TabStripPlacementProperty); } get { return GetValue(TabStripPlacementProperty); }
@ -83,7 +85,7 @@ namespace Avalonia.Controls
{ {
Header = obj.NewValue; Header = obj.NewValue;
} }
} }
} }
} }
} }

26
src/Avalonia.Controls/TextBlock.cs

@ -7,7 +7,6 @@ using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -565,7 +564,8 @@ namespace Avalonia.Controls
context.FillRectangle(background, new Rect(Bounds.Size)); context.FillRectangle(background, new Rect(Bounds.Size));
} }
var padding = Padding; var scale = LayoutHelper.GetLayoutScale(this);
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
var top = padding.Top; var top = padding.Top;
var textHeight = TextLayout.Bounds.Height; var textHeight = TextLayout.Bounds.Height;
@ -659,7 +659,6 @@ namespace Avalonia.Controls
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
var scale = LayoutHelper.GetLayoutScale(this); var scale = LayoutHelper.GetLayoutScale(this);
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
_constraint = availableSize.Deflate(padding); _constraint = availableSize.Deflate(padding);
@ -703,19 +702,24 @@ namespace Avalonia.Controls
} }
} }
var measuredSize = TextLayout.Bounds.Size.Inflate(padding); return TextLayout.Bounds.Size.Inflate(padding);
return measuredSize;
} }
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
if (HasComplexContent) var scale = LayoutHelper.GetLayoutScale(this);
{ var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
var scale = LayoutHelper.GetLayoutScale(this);
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); //Fixes: #11019
if (finalSize.Width < _constraint.Width)
{
_textLayout?.Dispose();
_textLayout = null;
_constraint = finalSize.Deflate(padding);
}
if (HasComplexContent)
{
var currentY = padding.Top; var currentY = padding.Top;
foreach (var textLine in TextLayout.TextLines) foreach (var textLine in TextLayout.TextLines)
@ -730,7 +734,7 @@ namespace Avalonia.Controls
&& controlRun.Control is Control control) && controlRun.Control is Control control)
{ {
control.Arrange( control.Arrange(
new Rect(new Point(currentX, currentY), new Rect(new Point(currentX, currentY),
new Size(control.DesiredSize.Width, textLine.Height))); new Size(control.DesiredSize.Width, textLine.Height)));
} }

2
src/Avalonia.Controls/ToggleSwitch.cs

@ -201,7 +201,7 @@ namespace Avalonia.Controls
} }
else else
{ {
IsChecked = shouldBecomeChecked; SetCurrentValue(IsCheckedProperty, shouldBecomeChecked);
} }
} }
else else

12
src/Avalonia.Controls/TopLevel.cs

@ -83,11 +83,11 @@ namespace Avalonia.Controls
/// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" /> /// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty = public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>(); ThemeVariantScope.ActualThemeVariantProperty.AddOwner<TopLevel>();
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" /> /// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty = public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>(); ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<TopLevel>();
/// <summary> /// <summary>
/// Defines the SystemBarColor attached property. /// Defines the SystemBarColor attached property.
@ -285,6 +285,11 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public event EventHandler? Closed; public event EventHandler? Closed;
/// <summary>
/// Gets or sets a method called when the TopLevel's scaling changes.
/// </summary>
public event EventHandler? ScalingChanged;
/// <summary> /// <summary>
/// Gets or sets the client size of the window. /// Gets or sets the client size of the window.
/// </summary> /// </summary>
@ -428,7 +433,7 @@ namespace Avalonia.Controls
double ILayoutRoot.LayoutScaling => PlatformImpl?.RenderScaling ?? 1; double ILayoutRoot.LayoutScaling => PlatformImpl?.RenderScaling ?? 1;
/// <inheritdoc/> /// <inheritdoc/>
double IRenderRoot.RenderScaling => PlatformImpl?.RenderScaling ?? 1; public double RenderScaling => PlatformImpl?.RenderScaling ?? 1;
IStyleHost IStyleHost.StylingParent => _globalStyles!; IStyleHost IStyleHost.StylingParent => _globalStyles!;
@ -590,6 +595,7 @@ namespace Avalonia.Controls
protected virtual void HandleScalingChanged(double scaling) protected virtual void HandleScalingChanged(double scaling)
{ {
LayoutHelper.InvalidateSelfAndChildrenMeasure(this); LayoutHelper.InvalidateSelfAndChildrenMeasure(this);
ScalingChanged?.Invoke(this, EventArgs.Empty);
} }
private static bool TransparencyLevelsMatch (WindowTransparencyLevel requested, WindowTransparencyLevel received) private static bool TransparencyLevelsMatch (WindowTransparencyLevel requested, WindowTransparencyLevel received)

3
src/Avalonia.Controls/TreeView.cs

@ -3,6 +3,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 System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Collections; using Avalonia.Collections;
@ -715,7 +716,7 @@ namespace Avalonia.Controls
} }
} }
[Obsolete] [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
private protected override ItemContainerGenerator CreateItemContainerGenerator() private protected override ItemContainerGenerator CreateItemContainerGenerator()
{ {
return new TreeItemContainerGenerator(this); return new TreeItemContainerGenerator(this);

6
src/Avalonia.Controls/TreeViewItem.cs

@ -45,6 +45,7 @@ namespace Avalonia.Controls
private TreeView? _treeView; private TreeView? _treeView;
private Control? _header; private Control? _header;
private Control? _headerPresenter;
private int _level; private int _level;
private bool _templateApplied; private bool _templateApplied;
private bool _deferredBringIntoViewFlag; private bool _deferredBringIntoViewFlag;
@ -255,15 +256,16 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
if (_header is InputElement previousInputMethod) if (_headerPresenter is InputElement previousInputMethod)
{ {
previousInputMethod.DoubleTapped -= HeaderDoubleTapped; previousInputMethod.DoubleTapped -= HeaderDoubleTapped;
} }
_header = e.NameScope.Find<Control>("PART_Header"); _header = e.NameScope.Find<Control>("PART_Header");
_headerPresenter = e.NameScope.Find<Control>("PART_HeaderPresenter");
_templateApplied = true; _templateApplied = true;
if (_header is InputElement im) if (_headerPresenter is InputElement im)
{ {
im.DoubleTapped += HeaderDoubleTapped; im.DoubleTapped += HeaderDoubleTapped;
} }

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

Loading…
Cancel
Save