diff --git a/.editorconfig b/.editorconfig
index c7a381b730..25e0135725 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -137,7 +137,7 @@ space_within_single_line_array_initializer_braces = true
csharp_wrap_before_ternary_opsigns = false
# Xaml files
-[*.xaml]
+[*.{xaml,axaml}]
indent_size = 2
# Xml project files
diff --git a/.gitignore b/.gitignore
index 9b15011929..44fe5e4ba4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,13 +192,12 @@ dirs.sln
##################
-# XCode
+# Xcode
##################
Index/
Logs/
ModuleCache.noindex/
Build/Intermediates.noindex/
-info.plist
build-intermediate
obj-Direct2D1/
obj-Skia/
diff --git a/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject b/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject b/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/IntegrationTestApp.v3.ncrunchproject b/.ncrunch/IntegrationTestApp.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/IntegrationTestApp.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Avalonia.sln b/Avalonia.sln
index 4e7b4cc318..a989fb828d 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -60,27 +60,24 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
+ src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlatformSupport", "src\Shared\PlatformSupport\PlatformSupport.shproj", "{E4D9629C-F168-4224-3F51-A5E482FFBC42}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup", "src\Markup\Avalonia.Markup\Avalonia.Markup.csproj", "{6417E941-21BC-467B-A771-0DE389353CE6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.UnitTests", "tests\Avalonia.Markup.UnitTests\Avalonia.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BindingDemo", "samples\BindingDemo\BindingDemo.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RenderHelpers", "src\Shared\RenderHelpers\RenderHelpers.shproj", "{3C4C0CB4-0C0F-4450-A37B-148C84FF905F}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{3743B0F2-CC41-4F14-A8C8-267F579BF91E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject
@@ -98,8 +95,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}"
@@ -110,7 +105,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject
@@ -118,14 +113,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
- build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\ApiDiff.props = build\ApiDiff.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\CoreLibraries.props = build\CoreLibraries.props
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
- build\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@@ -181,8 +174,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.OpenGL", "src\Aval
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesktopRuntime", "src\Avalonia.DesktopRuntime\Avalonia.DesktopRuntime.csproj", "{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{E870DCD7-F46A-498D-83FC-D0FD13E0A11C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalonia\Avalonia.csproj", "{D49233F8-F29C-47DD-9975-C4C9E4502720}"
@@ -225,6 +216,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MicroCom", "src\Av
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestApp", "samples\IntegrationTestApp\IntegrationTestApp.csproj", "{676D6BFD-029D-4E43-BFC7-3892265CE251}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.IntegrationTests.Appium", "tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj", "{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}"
@@ -235,15 +230,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "sampl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}"
+EndProject
Global
- GlobalSection(SharedMSBuildProjectFiles) = preSolution
- src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
- src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5
- src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 5
- src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 5
- src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5
- src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
Ad-Hoc|iPhone = Ad-Hoc|iPhone
@@ -1105,26 +1096,6 @@ Global
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.Build.0 = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|Any CPU.ActiveCfg = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.ActiveCfg = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.Build.0 = Release|iPhone
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -1569,30 +1540,6 @@ Global
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.Build.0 = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -2073,6 +2020,54 @@ Global
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.Build.0 = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.Build.0 = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -2169,6 +2164,54 @@ Global
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.Build.0 = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2187,11 +2230,9 @@ Global
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C}
- {E4D9629C-F168-4224-3F51-A5E482FFBC42} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {3C4C0CB4-0C0F-4450-A37B-148C84FF905F} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}
{FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}
{4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}
@@ -2200,7 +2241,6 @@ Global
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{D0A739B9-3C68-4BA6-A328-41606954B6BD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{2B888490-D14A-4BCA-AB4B-48676FA93C9B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {57E0455D-D565-44BB-B069-EE1AA20F8337} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -2209,6 +2249,7 @@ Global
{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
@@ -2228,10 +2269,13 @@ Global
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+ {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/Directory.Build.props b/Directory.Build.props
index c6610695c4..835decc672 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,5 +4,6 @@
$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll
false
+ false
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 40669f4f53..79456b117b 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,6 +1,3 @@
-variables:
- MSBuildEnableWorkloadResolver: 'false'
-
jobs:
- job: GetPRNumber
@@ -41,7 +38,7 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
- version: 6.0.100
+ version: 6.0.200
- task: CmdLine@2
displayName: 'Run Build'
@@ -72,7 +69,7 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
- version: 6.0.100
+ version: 6.0.200
- task: CmdLine@2
displayName: 'Install Mono 5.18'
@@ -144,7 +141,13 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
- version: 6.0.100
+ version: 6.0.200
+
+ - task: CmdLine@2
+ displayName: 'Install Workloads'
+ inputs:
+ script: |
+ dotnet workload install --no-cache --disable-parallel android ios --skip-manifest-update --source "https://api.nuget.org/v3/index.json"
- task: CmdLine@2
displayName: 'Install Nuke'
diff --git a/build/AndroidWorkarounds.props b/build/AndroidWorkarounds.props
deleted file mode 100644
index de86acc6de..0000000000
--- a/build/AndroidWorkarounds.props
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- false
-
-
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index fff00041c3..6bf69603c0 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -16,6 +16,6 @@
-
+
diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props
index 1d84d5289a..6dd6cccb53 100644
--- a/build/HarfBuzzSharp.props
+++ b/build/HarfBuzzSharp.props
@@ -1,7 +1,7 @@
-
-
-
+
+
+
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 7d75901288..3d9548ab9d 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -3,7 +3,7 @@
Avalonia
0.10.999
- Copyright 2021 © The AvaloniaUI Project
+ Copyright 2022 © The AvaloniaUI Project
https://avaloniaui.net
https://github.com/AvaloniaUI/Avalonia/
true
@@ -11,7 +11,7 @@
latest
MIT
Icon.png
- Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.
+ Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly.
avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin
https://github.com/AvaloniaUI/Avalonia/releases
git
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index bb370256f9..60bebaad40 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,7 +1,7 @@
-
-
-
+
+
+
diff --git a/build/System.Drawing.Common.props b/build/System.Drawing.Common.props
index 2b3707d38a..108a0f41e0 100644
--- a/build/System.Drawing.Common.props
+++ b/build/System.Drawing.Common.props
@@ -1,5 +1,6 @@
-
+
+
diff --git a/build/XUnit.props b/build/XUnit.props
index a75e1bac86..17ead91aa3 100644
--- a/build/XUnit.props
+++ b/build/XUnit.props
@@ -1,14 +1,14 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/dirs.proj b/dirs.proj
index 594f2c22d3..396e0c915c 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -1,5 +1,7 @@
+
+
@@ -8,21 +10,21 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/global.json b/global.json
index b160e4561d..30265268dc 100644
--- a/global.json
+++ b/global.json
@@ -1,9 +1,11 @@
{
"sdk": {
- "version": "6.0.100"
+ "version": "6.0.200",
+ "rollForward": "latestFeature"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
+ "Xamarin.Legacy.Sdk": "0.1.2-alpha6",
"MSBuild.Sdk.Extras": "3.0.22",
"AggregatePackage.NuGet.Sdk" : "0.1.12"
}
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
index 85fcf20034..7571d51c9f 100644
--- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
+++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
@@ -30,6 +30,8 @@
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
+ BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
+ BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -64,6 +66,8 @@
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; };
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; };
+ BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = ""; };
+ BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -97,6 +101,8 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
+ BC11A5BC2608D58F0017BAD0 /* automation.h */,
+ BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */,
@@ -143,6 +149,7 @@
buildActionMask = 2147483647;
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
+ BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -213,6 +220,7 @@
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
+ BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
diff --git a/native/Avalonia.Native/src/OSX/AvnString.h b/native/Avalonia.Native/src/OSX/AvnString.h
index 3ce83d370a..3b750b11db 100644
--- a/native/Avalonia.Native/src/OSX/AvnString.h
+++ b/native/Avalonia.Native/src/OSX/AvnString.h
@@ -14,4 +14,5 @@ extern IAvnStringArray* CreateAvnStringArray(NSArray* array);
extern IAvnStringArray* CreateAvnStringArray(NSArray* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string);
extern IAvnString* CreateByteArray(void* data, int len);
+extern NSString* GetNSStringAndRelease(IAvnString* s);
#endif /* AvnString_h */
diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm
index cd0e2cdf94..5e50068c51 100644
--- a/native/Avalonia.Native/src/OSX/AvnString.mm
+++ b/native/Avalonia.Native/src/OSX/AvnString.mm
@@ -153,3 +153,19 @@ IAvnString* CreateByteArray(void* data, int len)
{
return new AvnStringImpl(data, len);
}
+
+NSString* GetNSStringAndRelease(IAvnString* s)
+{
+ NSString* result = nil;
+
+ if (s != nullptr)
+ {
+ char* p;
+ if (s->Pointer((void**)&p) == S_OK && p != nullptr)
+ result = [NSString stringWithUTF8String:p];
+
+ s->Release();
+ }
+
+ return result;
+}
diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h
new file mode 100644
index 0000000000..4a12a965fd
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/automation.h
@@ -0,0 +1,12 @@
+#import
+#include "window.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+class IAvnAutomationPeer;
+
+@interface AvnAccessibilityElement : NSAccessibilityElement
++ (AvnAccessibilityElement *) acquire:(IAvnAutomationPeer *) peer;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
new file mode 100644
index 0000000000..7d697140c2
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -0,0 +1,496 @@
+#include "common.h"
+#include "automation.h"
+#include "AvnString.h"
+#include "window.h"
+
+@interface AvnAccessibilityElement (Events)
+- (void) raiseChildrenChanged;
+@end
+
+@interface AvnRootAccessibilityElement : AvnAccessibilityElement
+- (AvnView *) ownerView;
+- (AvnRootAccessibilityElement *) initWithPeer:(IAvnAutomationPeer *) peer owner:(AvnView*) owner;
+- (void) raiseFocusChanged;
+@end
+
+class AutomationNode : public ComSingleObject
+{
+public:
+ FORWARD_IUNKNOWN()
+
+ AutomationNode(AvnAccessibilityElement* owner)
+ {
+ _owner = owner;
+ }
+
+ AvnAccessibilityElement* GetOwner()
+ {
+ return _owner;
+ }
+
+ virtual void Dispose() override
+ {
+ _owner = nil;
+ }
+
+ virtual void ChildrenChanged () override
+ {
+ [_owner raiseChildrenChanged];
+ }
+
+ virtual void PropertyChanged (AvnAutomationProperty property) override
+ {
+
+ }
+
+ virtual void FocusChanged () override
+ {
+ [(AvnRootAccessibilityElement*)_owner raiseFocusChanged];
+ }
+
+private:
+ __strong AvnAccessibilityElement* _owner;
+};
+
+@implementation AvnAccessibilityElement
+{
+ IAvnAutomationPeer* _peer;
+ AutomationNode* _node;
+ NSMutableArray* _children;
+}
+
++ (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer
+{
+ if (peer == nullptr)
+ return nil;
+
+ auto instance = peer->GetNode();
+
+ if (instance != nullptr)
+ return dynamic_cast(instance)->GetOwner();
+
+ if (peer->IsRootProvider())
+ {
+ auto window = peer->RootProvider_GetWindow();
+ auto holder = dynamic_cast(window);
+ auto view = holder->GetNSView();
+ return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view];
+ }
+ else
+ {
+ return [[AvnAccessibilityElement alloc] initWithPeer:peer];
+ }
+}
+
+- (AvnAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer
+{
+ self = [super init];
+ _peer = peer;
+ _node = new AutomationNode(self);
+ _peer->SetNode(_node);
+ return self;
+}
+
+- (void)dealloc
+{
+ if (_node)
+ delete _node;
+ _node = nullptr;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@ '%@' (%p)",
+ GetNSStringAndRelease(_peer->GetClassName()),
+ GetNSStringAndRelease(_peer->GetName()),
+ _peer];
+}
+
+- (IAvnAutomationPeer *)peer
+{
+ return _peer;
+}
+
+- (BOOL)isAccessibilityElement
+{
+ return _peer->IsControlElement();
+}
+
+- (NSAccessibilityRole)accessibilityRole
+{
+ auto controlType = _peer->GetAutomationControlType();
+
+ switch (controlType) {
+ case AutomationButton: return NSAccessibilityButtonRole;
+ case AutomationCalendar: return NSAccessibilityGridRole;
+ case AutomationCheckBox: return NSAccessibilityCheckBoxRole;
+ case AutomationComboBox: return NSAccessibilityPopUpButtonRole;
+ case AutomationComboBoxItem: return NSAccessibilityMenuItemRole;
+ case AutomationEdit: return NSAccessibilityTextFieldRole;
+ case AutomationHyperlink: return NSAccessibilityLinkRole;
+ case AutomationImage: return NSAccessibilityImageRole;
+ case AutomationListItem: return NSAccessibilityRowRole;
+ case AutomationList: return NSAccessibilityTableRole;
+ case AutomationMenu: return NSAccessibilityMenuBarRole;
+ case AutomationMenuBar: return NSAccessibilityMenuBarRole;
+ case AutomationMenuItem: return NSAccessibilityMenuItemRole;
+ case AutomationProgressBar: return NSAccessibilityProgressIndicatorRole;
+ case AutomationRadioButton: return NSAccessibilityRadioButtonRole;
+ case AutomationScrollBar: return NSAccessibilityScrollBarRole;
+ case AutomationSlider: return NSAccessibilitySliderRole;
+ case AutomationSpinner: return NSAccessibilityIncrementorRole;
+ case AutomationStatusBar: return NSAccessibilityTableRole;
+ case AutomationTab: return NSAccessibilityTabGroupRole;
+ case AutomationTabItem: return NSAccessibilityRadioButtonRole;
+ case AutomationText: return NSAccessibilityStaticTextRole;
+ case AutomationToolBar: return NSAccessibilityToolbarRole;
+ case AutomationToolTip: return NSAccessibilityPopoverRole;
+ case AutomationTree: return NSAccessibilityOutlineRole;
+ case AutomationTreeItem: return NSAccessibilityCellRole;
+ case AutomationCustom: return NSAccessibilityUnknownRole;
+ case AutomationGroup: return NSAccessibilityGroupRole;
+ case AutomationThumb: return NSAccessibilityHandleRole;
+ case AutomationDataGrid: return NSAccessibilityGridRole;
+ case AutomationDataItem: return NSAccessibilityCellRole;
+ case AutomationDocument: return NSAccessibilityStaticTextRole;
+ case AutomationSplitButton: return NSAccessibilityPopUpButtonRole;
+ case AutomationWindow: return NSAccessibilityWindowRole;
+ case AutomationPane: return NSAccessibilityGroupRole;
+ case AutomationHeader: return NSAccessibilityGroupRole;
+ case AutomationHeaderItem: return NSAccessibilityButtonRole;
+ case AutomationTable: return NSAccessibilityTableRole;
+ case AutomationTitleBar: return NSAccessibilityGroupRole;
+ // Treat unknown roles as generic group container items. Returning
+ // NSAccessibilityUnknownRole is also possible but makes the screen
+ // reader focus on the item instead of passing focus to child items.
+ default: return NSAccessibilityGroupRole;
+ }
+}
+
+- (NSString *)accessibilityIdentifier
+{
+ return GetNSStringAndRelease(_peer->GetAutomationId());
+}
+
+- (NSString *)accessibilityTitle
+{
+ // StaticText exposes its text via the value property.
+ if (_peer->GetAutomationControlType() != AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityTitle];
+}
+
+- (id)accessibilityValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetValue()];
+ }
+ else if (_peer->IsToggleProvider())
+ {
+ switch (_peer->ToggleProvider_GetToggleState()) {
+ case 0: return [NSNumber numberWithBool:NO];
+ case 1: return [NSNumber numberWithBool:YES];
+ default: return [NSNumber numberWithInt:2];
+ }
+ }
+ else if (_peer->IsValueProvider())
+ {
+ return GetNSStringAndRelease(_peer->ValueProvider_GetValue());
+ }
+ else if (_peer->GetAutomationControlType() == AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityValue];
+}
+
+- (id)accessibilityMinValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMinimum()];
+ }
+
+ return [super accessibilityMinValue];
+}
+
+- (id)accessibilityMaxValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMaximum()];
+ }
+
+ return [super accessibilityMaxValue];
+}
+
+- (BOOL)isAccessibilityEnabled
+{
+ return _peer->IsEnabled();
+}
+
+- (BOOL)isAccessibilityFocused
+{
+ return _peer->HasKeyboardFocus();
+}
+
+- (NSArray *)accessibilityChildren
+{
+ if (_children == nullptr && _peer != nullptr)
+ [self recalculateChildren];
+ return _children;
+}
+
+- (NSRect)accessibilityFrame
+{
+ id topLevel = [self accessibilityTopLevelUIElement];
+ auto result = NSZeroRect;
+
+ if ([topLevel isKindOfClass:[AvnRootAccessibilityElement class]])
+ {
+ auto root = (AvnRootAccessibilityElement*)topLevel;
+ auto view = [root ownerView];
+
+ if (view)
+ {
+ auto window = [view window];
+ auto bounds = ToNSRect(_peer->GetBoundingRectangle());
+ auto windowBounds = [view convertRect:bounds toView:nil];
+ auto screenBounds = [window convertRectToScreen:windowBounds];
+ result = screenBounds;
+ }
+ }
+
+ return result;
+}
+
+- (id)accessibilityParent
+{
+ auto parentPeer = _peer->GetParent();
+ return parentPeer ? [AvnAccessibilityElement acquire:parentPeer] : [NSApplication sharedApplication];
+}
+
+- (id)accessibilityTopLevelUIElement
+{
+ auto rootPeer = _peer->GetRootPeer();
+ return [AvnAccessibilityElement acquire:rootPeer];
+}
+
+- (id)accessibilityWindow
+{
+ id topLevel = [self accessibilityTopLevelUIElement];
+ return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil;
+}
+
+- (BOOL)isAccessibilityExpanded
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return NO;
+ return _peer->ExpandCollapseProvider_GetIsExpanded();
+}
+
+- (void)setAccessibilityExpanded:(BOOL)accessibilityExpanded
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return;
+ if (accessibilityExpanded)
+ _peer->ExpandCollapseProvider_Expand();
+ else
+ _peer->ExpandCollapseProvider_Collapse();
+}
+
+- (BOOL)accessibilityPerformPress
+{
+ if (_peer->IsInvokeProvider())
+ {
+ _peer->InvokeProvider_Invoke();
+ }
+ else if (_peer->IsExpandCollapseProvider())
+ {
+ _peer->ExpandCollapseProvider_Expand();
+ }
+ else if (_peer->IsToggleProvider())
+ {
+ _peer->ToggleProvider_Toggle();
+ }
+ return YES;
+}
+
+- (BOOL)accessibilityPerformIncrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value += _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
+- (BOOL)accessibilityPerformDecrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value -= _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
+- (BOOL)accessibilityPerformShowMenu
+{
+ if (!_peer->IsExpandCollapseProvider())
+ return NO;
+ _peer->ExpandCollapseProvider_Expand();
+ return YES;
+}
+
+- (BOOL)isAccessibilitySelected
+{
+ if (_peer->IsSelectionItemProvider())
+ return _peer->SelectionItemProvider_IsSelected();
+ return NO;
+}
+
+- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
+{
+ if (selector == @selector(accessibilityPerformShowMenu))
+ {
+ return _peer->IsExpandCollapseProvider() && _peer->ExpandCollapseProvider_GetShowsMenu();
+ }
+ else if (selector == @selector(isAccessibilityExpanded))
+ {
+ return _peer->IsExpandCollapseProvider();
+ }
+ else if (selector == @selector(accessibilityPerformPress))
+ {
+ return _peer->IsInvokeProvider() || _peer->IsExpandCollapseProvider() || _peer->IsToggleProvider();
+ }
+ else if (selector == @selector(accessibilityPerformIncrement) ||
+ selector == @selector(accessibilityPerformDecrement) ||
+ selector == @selector(accessibilityMinValue) ||
+ selector == @selector(accessibilityMaxValue))
+ {
+ return _peer->IsRangeValueProvider();
+ }
+
+ return [super isAccessibilitySelectorAllowed:selector];
+}
+
+- (void)raiseChildrenChanged
+{
+ auto changed = _children ? [NSMutableSet setWithArray:_children] : [NSMutableSet set];
+
+ [self recalculateChildren];
+
+ if (_children)
+ [changed addObjectsFromArray:_children];
+
+ NSAccessibilityPostNotificationWithUserInfo(
+ self,
+ NSAccessibilityLayoutChangedNotification,
+ @{ NSAccessibilityUIElementsKey: [changed allObjects]});
+}
+
+- (void)raisePropertyChanged
+{
+}
+
+- (void)setAccessibilityFocused:(BOOL)accessibilityFocused
+{
+ if (accessibilityFocused)
+ _peer->SetFocus();
+}
+
+- (void)recalculateChildren
+{
+ auto childPeers = _peer->GetChildren();
+ auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;
+
+ if (childCount > 0)
+ {
+ _children = [[NSMutableArray alloc] initWithCapacity:childCount];
+
+ for (int i = 0; i < childCount; ++i)
+ {
+ IAvnAutomationPeer* child;
+
+ if (childPeers->Get(i, &child) == S_OK)
+ {
+ auto element = [AvnAccessibilityElement acquire:child];
+ [_children addObject:element];
+ }
+ }
+ }
+ else
+ {
+ _children = nil;
+ }
+}
+
+@end
+
+@implementation AvnRootAccessibilityElement
+{
+ AvnView* _owner;
+}
+
+- (AvnRootAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer owner:(AvnView *)owner
+{
+ self = [super initWithPeer:peer];
+ _owner = owner;
+
+ // Seems we need to raise a focus changed notification here if we have focus
+ auto focusedPeer = [self peer]->RootProvider_GetFocus();
+ id focused = [AvnAccessibilityElement acquire:focusedPeer];
+
+ if (focused)
+ NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
+
+ return self;
+}
+
+- (AvnView *)ownerView
+{
+ return _owner;
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ auto focusedPeer = [self peer]->RootProvider_GetFocus();
+ return [AvnAccessibilityElement acquire:focusedPeer];
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ auto clientPoint = [[_owner window] convertPointFromScreen:point];
+ auto localPoint = [_owner translateLocalPoint:ToAvnPoint(clientPoint)];
+ auto hit = [self peer]->RootProvider_GetPeerFromPoint(localPoint);
+ return [AvnAccessibilityElement acquire:hit];
+}
+
+- (id)accessibilityParent
+{
+ return _owner;
+}
+
+- (void)raiseFocusChanged
+{
+ id focused = [self accessibilityFocusedUIElement];
+ NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
+}
+
+// Although this method is marked as deprecated we get runtime warnings if we don't handle it.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+- (void)accessibilityPerformAction:(NSAccessibilityActionName)action
+{
+ [_owner accessibilityPerformAction:action];
+}
+#pragma clang diagnostic pop
+
+@end
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 126c9aa87b..9186d9e15a 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/native/Avalonia.Native/src/OSX/common.h
@@ -35,6 +35,7 @@ extern NSMenuItem* GetAppMenuItem ();
extern void InitializeAvnApp(IAvnApplicationEvents* events);
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
+extern NSRect ToNSRect (AvnRect r);
extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
extern CGFloat PrimaryDisplayHeight();
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index 69f2995847..ea79c494d7 100644
--- a/native/Avalonia.Native/src/OSX/main.mm
+++ b/native/Avalonia.Native/src/OSX/main.mm
@@ -1,6 +1,7 @@
//This file will contain actual IID structures
#define COM_GUIDS_MATERIALIZE
#include "common.h"
+#include "window.h"
static NSString* s_appTitle = @"Avalonia";
@@ -335,7 +336,7 @@ public:
return S_OK;
}
}
-
+
virtual HRESULT SetAppMenu (IAvnMenu* appMenu) override
{
START_COM_CALL;
@@ -400,6 +401,15 @@ NSPoint ToNSPoint (AvnPoint p)
return result;
}
+NSRect ToNSRect (AvnRect r)
+{
+ return NSRect
+ {
+ NSPoint { r.X, r.Y },
+ NSSize { r.Width, r.Height }
+ };
+}
+
AvnPoint ToAvnPoint (NSPoint p)
{
AvnPoint result;
diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h
index 1dc091a48d..1369ceaea0 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/native/Avalonia.Native/src/OSX/window.h
@@ -43,6 +43,7 @@ class WindowBaseImpl;
struct INSWindowHolder
{
virtual AvnWindow* _Nonnull GetNSWindow () = 0;
+ virtual AvnView* _Nonnull GetNSView () = 0;
};
struct IWindowStateChanged
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 40180274e1..9b703c4838 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -5,14 +5,22 @@
#include "menu.h"
#include
#include "rendertarget.h"
+#include "AvnString.h"
+#include "automation.h"
-class WindowBaseImpl : public virtual ComSingleObject, public INSWindowHolder
+class WindowBaseImpl : public virtual ComObject,
+ public virtual IAvnWindowBase,
+ public INSWindowHolder
{
private:
NSCursor* cursor;
public:
FORWARD_IUNKNOWN()
+ BEGIN_INTERFACE_MAP()
+ INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
+ END_INTERFACE_MAP()
+
virtual ~WindowBaseImpl()
{
View = NULL;
@@ -115,7 +123,12 @@ public:
{
return Window;
}
-
+
+ virtual AvnView* GetNSView() override
+ {
+ return View;
+ }
+
virtual HRESULT Show(bool activate, bool isDialog) override
{
START_COM_CALL;
@@ -722,7 +735,7 @@ private:
return E_INVALIDARG;
// If one tries to show a child window with a minimized parent window, then the parent window will be
- // restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+ // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
// state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
if (cparent->WindowState() == Minimized)
cparent->SetWindowState(Normal);
@@ -1396,6 +1409,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
AvnPixelSize _lastPixelSize;
NSObject* _renderTarget;
AvnPlatformResizeReason _resizeReason;
+ AvnAccessibilityElement* _accessibilityChild;
}
- (void)onClosed
@@ -2050,6 +2064,37 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_resizeReason = reason;
}
+- (AvnAccessibilityElement *) accessibilityChild
+{
+ if (_accessibilityChild == nil)
+ {
+ auto peer = _parent->BaseEvents->GetAutomationPeer();
+
+ if (peer == nil)
+ return nil;
+
+ _accessibilityChild = [AvnAccessibilityElement acquire:peer];
+ }
+
+ return _accessibilityChild;
+}
+
+- (NSArray *)accessibilityChildren
+{
+ auto child = [self accessibilityChild];
+ return NSAccessibilityUnignoredChildrenForOnlyChild(child);
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ return [[self accessibilityChild] accessibilityHitTest:point];
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ return [[self accessibilityChild] accessibilityFocusedUIElement];
+}
+
@end
@@ -2062,6 +2107,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _isExtended;
AvnMenu* _menu;
double _lastScaling;
+ IAvnAutomationPeer* _automationPeer;
+ NSMutableArray* _automationChildren;
}
-(void) setIsExtended:(bool)value;
@@ -2465,6 +2512,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
}
}
+
@end
class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index f0b894b596..72d90abbf3 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -87,7 +87,8 @@ partial class Build : NukeBuild
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
}
- ExecWait("dotnet version:", "dotnet", "--version");
+ ExecWait("dotnet version:", "dotnet", "--info");
+ ExecWait("dotnet workloads:", "dotnet", "workload list");
}
IReadOnlyCollection
+
+ true
+ https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json
+ 7.0.0-*
+
+
+
@@ -15,6 +22,14 @@
+
+
+
+
+
+
+
+
en
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 0c8fd9465c..4b81935452 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -118,6 +118,13 @@ namespace ControlCatalog.NetCore
})
.UseSkia()
.UseManagedSystemDialogs()
+ .AfterSetup(builder =>
+ {
+ builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
+ {
+ StartupScreenIndex = 1,
+ });
+ })
.LogToTrace();
static void SilenceConsole()
diff --git a/samples/ControlCatalog.NetCore/rd.xml b/samples/ControlCatalog.NetCore/rd.xml
new file mode 100644
index 0000000000..27db7f34ca
--- /dev/null
+++ b/samples/ControlCatalog.NetCore/rd.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
index 199fa85ad2..520bbdf32b 100644
--- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
+++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj
@@ -1,6 +1,7 @@
net6.0
+ false
enable
True
diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
index db1e16166a..12d1d5645e 100644
--- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
+++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
@@ -1,186 +1,16 @@
-
-
+
- Debug
- iPhoneSimulator
- {57E0455D-D565-44BB-B069-EE1AA20F8337}
- {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Exe
- ControlCatalog.iOS
- Resources
- ControlCatalogiOS
- true
- NSUrlSessionHandler
- PackageReference
- automatic
+ manual
+ net6.0-ios
+ 10.0
+
+ True
+ iossimulator-x64
+
-
- true
- full
- false
- bin\iPhoneSimulator\Debug
- DEBUG
- prompt
- 4
- false
- x86_64
- None
- True
- 9.1
- False
- False
- False
- False
- False
- False
- False
- False
- True
- Default
- HttpClientHandler
- False
-
-
- none
- true
- bin\iPhoneSimulator\Release
- prompt
- 4
- None
- x86_64
- false
-
-
- true
- full
- false
- bin\iPhone\Debug
- DEBUG
- prompt
- 4
- false
- ARMv7, ARM64
- Entitlements.plist
- iPhone Developer
- true
-
-
- none
- true
- bin\iPhone\Release
- prompt
- 4
- Entitlements.plist
- ARMv7, ARM64
- false
- iPhone Developer
-
-
- none
- True
- bin\iPhone\Ad-Hoc
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- True
- Automatic:AdHoc
- iPhone Distribution
-
-
- none
- True
- bin\iPhone\AppStore
- prompt
- 4
- False
- ARMv7, ARM64
- Entitlements.plist
- Automatic:AppStore
- iPhone Distribution
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {4488AD85-1495-4809-9AA4-DDFE0A48527E}
- Avalonia.iOS
- false
- false
-
-
- {3E53A01A-B331-47F3-B828-4A5717E77A24}
- Avalonia.Markup.Xaml
-
-
- {6417E941-21BC-467B-A771-0DE389353CE6}
- Avalonia.Markup
-
-
- {D211E587-D8BC-45B9-95A4-F297C8FA5200}
- Avalonia.Animation
-
-
- {B09B78D8-9B26-48B0-9149-D64A2F120F3F}
- Avalonia.Base
-
-
- {D2221C82-4A25-4583-9B43-D791E3F6820C}
- Avalonia.Controls
-
-
- {7062AE20-5DCC-4442-9645-8195BDECE63E}
- Avalonia.Diagnostics
-
-
- {62024B2D-53EB-4638-B26B-85EEAA54866E}
- Avalonia.Input
-
-
- {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}
- Avalonia.Interactivity
-
-
- {42472427-4774-4C81-8AFF-9F27B8E31721}
- Avalonia.Layout
-
-
- {EB582467-6ABB-43A1-B052-E981BA910E3A}
- Avalonia.Visuals
-
-
- {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}
- Avalonia.Styling
-
-
- {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}
- Avalonia.Themes.Default
-
-
- {7d2d3083-71dd-4cc9-8907-39a0d86fb322}
- Avalonia.Skia
-
-
- {d0a739b9-3c68-4ba6-a328-41606954b6bd}
- ControlCatalog
-
-
+
+
-
-
-
-
diff --git a/samples/ControlCatalog.iOS/Info.plist b/samples/ControlCatalog.iOS/Info.plist
index 216fd9c333..6ffe3ba662 100644
--- a/samples/ControlCatalog.iOS/Info.plist
+++ b/samples/ControlCatalog.iOS/Info.plist
@@ -5,7 +5,7 @@
CFBundleDisplayName
ControlCatalog.iOS
CFBundleIdentifier
- com.companyname.ControlCatalog.iOS
+ Avalonia.ControlCatalog
CFBundleShortVersionString
1.0
CFBundleVersion
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 8.0
+ 10.0
UIDeviceFamily
1
@@ -28,6 +28,7 @@
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
@@ -38,5 +39,9 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ UIStatusBarHidden
+
+ UIViewControllerBasedStatusBarAppearance
+
diff --git a/samples/ControlCatalog.iOS/Main.cs b/samples/ControlCatalog.iOS/Main.cs
index fe039ba69e..2400115041 100644
--- a/samples/ControlCatalog.iOS/Main.cs
+++ b/samples/ControlCatalog.iOS/Main.cs
@@ -9,7 +9,7 @@ namespace ControlCatalog.iOS
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
- UIApplication.Main(args, null, "AppDelegate");
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs b/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs
deleted file mode 100644
index 0a5a598651..0000000000
--- a/samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("ControlCatalog.iOS")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("ControlCatalog.iOS")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("57e0455d-d565-44bb-b069-ee1aa20f8337")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib b/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
index be4abb2b43..5d3ccc97db 100644
--- a/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
+++ b/samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
@@ -11,7 +11,7 @@
-
+
{d0a739b9-3c68-4ba6-a328-41606954b6bd}
diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs
index 7e49cb5dfa..880b210a6c 100644
--- a/src/Android/Avalonia.Android/AndroidInputMethod.cs
+++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs
@@ -13,7 +13,6 @@ namespace Avalonia.Android
{
private readonly TView _host;
private readonly InputMethodManager _imm;
- private IInputElement _inputElement;
public AndroidInputMethod(TView host)
{
@@ -25,7 +24,7 @@ namespace Avalonia.Android
_host.Focusable = true;
_host.FocusableInTouchMode = true;
- _host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
+ _host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
}
public void Reset()
@@ -33,8 +32,10 @@ namespace Avalonia.Android
_imm.RestartInput(_host);
}
- public void SetActive(bool active)
+ public void SetClient(ITextInputMethodClient client)
{
+ var active = client is { };
+
if (active)
{
_host.RequestFocus();
@@ -49,20 +50,8 @@ namespace Avalonia.Android
{
}
- public void SetOptions(TextInputOptionsQueryEventArgs options)
+ public void SetOptions(TextInputOptions options)
{
- if (_inputElement != null)
- {
- _inputElement.PointerReleased -= RestoreSoftKeyboard;
- }
-
- _inputElement = options.Source as InputElement;
-
- if (_inputElement == null)
- {
- _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
- }
-
_host.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = options.ContentType switch
@@ -70,7 +59,7 @@ namespace Avalonia.Android
TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
- TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone,
+ TextInputContentType.Digits => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
_ => global::Android.Text.InputTypes.ClassText
};
@@ -83,9 +72,9 @@ namespace Avalonia.Android
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
- });
- //_inputElement.PointerReleased += RestoreSoftKeyboard;
+ outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
+ });
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index 6a940a54f1..61aa6ce946 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -1,16 +1,15 @@
using System;
-
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Android;
using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input;
-using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
-using Avalonia.Shared.PlatformSupport;
using Avalonia.Skia;
namespace Avalonia
@@ -20,9 +19,10 @@ namespace Avalonia
public static T UseAndroid(this T builder) where T : AppBuilderBase, new()
{
var options = AvaloniaLocator.Current.GetService() ?? new AndroidPlatformOptions();
- builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android");
- builder.UseSkia();
- return builder;
+
+ return builder
+ .UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
+ .UseSkia();
}
}
}
@@ -44,7 +44,7 @@ namespace Avalonia.Android
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
- public static void Initialize(Type appType, AndroidPlatformOptions options)
+ public static void Initialize(AndroidPlatformOptions options)
{
Options = options;
@@ -59,8 +59,7 @@ namespace Avalonia.Android
.Bind().ToSingleton()
.Bind().ToConstant(new ChoreographerTimer())
.Bind().ToConstant(new RenderLoop())
- .Bind().ToSingleton()
- .Bind().ToConstant(new AssetLoader(appType.Assembly));
+ .Bind().ToSingleton();
SkiaPlatform.Initialize();
diff --git a/src/Android/Avalonia.Android/AppBuilder.cs b/src/Android/Avalonia.Android/AppBuilder.cs
deleted file mode 100644
index 805bb61655..0000000000
--- a/src/Android/Avalonia.Android/AppBuilder.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Shared.PlatformSupport;
-
-namespace Avalonia
-{
- public sealed class AppBuilder : AppBuilderBase
- {
- public AppBuilder() : base(new StandardRuntimePlatform(),
- builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
- {
-
- }
- }
-}
diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj
index 8c6775733f..203c3accd6 100644
--- a/src/Android/Avalonia.Android/Avalonia.Android.csproj
+++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj
@@ -1,13 +1,19 @@
-
+
- monoandroid11.0
+ net6.0-android
+ $(TargetFrameworks);monoandroid11.0
+ 21
true
+ true
+ portable
+
+
+
+
+
-
-
-
diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs
index 3c9f373a66..f5d620a97a 100644
--- a/src/Android/Avalonia.Android/AvaloniaActivity.cs
+++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs
@@ -1,35 +1,82 @@
-using Android.App;
using Android.OS;
-using Android.Views;
+using AndroidX.AppCompat.App;
+using Android.Content.Res;
+using AndroidX.Lifecycle;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Controls;
namespace Avalonia.Android
{
- public abstract class AvaloniaActivity : Activity
+ public abstract class AvaloniaActivity : AppCompatActivity where TApp : Application, new()
{
+ internal class SingleViewLifetime : ISingleViewApplicationLifetime
+ {
+ public AvaloniaView View { get; internal set; }
+
+ public Control MainView
+ {
+ get => (Control)View.Content;
+ set => View.Content = value;
+ }
+ }
+
internal AvaloniaView View;
- object _content;
+ internal AvaloniaViewModel _viewModel;
+
+ protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected override void OnCreate(Bundle savedInstanceState)
{
+ var builder = AppBuilder.Configure();
+
+ CustomizeAppBuilder(builder);
+
View = new AvaloniaView(this);
- if (_content != null)
- View.Content = _content;
SetContentView(View);
+
+ var lifetime = new SingleViewLifetime();
+ lifetime.View = View;
+
+ builder.AfterSetup(x =>
+ {
+ _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
+
+ if (_viewModel.Content != null)
+ {
+ View.Content = _viewModel.Content;
+ }
+
+ View.Prepare();
+ });
+
+ builder.SetupWithLifetime(lifetime);
+
base.OnCreate(savedInstanceState);
}
-
public object Content
{
get
{
- return _content;
+ return _viewModel.Content;
}
set
{
- _content = value;
+ _viewModel.Content = value;
if (View != null)
View.Content = value;
}
}
+
+ public override void OnConfigurationChanged(Configuration newConfig)
+ {
+ base.OnConfigurationChanged(newConfig);
+ }
+
+ protected override void OnDestroy()
+ {
+ View.Content = null;
+
+ base.OnDestroy();
+ }
}
}
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index 8de3657283..8177cf1f69 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Android
{
public class AvaloniaView : FrameLayout
{
- private readonly EmbeddableControlRoot _root;
+ private EmbeddableControlRoot _root;
private readonly ViewImpl _view;
private IDisposable? _timerSubscription;
@@ -21,6 +21,11 @@ namespace Avalonia.Android
{
_view = new ViewImpl(context);
AddView(_view.View);
+
+ }
+
+ internal void Prepare ()
+ {
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
}
diff --git a/src/Android/Avalonia.Android/AvaloniaViewModel.cs b/src/Android/Avalonia.Android/AvaloniaViewModel.cs
new file mode 100644
index 0000000000..1b2c00987a
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaViewModel.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Avalonia.Android
+{
+ internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
+ {
+ public object Content { get; set; }
+ }
+}
diff --git a/src/Android/Avalonia.Android/Resources/AboutResources.txt b/src/Android/Avalonia.Android/Resources/AboutResources.txt
deleted file mode 100644
index 194ae28a59..0000000000
--- a/src/Android/Avalonia.Android/Resources/AboutResources.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-Images, layout descriptions, binary blobs and string dictionaries can be included
-in your application as resource files. Various Android APIs are designed to
-operate on the resource IDs instead of dealing with images, strings or binary blobs
-directly.
-
-For example, a sample Android app that contains a user interface layout (main.xml),
-an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
-would keep its resources in the "Resources" directory of the application:
-
-Resources/
- drawable-hdpi/
- icon.png
-
- drawable-ldpi/
- icon.png
-
- drawable-mdpi/
- icon.png
-
- layout/
- main.xml
-
- values/
- strings.xml
-
-In order to get the build system to recognize Android resources, set the build action to
-"AndroidResource". The native Android APIs do not operate directly with filenames, but
-instead operate on resource IDs. When you compile an Android application that uses resources,
-the build system will package the resources for distribution and generate a class called
-"Resource" that contains the tokens for each one of the resources included. For example,
-for the above Resources layout, this is what the Resource class would expose:
-
-public class Resource {
- public class drawable {
- public const int icon = 0x123;
- }
-
- public class layout {
- public const int main = 0x456;
- }
-
- public class strings {
- public const int first_string = 0xabc;
- public const int second_string = 0xbcd;
- }
-}
-
-You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
-to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
-string in the dictionary file values/strings.xml.
\ No newline at end of file
diff --git a/src/Android/Avalonia.Android/Resources/Values/Strings.xml b/src/Android/Avalonia.Android/Resources/Values/Strings.xml
deleted file mode 100644
index 3823c6f4c6..0000000000
--- a/src/Android/Avalonia.Android/Resources/Values/Strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- Hello World, Click Me!
- $projectname$
-
\ No newline at end of file
diff --git a/src/Android/Avalonia.Android/RuntimeInfo.cs b/src/Android/Avalonia.Android/RuntimeInfo.cs
deleted file mode 100644
index bb2466c357..0000000000
--- a/src/Android/Avalonia.Android/RuntimeInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Avalonia.Platform;
-
-namespace Avalonia.Shared.PlatformSupport
-{
- internal partial class StandardRuntimePlatform
- {
- public RuntimePlatformInfo GetRuntimeInfo() => new RuntimePlatformInfo
- {
- IsCoreClr = false,
- IsDesktop = false,
- IsMobile = true,
- IsDotNetFramework = false,
- IsMono = true,
- IsUnix = true,
- OperatingSystem = OperatingSystemType.Android
- };
- }
-}
\ No newline at end of file
diff --git a/src/Android/Avalonia.Android/SoftKeyboardListner.cs b/src/Android/Avalonia.Android/SoftKeyboardListener.cs
similarity index 89%
rename from src/Android/Avalonia.Android/SoftKeyboardListner.cs
rename to src/Android/Avalonia.Android/SoftKeyboardListener.cs
index df658f6314..5e996639ed 100644
--- a/src/Android/Avalonia.Android/SoftKeyboardListner.cs
+++ b/src/Android/Avalonia.Android/SoftKeyboardListener.cs
@@ -9,7 +9,7 @@ using Avalonia.Input;
namespace Avalonia.Android
{
- class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
+ class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
@@ -17,7 +17,7 @@ namespace Avalonia.Android
private readonly View _host;
private bool _wasKeyboard;
- public SoftKeyboardListner(View view)
+ public SoftKeyboardListener(View view)
{
_host = view;
}
diff --git a/src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt b/src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt
deleted file mode 100644
index ee39886295..0000000000
--- a/src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Any raw assets you want to be deployed with your application can be placed in
-this directory (and child directories) and given a Build Action of "AndroidAsset".
-
-These files will be deployed with you package and will be accessible using Android's
-AssetManager, like this:
-
-public class ReadAsset : Activity
-{
- protected override void OnCreate (Bundle bundle)
- {
- base.OnCreate (bundle);
-
- InputStream input = Assets.Open ("my_asset.txt");
- }
-}
-
-Additionally, some Android functions will automatically load asset files:
-
-Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
\ No newline at end of file
diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
index 9104f1618c..8cb7aa1cfd 100644
--- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
+++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
@@ -1,153 +1,37 @@
-
-
+
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {FF69B927-C545-49AE-8E16-3D14D621AA12}
- {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- Library
- Properties
- Avalonia.AndroidTestApplication
- Avalonia.AndroidTestApplication
- 512
- true
- Resources\Resource.Designer.cs
- Off
- False
- v11.0
- Properties\AndroidManifest.xml
+ net6.0-android
+ 21
+ Exe
+ enable
+ com.Avalonia.AndroidTestApplication
+ 1
+ 1.0
+ apk
+ true
+ portable
-
+
+
True
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- True
- None
- True
- False
- False
- armeabi-v7a;x86
- Xamarin
- False
- True
- False
- False
- False
+ True
+ True
+
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- False
- Full
- True
- False
- False
- armeabi-v7a,x86
- Xamarin
- False
- False
- False
- False
- False
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
- Designer
-
-
-
-
-
-
-
+
+
+ True
+
+
+
+ True
+
+
-
- {7b92af71-6287-4693-9dcb-bd5b6e927e23}
- Avalonia.Android
-
-
- {3e53a01a-b331-47f3-b828-4a5717e77a24}
- Avalonia.Markup.Xaml
-
-
- {d211e587-d8bc-45b9-95a4-f297c8fa5200}
- Avalonia.Animation
-
-
- {b09b78d8-9b26-48b0-9149-d64a2f120f3f}
- Avalonia.Base
-
-
- {d2221c82-4a25-4583-9b43-d791e3f6820c}
- Avalonia.Controls
-
-
- {7062ae20-5dcc-4442-9645-8195bdece63e}
- Avalonia.Diagnostics
-
-
- {62024b2d-53eb-4638-b26b-85eeaa54866e}
- Avalonia.Input
-
-
- {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}
- Avalonia.Interactivity
-
-
- {42472427-4774-4c81-8aff-9f27b8e31721}
- Avalonia.Layout
-
-
- {eb582467-6abb-43a1-b052-e981ba910e3a}
- Avalonia.Visuals
-
-
- {f1baa01a-f176-4c6a-b39d-5b40bb1b148f}
- Avalonia.Styling
-
-
- {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}
- Avalonia.Themes.Default
-
-
- {7d2d3083-71dd-4cc9-8907-39a0d86fb322}
- Avalonia.Skia
-
+
+
-
-
-
-
-
-
diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
index 5f33cadf2e..471b982d9e 100644
--- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
+++ b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
@@ -14,6 +14,8 @@ namespace Avalonia.AndroidTestApplication
[Activity(Label = "Main",
MainLauncher = true,
Icon = "@drawable/icon",
+ Theme = "@style/Theme.AppCompat.NoActionBar",
+ ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : AvaloniaActivity
diff --git a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml b/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
index 57ee503005..ad8134f628 100644
--- a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
+++ b/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
@@ -1,6 +1,4 @@
-
-
+
-
-
\ No newline at end of file
+
diff --git a/src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs b/src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs
deleted file mode 100644
index 2528202974..0000000000
--- a/src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-
-[assembly: AssemblyTitle("Avalonia.AndroidTestApplication")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.AndroidTestApplication")]
-[assembly: AssemblyCopyright("Copyright © 2015")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: ComVisible(false)]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
deleted file mode 100644
index 87fd47df25..0000000000
--- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-#pragma warning disable 1591
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-[assembly: global::Android.Runtime.ResourceDesignerAttribute("Avalonia.AndroidTestApplication.Resource", IsApplication=true)]
-
-namespace Avalonia.AndroidTestApplication
-{
-
-
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
- public partial class Resource
- {
-
- static Resource()
- {
- global::Android.Runtime.ResourceIdManager.UpdateIdValues();
- }
-
- public static void UpdateIdValues()
- {
- }
-
- public partial class Attribute
- {
-
- static Attribute()
- {
- global::Android.Runtime.ResourceIdManager.UpdateIdValues();
- }
-
- private Attribute()
- {
- }
- }
-
- public partial class Drawable
- {
-
- // aapt resource value: 0x7F010000
- public const int Icon = 2130771968;
-
- static Drawable()
- {
- global::Android.Runtime.ResourceIdManager.UpdateIdValues();
- }
-
- private Drawable()
- {
- }
- }
-
- public partial class String
- {
-
- // aapt resource value: 0x7F020000
- public const int ApplicationName = 2130837504;
-
- // aapt resource value: 0x7F020001
- public const int Hello = 2130837505;
-
- static String()
- {
- global::Android.Runtime.ResourceIdManager.UpdateIdValues();
- }
-
- private String()
- {
- }
- }
- }
-}
-#pragma warning restore 1591
diff --git a/src/Android/Avalonia.AndroidTestApplication/app.config b/src/Android/Avalonia.AndroidTestApplication/app.config
deleted file mode 100644
index fc064cedfb..0000000000
--- a/src/Android/Avalonia.AndroidTestApplication/app.config
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
index 0e027712e0..2fe68e824d 100644
--- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs
@@ -146,6 +146,7 @@ namespace Avalonia.Collections
{
if (_inner.TryGetValue(key, out var value))
{
+ _inner.Remove(key);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Count)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]"));
diff --git a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
index 160c7301f5..1ca70140ec 100644
--- a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
+++ b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
@@ -22,19 +22,9 @@ namespace Avalonia.Data.Core.Plugins
var method = GetFirstMethodWithName(instance.GetType(), methodName);
- if (method != null)
+ if (method is not null)
{
- var parameters = method.GetParameters();
-
- if (parameters.Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
- {
- var exception = new ArgumentException(
- "Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.",
- nameof(methodName));
- return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
- }
-
- return new Accessor(reference, method, parameters);
+ return new Accessor(reference, method);
}
else
{
@@ -82,18 +72,20 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase
{
- public Accessor(WeakReference
diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs
index ee67f303f3..ee3be1d5b3 100644
--- a/src/Avalonia.Controls/Border.cs
+++ b/src/Avalonia.Controls/Border.cs
@@ -17,14 +17,14 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty BackgroundProperty =
- AvaloniaProperty.Register(nameof(Background));
+ public static readonly StyledProperty BackgroundProperty =
+ AvaloniaProperty.Register(nameof(Background));
///
/// Defines the property.
///
- public static readonly StyledProperty BorderBrushProperty =
- AvaloniaProperty.Register(nameof(BorderBrush));
+ public static readonly StyledProperty BorderBrushProperty =
+ AvaloniaProperty.Register(nameof(BorderBrush));
///
/// Defines the property.
@@ -91,7 +91,7 @@ namespace Avalonia.Controls
///
/// Gets or sets a brush with which to paint the background.
///
- public IBrush Background
+ public IBrush? Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
@@ -100,7 +100,7 @@ namespace Avalonia.Controls
///
/// Gets or sets a brush with which to paint the border.
///
- public IBrush BorderBrush
+ public IBrush? BorderBrush
{
get { return GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index 8537c9acbc..72495bdcb3 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Windows.Input;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
@@ -42,30 +43,30 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly DirectProperty
/// The event sender.
/// The event args.
- private void RootCancelKeyDown(object sender, KeyEventArgs e)
+ private void RootCancelKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && IsVisible && IsEnabled)
{
@@ -566,6 +555,9 @@ namespace Avalonia.Controls
}
}
+ ///
+ /// Updates the visual state of the control by applying latest PseudoClasses.
+ ///
private void UpdatePseudoClasses(bool isPressed)
{
PseudoClasses.Set(":pressed", isPressed);
diff --git a/src/Avalonia.Controls/ButtonSpinner.cs b/src/Avalonia.Controls/ButtonSpinner.cs
index f1c1a55a17..31aba024ae 100644
--- a/src/Avalonia.Controls/ButtonSpinner.cs
+++ b/src/Avalonia.Controls/ButtonSpinner.cs
@@ -42,11 +42,11 @@ namespace Avalonia.Controls
UpdatePseudoClasses(ButtonSpinnerLocation);
}
- private Button _decreaseButton;
+ private Button? _decreaseButton;
///
/// Gets or sets the DecreaseButton template part.
///
- private Button DecreaseButton
+ private Button? DecreaseButton
{
get { return _decreaseButton; }
set
@@ -63,11 +63,11 @@ namespace Avalonia.Controls
}
}
- private Button _increaseButton;
+ private Button? _increaseButton;
///
/// Gets or sets the IncreaseButton template part.
///
- private Button IncreaseButton
+ private Button? IncreaseButton
{
get
{
@@ -241,8 +241,8 @@ namespace Avalonia.Controls
{
if (e.Sender is ButtonSpinner spinner)
{
- var oldValue = (bool)e.OldValue;
- var newValue = (bool)e.NewValue;
+ var oldValue = (bool)e.OldValue!;
+ var newValue = (bool)e.NewValue!;
spinner.OnAllowSpinChanged(oldValue, newValue);
}
}
@@ -268,7 +268,7 @@ namespace Avalonia.Controls
///
/// The event sender.
/// The event args.
- private void OnButtonClick(object sender, RoutedEventArgs e)
+ private void OnButtonClick(object? sender, RoutedEventArgs e)
{
if (AllowSpin)
{
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 2c8a7c0831..6c83308b39 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -240,11 +240,11 @@ namespace Avalonia.Controls
private bool _isShiftPressed;
private bool _displayDateIsChanging = false;
- internal CalendarDayButton FocusButton { get; set; }
- internal CalendarButton FocusCalendarButton { get; set; }
+ internal CalendarDayButton? FocusButton { get; set; }
+ internal CalendarButton? FocusCalendarButton { get; set; }
- internal Panel Root { get; set; }
- internal CalendarItem MonthControl
+ internal Panel? Root { get; set; }
+ internal CalendarItem? MonthControl
{
get
{
@@ -280,7 +280,7 @@ namespace Avalonia.Controls
private void OnFirstDayOfWeekChanged(AvaloniaPropertyChangedEventArgs e)
{
- if (IsValidFirstDayOfWeek(e.NewValue))
+ if (IsValidFirstDayOfWeek(e.NewValue!))
{
UpdateMonths();
}
@@ -373,9 +373,9 @@ namespace Avalonia.Controls
/// The DependencyPropertyChangedEventArgs.
private void OnDisplayModePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
- CalendarMode mode = (CalendarMode)e.NewValue;
- CalendarMode oldMode = (CalendarMode)e.OldValue;
- CalendarItem monthControl = MonthControl;
+ CalendarMode mode = (CalendarMode)e.NewValue!;
+ CalendarMode oldMode = (CalendarMode)e.OldValue!;
+ CalendarItem? monthControl = MonthControl;
if (monthControl != null)
{
@@ -459,7 +459,7 @@ namespace Avalonia.Controls
}
private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e)
{
- if (IsValidSelectionMode(e.NewValue))
+ if (IsValidSelectionMode(e.NewValue!))
{
_displayDateIsChanging = true;
SelectedDate = null;
@@ -656,7 +656,7 @@ namespace Avalonia.Controls
{
FocusButton.IsCurrent = false;
}
- FocusButton = FindDayButtonFromDay(LastSelectedDate.Value);
+ FocusButton = FindDayButtonFromDay(LastSelectedDate!.Value);
if (FocusButton != null)
{
FocusButton.IsCurrent = HasFocusInternal;
@@ -754,11 +754,11 @@ namespace Avalonia.Controls
private void OnDisplayDateChanged(AvaloniaPropertyChangedEventArgs e)
{
- UpdateDisplayDate(this, (DateTime)e.NewValue, (DateTime)e.OldValue);
+ UpdateDisplayDate(this, (DateTime)e.NewValue!, (DateTime)e.OldValue!);
}
private static void UpdateDisplayDate(Calendar c, DateTime addedDate, DateTime removedDate)
{
- Contract.Requires(c != null);
+ _ = c ?? throw new ArgumentNullException(nameof(c));
// If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart
if (DateTime.Compare(addedDate, c.DisplayDateRangeStart) < 0)
@@ -871,7 +871,7 @@ namespace Avalonia.Controls
if (cal.SelectedDates.Count > 0)
{
selectedDateMin = cal.SelectedDates[0];
- Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMin) == 0, "The SelectedDate should be the minimum selected date!");
+ Debug.Assert(DateTime.Compare(cal.SelectedDate!.Value, selectedDateMin) == 0, "The SelectedDate should be the minimum selected date!");
}
else
{
@@ -959,7 +959,7 @@ namespace Avalonia.Controls
if (cal.SelectedDates.Count > 0)
{
selectedDateMax = cal.SelectedDates[0];
- Debug.Assert(DateTime.Compare(cal.SelectedDate.Value, selectedDateMax) == 0, "The SelectedDate should be the maximum SelectedDate!");
+ Debug.Assert(DateTime.Compare(cal.SelectedDate!.Value, selectedDateMax) == 0, "The SelectedDate should be the maximum SelectedDate!");
}
else
{
@@ -1003,9 +1003,9 @@ namespace Avalonia.Controls
///
internal bool CalendarDatePickerDisplayDateFlag { get; set; }
- internal CalendarDayButton FindDayButtonFromDay(DateTime day)
+ internal CalendarDayButton? FindDayButtonFromDay(DateTime day)
{
- CalendarItem monthControl = MonthControl;
+ CalendarItem? monthControl = MonthControl;
// REMOVE_RTM: should be updated if we support MultiCalendar
int count = RowsPerMonth * ColumnsPerMonth;
@@ -1054,7 +1054,7 @@ namespace Avalonia.Controls
internal void OnHeaderClick()
{
Debug.Assert(DisplayMode == CalendarMode.Year || DisplayMode == CalendarMode.Decade, "The DisplayMode should be Year or Decade");
- CalendarItem monthControl = MonthControl;
+ CalendarItem? monthControl = MonthControl;
if (monthControl != null && monthControl.MonthView != null && monthControl.YearView != null)
{
monthControl.MonthView.IsVisible = false;
@@ -1065,7 +1065,7 @@ namespace Avalonia.Controls
internal void ResetStates()
{
- CalendarItem monthControl = MonthControl;
+ CalendarItem? monthControl = MonthControl;
int count = RowsPerMonth * ColumnsPerMonth;
if (monthControl != null)
{
@@ -1083,7 +1083,7 @@ namespace Avalonia.Controls
internal void UpdateMonths()
{
- CalendarItem monthControl = MonthControl;
+ CalendarItem? monthControl = MonthControl;
if (monthControl != null)
{
switch (DisplayMode)
@@ -1163,6 +1163,8 @@ namespace Avalonia.Controls
{
if (HoverEnd != null && HoverStart != null)
{
+ Debug.Assert(MonthControl is not null);
+
int startIndex, endIndex, i;
CalendarItem monthControl = MonthControl;
@@ -1173,7 +1175,7 @@ namespace Avalonia.Controls
for (i = startIndex; i <= endIndex; i++)
{
- if (monthControl.MonthView.Children[i] is CalendarDayButton b)
+ if (monthControl.MonthView!.Children[i] is CalendarDayButton b)
{
b.IsSelected = true;
var d = b.DataContext as DateTime?;
@@ -1201,6 +1203,8 @@ namespace Avalonia.Controls
{
if (HoverEnd != null && HoverStart != null)
{
+ Debug.Assert(MonthControl is not null);
+
CalendarItem monthControl = MonthControl;
if (HoverEndIndex != null && HoverStartIndex != null)
@@ -1212,7 +1216,7 @@ namespace Avalonia.Controls
{
for (i = startIndex; i <= endIndex; i++)
{
- if (monthControl.MonthView.Children[i] is CalendarDayButton b)
+ if (monthControl.MonthView!.Children[i] is CalendarDayButton b)
{
var d = b.DataContext as DateTime?;
@@ -1231,7 +1235,7 @@ namespace Avalonia.Controls
// It is SingleRange
for (i = startIndex; i <= endIndex; i++)
{
- ((CalendarDayButton)monthControl.MonthView.Children[i]).IsSelected = false;
+ ((CalendarDayButton)monthControl.MonthView!.Children[i]).IsSelected = false;
}
}
}
@@ -1239,6 +1243,11 @@ namespace Avalonia.Controls
}
internal void SortHoverIndexes(out int startIndex, out int endIndex)
{
+ Debug.Assert(HoverStart.HasValue);
+ Debug.Assert(HoverEnd.HasValue);
+ Debug.Assert(HoverStartIndex.HasValue);
+ Debug.Assert(HoverEndIndex.HasValue);
+
if (DateTimeHelper.CompareDays(HoverEnd.Value, HoverStart.Value) > 0)
{
startIndex = HoverStartIndex.Value;
@@ -1373,6 +1382,8 @@ namespace Avalonia.Controls
}
private void OnMonthClick()
{
+ Debug.Assert(MonthControl is not null);
+
CalendarItem monthControl = MonthControl;
if (monthControl != null && monthControl.YearView != null && monthControl.MonthView != null)
{
@@ -1400,7 +1411,7 @@ namespace Avalonia.Controls
}
}
- public event EventHandler SelectedDatesChanged;
+ public event EventHandler? SelectedDatesChanged;
///
/// Occurs when the
@@ -1410,19 +1421,19 @@ namespace Avalonia.Controls
///
/// This event occurs after DisplayDate is assigned its new value.
///
- public event EventHandler DisplayDateChanged;
+ public event EventHandler? DisplayDateChanged;
///
/// Occurs when the
///
/// property is changed.
///
- public event EventHandler DisplayModeChanged;
+ public event EventHandler? DisplayModeChanged;
///
/// Inherited code: Requires comment.
///
- internal event EventHandler DayButtonMouseUp;
+ internal event EventHandler? DayButtonMouseUp;
///
/// This method adds the days that were selected by Keyboard to the
@@ -1461,7 +1472,7 @@ namespace Avalonia.Controls
SelectedDates.ClearInternal();
if (shift)
{
- CalendarDayButton b;
+ CalendarDayButton? b;
_isShiftPressed = true;
if (HoverStart == null)
{
@@ -1513,6 +1524,8 @@ namespace Avalonia.Controls
}
else
{
+ Debug.Assert(HoverEndInternal is not null);
+
// For Home, End, PageUp and PageDown Keys there
// is no easy way to predict the index value
b = FindDayButtonFromDay(HoverEndInternal.Value);
@@ -1524,6 +1537,7 @@ namespace Avalonia.Controls
}
}
+ Debug.Assert(HoverEnd is not null);
OnDayClick(HoverEnd.Value);
HighlightDays();
}
@@ -1557,7 +1571,7 @@ namespace Avalonia.Controls
base.OnPointerReleased(e);
if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left)
{
- FocusManager.Instance.Focus(this);
+ FocusManager.Instance?.Focus(this);
}
}
@@ -1876,8 +1890,8 @@ namespace Avalonia.Controls
// since DisplayDate is not equal to
// DateTime.MaxValue we are sure selectedDate is\
// not null
- selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value;
- selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value;
+ selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1)!.Value;
+ selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1)!.Value;
}
else
{
@@ -2098,7 +2112,7 @@ namespace Avalonia.Controls
if (Root != null)
{
- CalendarItem month = e.NameScope.Find(PART_ElementMonth);
+ CalendarItem? month = e.NameScope.Find(PART_ElementMonth);
if (month != null)
{
diff --git a/src/Avalonia.Controls/Calendar/CalendarButton.cs b/src/Avalonia.Controls/Calendar/CalendarButton.cs
index 76af933b55..0a8e4dfae8 100644
--- a/src/Avalonia.Controls/Calendar/CalendarButton.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarButton.cs
@@ -45,7 +45,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets or sets the Calendar associated with this button.
///
- internal Calendar Owner { get; set; }
+ internal Calendar? Owner { get; set; }
///
/// Gets or sets a value indicating whether the button is focused.
@@ -120,7 +120,7 @@ namespace Avalonia.Controls.Primitives
/// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement.
///
- public event EventHandler CalendarLeftMouseButtonDown;
+ public event EventHandler? CalendarLeftMouseButtonDown;
///
/// Occurs when the left mouse button is released (or the tip of the
@@ -128,7 +128,7 @@ namespace Avalonia.Controls.Primitives
/// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture).
///
- public event EventHandler CalendarLeftMouseButtonUp;
+ public event EventHandler? CalendarLeftMouseButtonUp;
///
/// Provides class handling for the MouseLeftButtonDown event that
diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
index cd9c80d3e0..f1c56a7331 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
@@ -123,15 +123,15 @@ namespace Avalonia.Controls
private const string ElementPopup = "PART_Popup";
private const string ElementCalendar = "PART_Calendar";
- private Calendar _calendar;
+ private Calendar? _calendar;
private string _defaultText;
- private Button _dropDownButton;
+ private Button? _dropDownButton;
//private Canvas _outsideCanvas;
//private Canvas _outsidePopupCanvas;
- private Popup _popUp;
- private TextBox _textBox;
- private IDisposable _textBoxTextChangedSubscription;
- private IDisposable _buttonPointerPressedSubscription;
+ private Popup? _popUp;
+ private TextBox? _textBox;
+ private IDisposable? _textBoxTextChangedSubscription;
+ private IDisposable? _buttonPointerPressedSubscription;
private DateTime? _onOpenSelectedDate;
private bool _settingSelectedDate;
@@ -141,7 +141,7 @@ namespace Avalonia.Controls
private DateTime? _displayDateEnd;
private bool _isDropDownOpen;
private DateTime? _selectedDate;
- private string _text;
+ private string? _text;
private bool _suspendTextChangeHandler = false;
private bool _isPopupClosing = false;
private bool _ignoreButtonClick = false;
@@ -153,7 +153,7 @@ namespace Avalonia.Controls
/// A collection of dates that cannot be selected. The default value is
/// an empty collection.
///
- public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }
+ public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; }
public static readonly DirectProperty DisplayDateProperty =
AvaloniaProperty.RegisterDirect(
@@ -186,7 +186,8 @@ namespace Avalonia.Controls
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v,
- enableDataValidation: true);
+ enableDataValidation: true,
+ defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty SelectedDateFormatProperty =
AvaloniaProperty.Register(
@@ -200,12 +201,12 @@ namespace Avalonia.Controls
defaultValue: "d",
validate: IsValidDateFormatString);
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
- public static readonly StyledProperty WatermarkProperty =
+ public static readonly StyledProperty WatermarkProperty =
TextBox.WatermarkProperty.AddOwner();
public static readonly StyledProperty UseFloatingWatermarkProperty =
TextBox.UseFloatingWatermarkProperty.AddOwner();
@@ -361,13 +362,13 @@ namespace Avalonia.Controls
///
/// The text entered parses to a date that is not selectable.
///
- public string Text
+ public string? Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
}
- public string Watermark
+ public string? Watermark
{
get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
@@ -401,26 +402,26 @@ namespace Avalonia.Controls
/// Occurs when the drop-down
/// is closed.
///
- public event EventHandler CalendarClosed;
+ public event EventHandler? CalendarClosed;
///
/// Occurs when the drop-down
/// is opened.
///
- public event EventHandler CalendarOpened;
+ public event EventHandler? CalendarOpened;
///
/// Occurs when
/// is assigned a value that cannot be interpreted as a date.
///
- public event EventHandler DateValidationError;
+ public event EventHandler? DateValidationError;
///
/// Occurs when the
///
/// property is changed.
///
- public event EventHandler SelectedDateChanged;
+ public event EventHandler? SelectedDateChanged;
static CalendarDatePicker()
{
@@ -579,14 +580,14 @@ namespace Avalonia.Controls
private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
- var oldValue = (bool)e.OldValue;
- var value = (bool)e.NewValue;
+ var oldValue = (bool)e.OldValue!;
+ var value = (bool)e.NewValue!;
if (_popUp != null && _popUp.Child != null)
{
if (value != oldValue)
{
- if (_calendar.DisplayMode != CalendarMode.Month)
+ if (_calendar!.DisplayMode != CalendarMode.Month)
{
_calendar.DisplayMode = CalendarMode.Month;
}
@@ -660,7 +661,7 @@ namespace Avalonia.Controls
if (date != null)
{
- string s = DateTimeToString((DateTime)date);
+ string? s = DateTimeToString((DateTime)date);
Text = s;
}
}
@@ -679,8 +680,8 @@ namespace Avalonia.Controls
}
private void OnTextChanged(AvaloniaPropertyChangedEventArgs e)
{
- var oldValue = (string)e.OldValue;
- var value = (string)e.NewValue;
+ var oldValue = (string?)e.OldValue;
+ var value = (string?)e.NewValue;
if (!_suspendTextChangeHandler)
{
@@ -731,7 +732,7 @@ namespace Avalonia.Controls
}
private void OnDateSelected(DateTime? addedDate, DateTime? removedDate)
{
- EventHandler handler = this.SelectedDateChanged;
+ EventHandler? handler = this.SelectedDateChanged;
if (null != handler)
{
Collection addedItems = new Collection();
@@ -759,23 +760,23 @@ namespace Avalonia.Controls
CalendarOpened?.Invoke(this, e);
}
- private void Calendar_DayButtonMouseUp(object sender, PointerReleasedEventArgs e)
+ private void Calendar_DayButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{
Focus();
IsDropDownOpen = false;
}
- private void Calendar_DisplayDateChanged(object sender, CalendarDateChangedEventArgs e)
+ private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e)
{
if (e.AddedDate != this.DisplayDate)
{
- SetValue(DisplayDateProperty, (DateTime) e.AddedDate);
+ SetValue(DisplayDateProperty, (DateTime) e.AddedDate!);
}
}
- private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
+ private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e)
{
Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!");
- if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0], SelectedDate.Value) != 0)
+ if (e.AddedItems.Count > 0 && SelectedDate.HasValue && DateTime.Compare((DateTime)e.AddedItems[0]!, SelectedDate.Value) != 0)
{
SelectedDate = (DateTime?)e.AddedItems[0];
}
@@ -796,7 +797,7 @@ namespace Avalonia.Controls
}
}
}
- private void Calendar_PointerReleased(object sender, PointerReleasedEventArgs e)
+ private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
if (e.InitialPressMouseButton == MouseButton.Left)
@@ -804,10 +805,9 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
- private void Calendar_KeyDown(object sender, KeyEventArgs e)
+ private void Calendar_KeyDown(object? sender, KeyEventArgs e)
{
- Calendar c = sender as Calendar;
- Contract.Requires(c != null);
+ Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
{
@@ -820,11 +820,11 @@ namespace Avalonia.Controls
}
}
}
- private void TextBox_GotFocus(object sender, RoutedEventArgs e)
+ private void TextBox_GotFocus(object? sender, RoutedEventArgs e)
{
IsDropDownOpen = false;
}
- private void TextBox_KeyDown(object sender, KeyEventArgs e)
+ private void TextBox_KeyDown(object? sender, KeyEventArgs e)
{
if (!e.Handled)
{
@@ -840,11 +840,11 @@ namespace Avalonia.Controls
_suspendTextChangeHandler = false;
}
}
- private void DropDownButton_PointerPressed(object sender, PointerPressedEventArgs e)
+ private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e)
{
_ignoreButtonClick = _isPopupClosing;
}
- private void DropDownButton_Click(object sender, RoutedEventArgs e)
+ private void DropDownButton_Click(object? sender, RoutedEventArgs e)
{
if (!_ignoreButtonClick)
{
@@ -855,7 +855,7 @@ namespace Avalonia.Controls
_ignoreButtonClick = false;
}
}
- private void PopUp_Closed(object sender, EventArgs e)
+ private void PopUp_Closed(object? sender, EventArgs e)
{
IsDropDownOpen = false;
@@ -891,7 +891,7 @@ namespace Avalonia.Controls
private void OpenPopUp()
{
_onOpenSelectedDate = SelectedDate;
- _popUp.IsOpen = true;
+ _popUp!.IsOpen = true;
}
///
@@ -914,7 +914,7 @@ namespace Avalonia.Controls
{
newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetCurrentDateFormat());
- if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate))
+ if (Calendar.IsValidDateSelection(this._calendar!, newSelectedDate))
{
return newSelectedDate;
}
@@ -941,7 +941,7 @@ namespace Avalonia.Controls
}
return null;
}
- private string DateTimeToString(DateTime d)
+ private string? DateTimeToString(DateTime d)
{
DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat();
@@ -982,7 +982,7 @@ namespace Avalonia.Controls
{
SetSelectedDate();
IsDropDownOpen = true;
- _calendar.Focus();
+ _calendar!.Focus();
}
private void SetSelectedDate()
{
@@ -1001,7 +1001,7 @@ namespace Avalonia.Controls
// ex: SelectedDate = DateTime(1008,12,19) but when
// "12/19/08" is parsed it is interpreted as
// DateTime(2008,12,19)
- string selectedDate = DateTimeToString(SelectedDate.Value);
+ string? selectedDate = DateTimeToString(SelectedDate.Value);
if (selectedDate == s)
{
return;
@@ -1053,7 +1053,7 @@ namespace Avalonia.Controls
// SelectedDate value:
if (SelectedDate != null)
{
- string newtext = this.DateTimeToString(SelectedDate.Value);
+ string? newtext = this.DateTimeToString(SelectedDate.Value);
SetValue(TextProperty, newtext);
return SelectedDate;
}
diff --git a/src/Avalonia.Controls/Calendar/CalendarDateRange.cs b/src/Avalonia.Controls/Calendar/CalendarDateRange.cs
index 47a0ad047b..88bc5ed7bd 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDateRange.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDateRange.cs
@@ -65,7 +65,7 @@ namespace Avalonia.Controls
/// Inherited code: Requires comment 2.
internal bool ContainsAny(CalendarDateRange range)
{
- Contract.Requires(range != null);
+ _ = range ?? throw new ArgumentNullException(nameof(range));
int start = DateTime.Compare(Start, range.Start);
diff --git a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs b/src/Avalonia.Controls/Calendar/CalendarDayButton.cs
index d5748bb9e4..2ba4e36260 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDayButton.cs
@@ -40,7 +40,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets or sets the Calendar associated with this button.
///
- internal Calendar Owner { get; set; }
+ internal Calendar? Owner { get; set; }
internal int Index { get; set; }
///
@@ -177,7 +177,7 @@ namespace Avalonia.Controls.Primitives
/// stylus touches the tablet PC) while the mouse pointer is over a
/// UIElement.
///
- public event EventHandler CalendarDayButtonMouseDown;
+ public event EventHandler? CalendarDayButtonMouseDown;
///
/// Occurs when the left mouse button is released (or the tip of the
@@ -185,7 +185,7 @@ namespace Avalonia.Controls.Primitives
/// stylus) is over a UIElement (or while a UIElement holds mouse
/// capture).
///
- public event EventHandler CalendarDayButtonMouseUp;
+ public event EventHandler? CalendarDayButtonMouseUp;
///
/// Provides class handling for the MouseLeftButtonDown event that
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index e830717b95..616b9083ff 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -33,10 +33,10 @@ namespace Avalonia.Controls.Primitives
private const string PART_ElementMonthView = "MonthView";
private const string PART_ElementYearView = "YearView";
- private Button _headerButton;
- private Button _nextButton;
- private Button _previousButton;
- private ITemplate _dayTitleTemplate;
+ private Button? _headerButton;
+ private Button? _nextButton;
+ private Button? _previousButton;
+ private ITemplate? _dayTitleTemplate;
private DateTime _currentMonth;
private bool _isMouseLeftButtonDown = false;
@@ -45,11 +45,11 @@ namespace Avalonia.Controls.Primitives
private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
- private PointerPressedEventArgs _downEventArg;
- private PointerPressedEventArgs _downEventArgYearView;
+ private PointerPressedEventArgs? _downEventArg;
+ private PointerPressedEventArgs? _downEventArgYearView;
- internal Calendar Owner { get; set; }
- internal CalendarDayButton CurrentButton { get; set; }
+ internal Calendar? Owner { get; set; }
+ internal CalendarDayButton? CurrentButton { get; set; }
public static readonly StyledProperty HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner();
public IBrush HeaderBackground
@@ -57,13 +57,13 @@ namespace Avalonia.Controls.Primitives
get { return GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); }
}
- public static readonly DirectProperty> DayTitleTemplateProperty =
- AvaloniaProperty.RegisterDirect>(
+ public static readonly DirectProperty?> DayTitleTemplateProperty =
+ AvaloniaProperty.RegisterDirect?>(
nameof(DayTitleTemplate),
o => o.DayTitleTemplate,
(o,v) => o.DayTitleTemplate = v,
defaultBindingMode: BindingMode.OneTime);
- public ITemplate DayTitleTemplate
+ public ITemplate? DayTitleTemplate
{
get { return _dayTitleTemplate; }
set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); }
@@ -73,7 +73,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that allows switching between month mode, year mode,
/// and decade mode.
///
- internal Button HeaderButton
+ internal Button? HeaderButton
{
get { return _headerButton; }
private set
@@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the next page of the calendar when it
/// is clicked.
///
- internal Button NextButton
+ internal Button? NextButton
{
get { return _nextButton; }
private set
@@ -125,7 +125,7 @@ namespace Avalonia.Controls.Primitives
/// Gets the button that displays the previous page of the calendar when
/// it is clicked.
///
- internal Button PreviousButton
+ internal Button? PreviousButton
{
get { return _previousButton; }
private set
@@ -156,11 +156,11 @@ namespace Avalonia.Controls.Primitives
///
/// Gets the Grid that hosts the content when in month mode.
///
- internal Grid MonthView { get; set; }
+ internal Grid? MonthView { get; set; }
///
/// Gets the Grid that hosts the content when in year or decade mode.
///
- internal Grid YearView { get; set; }
+ internal Grid? YearView { get; set; }
private void PopulateGrids()
{
@@ -294,7 +294,7 @@ namespace Avalonia.Controls.Primitives
{
for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
{
- var daytitle = MonthView.Children[childIndex];
+ var daytitle = MonthView!.Children[childIndex];
if (daytitle != null)
{
if (Owner != null)
@@ -495,8 +495,7 @@ namespace Avalonia.Controls.Primitives
for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
{
- CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
- Contract.Requires(childButton != null);
+ CalendarDayButton childButton = (CalendarDayButton)MonthView!.Children[childIndex];
childButton.Index = childIndex;
SetButtonState(childButton, dateToAdd);
@@ -532,8 +531,7 @@ namespace Avalonia.Controls.Primitives
childIndex++;
for (int i = childIndex; i < count; i++)
{
- childButton = MonthView.Children[i] as CalendarDayButton;
- Contract.Requires(childButton != null);
+ childButton = (CalendarDayButton)MonthView.Children[i];
// button needs a content to occupy the necessary space
// for the content presenter
childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
@@ -626,10 +624,9 @@ namespace Avalonia.Controls.Primitives
private void SetMonthButtonsForYearMode()
{
int count = 0;
- foreach (object child in YearView.Children)
+ foreach (object child in YearView!.Children)
{
- CalendarButton childButton = child as CalendarButton;
- Contract.Requires(childButton != null);
+ CalendarButton childButton = (CalendarButton)child;
// There should be no time component. Time is 12:00 AM
DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
childButton.DataContext = day;
@@ -703,7 +700,7 @@ namespace Avalonia.Controls.Primitives
{
if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
{
- Owner.FocusCalendarButton.IsCalendarButtonFocused = false;
+ Owner.FocusCalendarButton!.IsCalendarButtonFocused = false;
Owner.FocusCalendarButton = calendarButton;
calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
@@ -722,10 +719,9 @@ namespace Avalonia.Controls.Primitives
{
int year;
int count = -1;
- foreach (object child in YearView.Children)
+ foreach (object child in YearView!.Children)
{
- CalendarButton childButton = child as CalendarButton;
- Contract.Requires(childButton != null);
+ CalendarButton childButton = (CalendarButton)child;
year = decade + count;
if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
@@ -797,7 +793,7 @@ namespace Avalonia.Controls.Primitives
}
}
- internal void HeaderButton_Click(object sender, RoutedEventArgs e)
+ internal void HeaderButton_Click(object? sender, RoutedEventArgs e)
{
if (Owner != null)
{
@@ -805,7 +801,7 @@ namespace Avalonia.Controls.Primitives
{
Owner.Focus();
}
- Button b = (Button)sender;
+ Button b = (Button)sender!;
DateTime d;
if (b.IsEnabled)
@@ -833,7 +829,7 @@ namespace Avalonia.Controls.Primitives
}
}
}
- internal void PreviousButton_Click(object sender, RoutedEventArgs e)
+ internal void PreviousButton_Click(object? sender, RoutedEventArgs e)
{
if (Owner != null)
{
@@ -842,14 +838,14 @@ namespace Avalonia.Controls.Primitives
Owner.Focus();
}
- Button b = (Button)sender;
+ Button b = (Button)sender!;
if (b.IsEnabled)
{
Owner.OnPreviousClick();
}
}
}
- internal void NextButton_Click(object sender, RoutedEventArgs e)
+ internal void NextButton_Click(object? sender, RoutedEventArgs e)
{
if (Owner != null)
{
@@ -857,7 +853,7 @@ namespace Avalonia.Controls.Primitives
{
Owner.Focus();
}
- Button b = (Button)sender;
+ Button b = (Button)sender!;
if (b.IsEnabled)
{
@@ -866,7 +862,7 @@ namespace Avalonia.Controls.Primitives
}
}
- internal void Cell_MouseEnter(object sender, PointerEventArgs e)
+ internal void Cell_MouseEnter(object? sender, PointerEventArgs e)
{
if (Owner != null)
{
@@ -878,7 +874,7 @@ namespace Avalonia.Controls.Primitives
{
case CalendarSelectionMode.SingleDate:
{
- DateTime selectedDate = (DateTime)b.DataContext;
+ DateTime selectedDate = (DateTime)b.DataContext!;
Owner.CalendarDatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0)
{
@@ -906,7 +902,7 @@ namespace Avalonia.Controls.Primitives
}
}
- internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e)
+ internal void Cell_MouseLeftButtonDown(object? sender, PointerPressedEventArgs e)
{
if (Owner != null)
{
@@ -917,15 +913,14 @@ namespace Avalonia.Controls.Primitives
bool ctrl, shift;
CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out ctrl, out shift);
- CalendarDayButton b = sender as CalendarDayButton;
+ CalendarDayButton b = (CalendarDayButton)sender!;
if (b != null)
{
_isControlPressed = ctrl;
if (b.IsEnabled && !b.IsBlackout)
{
- DateTime selectedDate = (DateTime)b.DataContext;
- Contract.Requires(selectedDate != null);
+ DateTime selectedDate = (DateTime)b.DataContext!;
_isMouseLeftButtonDown = true;
// null check is added for unit tests
if (e != null)
@@ -1027,7 +1022,7 @@ namespace Avalonia.Controls.Primitives
if (Owner != null)
{
Owner.HoverEndIndex = b.Index;
- Owner.HoverEnd = (DateTime)b.DataContext;
+ Owner.HoverEnd = (DateTime)b.DataContext!;
if (Owner.HoverEnd != null && Owner.HoverStart != null)
{
@@ -1041,11 +1036,11 @@ namespace Avalonia.Controls.Primitives
}
}
}
- internal void Cell_MouseLeftButtonUp(object sender, PointerReleasedEventArgs e)
+ internal void Cell_MouseLeftButtonUp(object? sender, PointerReleasedEventArgs e)
{
if (Owner != null)
{
- CalendarDayButton b = sender as CalendarDayButton;
+ CalendarDayButton? b = sender as CalendarDayButton;
if (b != null && !b.IsBlackout)
{
Owner.OnDayButtonMouseUp(e);
@@ -1094,14 +1089,13 @@ namespace Avalonia.Controls.Primitives
}
}
}
- private void Cell_Click(object sender, RoutedEventArgs e)
+ private void Cell_Click(object? sender, RoutedEventArgs e)
{
if (Owner != null)
{
if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
{
- CalendarDayButton b = sender as CalendarDayButton;
- Contract.Requires(b != null);
+ CalendarDayButton b = (CalendarDayButton)sender!;
if (b.IsSelected)
{
@@ -1118,10 +1112,9 @@ namespace Avalonia.Controls.Primitives
_isControlPressed = false;
}
- private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
+ private void Month_CalendarButtonMouseDown(object? sender, PointerPressedEventArgs e)
{
- CalendarButton b = sender as CalendarButton;
- Contract.Requires(b != null);
+ CalendarButton b = (CalendarButton)sender!;
_isMouseLeftButtonDownYearView = true;
@@ -1133,13 +1126,13 @@ namespace Avalonia.Controls.Primitives
UpdateYearViewSelection(b);
}
- internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e)
+ internal void Month_CalendarButtonMouseUp(object? sender, PointerReleasedEventArgs e)
{
_isMouseLeftButtonDownYearView = false;
if (Owner != null)
{
- DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext;
+ DateTime newmonth = (DateTime)((CalendarButton)sender!).DataContext!;
if (Owner.DisplayMode == CalendarMode.Year)
{
@@ -1155,12 +1148,11 @@ namespace Avalonia.Controls.Primitives
}
}
- private void Month_MouseEnter(object sender, PointerEventArgs e)
+ private void Month_MouseEnter(object? sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDownYearView)
{
- CalendarButton b = sender as CalendarButton;
- Contract.Requires(b != null);
+ CalendarButton b = (CalendarButton)sender!;
UpdateYearViewSelection(b);
}
}
diff --git a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
index 033bfc5726..eb90f6c399 100644
--- a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
+++ b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
@@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace Avalonia.Controls
@@ -73,6 +74,7 @@ namespace Avalonia.Controls
return newD;
}
+ [return: NotNullIfNotNull("d")]
public static DateTime? DiscardTime(DateTime? d)
{
if (d == null)
diff --git a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
index 3573c67057..c7c35718e0 100644
--- a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
+++ b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives
private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems)
{
- _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems));
+ _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
}
///
@@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives
}
}
- _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _owner.RemovedItems, _addedItems));
+ _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, _owner.RemovedItems, _addedItems));
_owner.RemovedItems.Clear();
_owner.UpdateMonths();
_isRangeAdded = false;
diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs
index 5cc823a6af..fabf8978c7 100644
--- a/src/Avalonia.Controls/Canvas.cs
+++ b/src/Avalonia.Controls/Canvas.cs
@@ -135,7 +135,7 @@ namespace Avalonia.Controls
/// The control from which movement begins.
/// Whether to wrap around when the first or last item is reached.
/// The control.
- IInputElement INavigableContainer.GetControl(NavigationDirection direction, IInputElement from, bool wrap)
+ IInputElement? INavigableContainer.GetControl(NavigationDirection direction, IInputElement? from, bool wrap)
{
// TODO: Implement this
return null;
diff --git a/src/Avalonia.Controls/CheckBox.cs b/src/Avalonia.Controls/CheckBox.cs
index 05d49a44b1..238a21393f 100644
--- a/src/Avalonia.Controls/CheckBox.cs
+++ b/src/Avalonia.Controls/CheckBox.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
@@ -7,5 +9,9 @@ namespace Avalonia.Controls
///
public class CheckBox : ToggleButton
{
+ static CheckBox()
+ {
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.CheckBox);
+ }
}
}
diff --git a/src/Avalonia.Controls/Chrome/CaptionButtons.cs b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
index be15d3d444..1cad1a4c69 100644
--- a/src/Avalonia.Controls/Chrome/CaptionButtons.cs
+++ b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
@@ -3,8 +3,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
-#nullable enable
-
namespace Avalonia.Controls.Chrome
{
///
diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs
index fbddb06952..4da50e7220 100644
--- a/src/Avalonia.Controls/Chrome/TitleBar.cs
+++ b/src/Avalonia.Controls/Chrome/TitleBar.cs
@@ -3,8 +3,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
-#nullable enable
-
namespace Avalonia.Controls.Chrome
{
///
@@ -36,7 +34,7 @@ namespace Avalonia.Controls.Chrome
}
}
- IsVisible = window.PlatformImpl.NeedsManagedDecorations;
+ IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false;
}
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 274696d501..c5410ae9b0 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using Avalonia.Automation.Peers;
using System.Reactive.Disposables;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Mixins;
@@ -10,9 +11,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
-using Avalonia.LogicalTree;
using Avalonia.Media;
-using Avalonia.Threading;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@@ -46,8 +45,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly DirectProperty SelectionBoxItemProperty =
- AvaloniaProperty.RegisterDirect(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
+ public static readonly DirectProperty SelectionBoxItemProperty =
+ AvaloniaProperty.RegisterDirect(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
///
/// Defines the property.
@@ -58,14 +57,14 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty PlaceholderTextProperty =
- AvaloniaProperty.Register(nameof(PlaceholderText));
+ public static readonly StyledProperty PlaceholderTextProperty =
+ AvaloniaProperty.Register(nameof(PlaceholderText));
///
/// Defines the property.
///
- public static readonly StyledProperty PlaceholderForegroundProperty =
- AvaloniaProperty.Register(nameof(PlaceholderForeground));
+ public static readonly StyledProperty PlaceholderForegroundProperty =
+ AvaloniaProperty.Register(nameof(PlaceholderForeground));
///
/// Defines the property.
@@ -80,8 +79,8 @@ namespace Avalonia.Controls
ContentControl.VerticalContentAlignmentProperty.AddOwner();
private bool _isDropDownOpen;
- private Popup _popup;
- private object _selectionBoxItem;
+ private Popup? _popup;
+ private object? _selectionBoxItem;
private readonly CompositeDisposable _subscriptionsOnOpen = new CompositeDisposable();
///
@@ -91,7 +90,7 @@ namespace Avalonia.Controls
{
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
FocusableProperty.OverrideDefaultValue(true);
- SelectedItemProperty.Changed.AddClassHandler((x,e) => x.SelectedItemChanged(e));
+ SelectedItemProperty.Changed.AddClassHandler((x, e) => x.SelectedItemChanged(e));
KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
IsTextSearchEnabledProperty.OverrideDefaultValue(true);
}
@@ -117,7 +116,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the item to display as the control's content.
///
- protected object SelectionBoxItem
+ protected object? SelectionBoxItem
{
get { return _selectionBoxItem; }
set { SetAndRaise(SelectionBoxItemProperty, ref _selectionBoxItem, value); }
@@ -126,7 +125,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the PlaceHolder text.
///
- public string PlaceholderText
+ public string? PlaceholderText
{
get { return GetValue(PlaceholderTextProperty); }
set { SetValue(PlaceholderTextProperty, value); }
@@ -135,7 +134,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the Brush that renders the placeholder text.
///
- public IBrush PlaceholderForeground
+ public IBrush? PlaceholderForeground
{
get { return GetValue(PlaceholderForegroundProperty); }
set { SetValue(PlaceholderForegroundProperty, value); }
@@ -221,8 +220,9 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
+ // This part of code is needed just to acquire initial focus, subsequent focus navigation will be done by ItemsControl.
else if (IsDropDownOpen && SelectedIndex < 0 && ItemCount > 0 &&
- (e.Key == Key.Up || e.Key == Key.Down))
+ (e.Key == Key.Up || e.Key == Key.Down) && IsFocused == true)
{
var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
if (firstChild != null)
@@ -262,9 +262,9 @@ namespace Avalonia.Controls
///
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
- if (!e.Handled)
+ if (!e.Handled && e.Source is IVisual source)
{
- if (_popup?.IsInsidePopup((IVisual)e.Source) == true)
+ if (_popup?.IsInsidePopup(source) == true)
{
if (UpdateSelectionFromEventSource(e.Source))
{
@@ -296,6 +296,11 @@ namespace Avalonia.Controls
_popup.Closed += PopupClosed;
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ComboBoxAutomationPeer(this);
+ }
+
internal void ItemFocused(ComboBoxItem dropDownItem)
{
if (IsDropDownOpen && dropDownItem.IsFocused && dropDownItem.IsArrangeValid)
@@ -304,7 +309,7 @@ namespace Avalonia.Controls
}
}
- private void PopupClosed(object sender, EventArgs e)
+ private void PopupClosed(object? sender, EventArgs e)
{
_subscriptionsOnOpen.Clear();
@@ -314,7 +319,7 @@ namespace Avalonia.Controls
}
}
- private void PopupOpened(object sender, EventArgs e)
+ private void PopupOpened(object? sender, EventArgs e)
{
TryFocusSelectedItem();
@@ -326,7 +331,7 @@ namespace Avalonia.Controls
toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) =>
{
//eat wheel scroll event outside dropdown popup while it's open
- if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel)
+ if (IsDropDownOpen && (ev.Source as IVisual)?.GetVisualRoot() == toplevel)
{
ev.Handled = true;
}
@@ -377,7 +382,7 @@ namespace Avalonia.Controls
private bool CanFocus(IControl control) => control.Focusable && control.IsEffectivelyEnabled && control.IsVisible;
- private void UpdateSelectionBoxItem(object item)
+ private void UpdateSelectionBoxItem(object? item)
{
var contentControl = item as IContentControl;
@@ -430,7 +435,18 @@ namespace Avalonia.Controls
int next = SelectedIndex + 1;
if (next >= ItemCount)
- next = 0;
+ {
+ if (WrapSelection == true)
+ {
+ next = 0;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+
SelectedIndex = next;
}
@@ -440,7 +456,16 @@ namespace Avalonia.Controls
int prev = SelectedIndex - 1;
if (prev < 0)
- prev = ItemCount - 1;
+ {
+ if (WrapSelection == true)
+ {
+ prev = ItemCount - 1;
+ }
+ else
+ {
+ return;
+ }
+ }
SelectedIndex = prev;
}
diff --git a/src/Avalonia.Controls/ComboBoxItem.cs b/src/Avalonia.Controls/ComboBoxItem.cs
index a0a1f2a4aa..83057d139f 100644
--- a/src/Avalonia.Controls/ComboBoxItem.cs
+++ b/src/Avalonia.Controls/ComboBoxItem.cs
@@ -1,5 +1,7 @@
using System;
using System.Reactive.Linq;
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
namespace Avalonia.Controls
{
@@ -13,5 +15,10 @@ namespace Avalonia.Controls
this.GetObservable(ComboBoxItem.IsFocusedProperty).Where(focused => focused)
.Subscribe(_ => (Parent as ComboBox)?.ItemFocused(this));
}
+
+ static ComboBoxItem()
+ {
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.ComboBoxItem);
+ }
}
}
diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs
index 1329bd91c7..ac7d24be92 100644
--- a/src/Avalonia.Controls/ContentControl.cs
+++ b/src/Avalonia.Controls/ContentControl.cs
@@ -17,14 +17,14 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty ContentProperty =
- AvaloniaProperty.Register(nameof(Content));
+ public static readonly StyledProperty ContentProperty =
+ AvaloniaProperty.Register(nameof(Content));
///
/// Defines the property.
///
- public static readonly StyledProperty ContentTemplateProperty =
- AvaloniaProperty.Register(nameof(ContentTemplate));
+ public static readonly StyledProperty ContentTemplateProperty =
+ AvaloniaProperty.Register(nameof(ContentTemplate));
///
/// Defines the property.
@@ -48,7 +48,7 @@ namespace Avalonia.Controls
///
[Content]
[DependsOn(nameof(ContentTemplate))]
- public object Content
+ public object? Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
@@ -57,7 +57,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the data template used to display the content of the control.
///
- public IDataTemplate ContentTemplate
+ public IDataTemplate? ContentTemplate
{
get { return GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
@@ -66,7 +66,7 @@ namespace Avalonia.Controls
///
/// Gets the presenter from the control's template.
///
- public IContentPresenter Presenter
+ public IContentPresenter? Presenter
{
get;
private set;
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index d94bff37df..bc5195ff6c 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using Avalonia.Automation.Peers;
using System.Linq;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Generators;
@@ -13,8 +14,7 @@ using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling;
-
-#nullable enable
+using Avalonia.Automation;
namespace Avalonia.Controls
{
@@ -109,6 +109,8 @@ namespace Avalonia.Controls
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
PlacementModeProperty.OverrideDefaultValue(PlacementMode.Pointer);
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
+ AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue(AccessibilityView.Control);
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Menu);
}
///
@@ -349,6 +351,11 @@ namespace Avalonia.Controls
? PlacementMode.Bottom
: PlacementMode;
+ //Position of the line below is really important.
+ //All styles are being applied only when control has logical parent.
+ //Line below will add ContextMenu as child to the Popup and this will trigger styles and they would be applied.
+ //If you will move line below somewhere else it may cause that ContextMenu will behave differently from what you are expecting.
+ _popup.Child = this;
_popup.PlacementTarget = placementTarget;
_popup.HorizontalOffset = HorizontalOffset;
_popup.VerticalOffset = VerticalOffset;
@@ -357,7 +364,6 @@ namespace Avalonia.Controls
_popup.PlacementGravity = PlacementGravity;
_popup.PlacementRect = PlacementRect;
_popup.WindowManagerAddShadowHint = WindowManagerAddShadowHint;
- _popup.Child = this;
IsOpen = true;
_popup.IsOpen = true;
@@ -368,7 +374,7 @@ namespace Avalonia.Controls
});
}
- private void PopupOpened(object sender, EventArgs e)
+ private void PopupOpened(object? sender, EventArgs e)
{
_previousFocus = FocusManager.Instance?.Current;
Focus();
@@ -376,12 +382,12 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(_popup!.Host);
}
- private void PopupClosing(object sender, CancelEventArgs e)
+ private void PopupClosing(object? sender, CancelEventArgs e)
{
e.Cancel = CancelClosing();
}
- private void PopupClosed(object sender, EventArgs e)
+ private void PopupClosed(object? sender, EventArgs e)
{
foreach (var i in LogicalChildren)
{
@@ -411,7 +417,7 @@ namespace Avalonia.Controls
_popupHostChangedHandler?.Invoke(null);
}
- private void PopupKeyUp(object sender, KeyEventArgs e)
+ private void PopupKeyUp(object? sender, KeyEventArgs e)
{
if (IsOpen)
{
@@ -426,7 +432,7 @@ namespace Avalonia.Controls
}
}
- private static void ControlContextRequested(object sender, ContextRequestedEventArgs e)
+ private static void ControlContextRequested(object? sender, ContextRequestedEventArgs e)
{
if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu
@@ -439,7 +445,7 @@ namespace Avalonia.Controls
}
}
- private static void ControlDetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e)
+ private static void ControlDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu)
diff --git a/src/Avalonia.Controls/ContextRequestedEventArgs.cs b/src/Avalonia.Controls/ContextRequestedEventArgs.cs
index 8dc798245c..ad5ffef267 100644
--- a/src/Avalonia.Controls/ContextRequestedEventArgs.cs
+++ b/src/Avalonia.Controls/ContextRequestedEventArgs.cs
@@ -1,8 +1,6 @@
using Avalonia.Input;
using Avalonia.Interactivity;
-#nullable enable
-
namespace Avalonia.Controls
{
///
diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs
index 441421181c..cec662aad8 100644
--- a/src/Avalonia.Controls/Control.cs
+++ b/src/Avalonia.Controls/Control.cs
@@ -1,16 +1,17 @@
using System;
using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
+using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls
{
///
@@ -60,9 +61,16 @@ namespace Avalonia.Controls
public static readonly RoutedEvent ContextRequestedEvent =
RoutedEvent.Register(nameof(ContextRequested),
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
-
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FlowDirectionProperty =
+ AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true);
+
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
+ private AutomationPeer? _automationPeer;
///
/// Gets or sets the control's focus adorner.
@@ -108,11 +116,20 @@ namespace Avalonia.Controls
get => GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
+
+ ///
+ /// Gets or sets the text flow direction.
+ ///
+ public FlowDirection FlowDirection
+ {
+ get => GetValue(FlowDirectionProperty);
+ set => SetValue(FlowDirectionProperty, value);
+ }
///
/// Occurs when the user has completed a context input gesture, such as a right-click.
///
- public event EventHandler ContextRequested
+ public event EventHandler? ContextRequested
{
add => AddHandler(ContextRequestedEvent, value);
remove => RemoveHandler(ContextRequestedEvent, value);
@@ -227,6 +244,24 @@ namespace Avalonia.Controls
}
}
+ protected virtual AutomationPeer OnCreateAutomationPeer()
+ {
+ return new NoneAutomationPeer(this);
+ }
+
+ internal AutomationPeer GetOrCreateAutomationPeer()
+ {
+ VerifyAccess();
+
+ if (_automationPeer is object)
+ {
+ return _automationPeer;
+ }
+
+ _automationPeer = OnCreateAutomationPeer();
+ return _automationPeer;
+ }
+
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
diff --git a/src/Avalonia.Controls/ControlExtensions.cs b/src/Avalonia.Controls/ControlExtensions.cs
index c3cab0a729..1718679294 100644
--- a/src/Avalonia.Controls/ControlExtensions.cs
+++ b/src/Avalonia.Controls/ControlExtensions.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls
/// The control.
public static void BringIntoView(this IControl control)
{
- Contract.Requires(control != null);
+ _ = control ?? throw new ArgumentNullException(nameof(control));
control.BringIntoView(new Rect(control.Bounds.Size));
}
@@ -29,7 +29,7 @@ namespace Avalonia.Controls
/// The area of the control to being into view.
public static void BringIntoView(this IControl control, Rect rect)
{
- Contract.Requires(control != null);
+ _ = control ?? throw new ArgumentNullException(nameof(control));
if (control.IsEffectivelyVisible)
{
@@ -51,10 +51,10 @@ namespace Avalonia.Controls
/// The control to look in.
/// The name of the control to find.
/// The control or null if not found.
- public static T FindControl(this IControl control, string name) where T : class, IControl
+ public static T? FindControl(this IControl control, string name) where T : class, IControl
{
- Contract.Requires(control != null);
- Contract.Requires(name != null);
+ _ = control ?? throw new ArgumentNullException(nameof(control));
+ _ = name ?? throw new ArgumentNullException(nameof(name));
var nameScope = control.FindNameScope();
@@ -66,6 +66,29 @@ namespace Avalonia.Controls
return nameScope.Find(name);
}
+ ///
+ /// Finds the named control in the scope of the specified control and throws if not found.
+ ///
+ /// The type of the control to find.
+ /// The control to look in.
+ /// The name of the control to find.
+ /// The control.
+ public static T GetControl(this IControl control, string name) where T : class, IControl
+ {
+ _ = control ?? throw new ArgumentNullException(nameof(control));
+ _ = name ?? throw new ArgumentNullException(nameof(name));
+
+ var nameScope = control.FindNameScope();
+
+ if (nameScope == null)
+ {
+ throw new InvalidOperationException("Could not find parent name scope.");
+ }
+
+ return nameScope.Find(name) ??
+ throw new ArgumentException($"Could not find control named '{name}'.");
+ }
+
///
/// Sets a pseudoclass depending on an observable trigger.
///
@@ -75,7 +98,7 @@ namespace Avalonia.Controls
/// A disposable used to cancel the subscription.
public static IDisposable Set(this IPseudoClasses classes, string name, IObservable trigger)
{
- Contract.Requires(classes != null);
+ _ = classes ?? throw new ArgumentNullException(nameof(classes));
return trigger.Subscribe(x => classes.Set(name, x));
}
diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
index 7587a9dacb..643c30178e 100644
--- a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
+++ b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs
@@ -1,5 +1,4 @@
-#nullable enable
-using System;
+using System;
using System.Globalization;
using Avalonia.Data.Converters;
diff --git a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
index 46affcbe33..b0c30ea11f 100644
--- a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
+++ b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Controls.Converters
public bool Bottom { get; set; } = false;
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is int scalarDepth)
{
@@ -38,7 +38,7 @@ namespace Avalonia.Controls.Converters
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
diff --git a/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs b/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
index 65f95808ff..6ab2f4c517 100644
--- a/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
+++ b/src/Avalonia.Controls/Converters/MenuScrollingVisibilityConverter.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Controls.Converters
{
public static readonly MenuScrollingVisibilityConverter Instance = new MenuScrollingVisibilityConverter();
- public object Convert(IList values, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture)
{
if (parameter == null ||
values == null ||
diff --git a/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs b/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
index 2af8dba479..9a657cce68 100644
--- a/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
+++ b/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls.Converters
///
public class PlatformKeyGestureConverter : IValueConverter
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
{
@@ -29,7 +29,7 @@ namespace Avalonia.Controls.Converters
}
}
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
diff --git a/src/Avalonia.Controls/DataValidationErrors.cs b/src/Avalonia.Controls/DataValidationErrors.cs
index 3c64691816..00a06101c5 100644
--- a/src/Avalonia.Controls/DataValidationErrors.cs
+++ b/src/Avalonia.Controls/DataValidationErrors.cs
@@ -21,8 +21,8 @@ namespace Avalonia.Controls
///
/// Defines the DataValidationErrors.Errors attached property.
///
- public static readonly AttachedProperty> ErrorsProperty =
- AvaloniaProperty.RegisterAttached>("Errors");
+ public static readonly AttachedProperty?> ErrorsProperty =
+ AvaloniaProperty.RegisterAttached?>("Errors");
///
/// Defines the DataValidationErrors.HasErrors attached property.
@@ -34,15 +34,15 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(ErrorTemplate));
- private Control _owner;
+ private Control? _owner;
- public static readonly DirectProperty OwnerProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty OwnerProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(Owner),
o => o.Owner,
(o, v) => o.Owner = v);
- public Control Owner
+ public Control? Owner
{
get { return _owner; }
set { SetAndRaise(OwnerProperty, ref _owner, value); }
@@ -75,7 +75,7 @@ namespace Avalonia.Controls
private static void ErrorsChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
- var errors = (IEnumerable)e.NewValue;
+ var errors = (IEnumerable?)e.NewValue;
var hasErrors = false;
if (errors != null && errors.Any())
@@ -87,18 +87,18 @@ namespace Avalonia.Controls
{
var control = (Control)e.Sender;
var classes = (IPseudoClasses)control.Classes;
- classes.Set(":error", (bool)e.NewValue);
+ classes.Set(":error", (bool)e.NewValue!);
}
- public static IEnumerable GetErrors(Control control)
+ public static IEnumerable? GetErrors(Control control)
{
return control.GetValue(ErrorsProperty);
}
- public static void SetErrors(Control control, IEnumerable errors)
+ public static void SetErrors(Control control, IEnumerable? errors)
{
control.SetValue(ErrorsProperty, errors);
}
- public static void SetError(Control control, Exception error)
+ public static void SetError(Control control, Exception? error)
{
SetErrors(control, UnpackException(error));
}
@@ -111,7 +111,7 @@ namespace Avalonia.Controls
return control.GetValue(HasErrorsProperty);
}
- private static IEnumerable UnpackException(Exception exception)
+ private static IEnumerable? UnpackException(Exception? exception)
{
if (exception != null)
{
@@ -132,8 +132,9 @@ namespace Avalonia.Controls
private static object GetExceptionData(Exception exception)
{
- if (exception is DataValidationException dataValidationException)
- return dataValidationException.ErrorData;
+ if (exception is DataValidationException dataValidationException &&
+ dataValidationException.ErrorData is object data)
+ return data;
return exception;
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index 5893a02b04..6c74eb6b91 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
+using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -93,15 +94,15 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay);
// Template Items
- private Button _flyoutButton;
- private TextBlock _dayText;
- private TextBlock _monthText;
- private TextBlock _yearText;
- private Grid _container;
- private Rectangle _spacer1;
- private Rectangle _spacer2;
- private Popup _popup;
- private DatePickerPresenter _presenter;
+ private Button? _flyoutButton;
+ private TextBlock? _dayText;
+ private TextBlock? _monthText;
+ private TextBlock? _yearText;
+ private Grid? _container;
+ private Rectangle? _spacer1;
+ private Rectangle? _spacer2;
+ private Popup? _popup;
+ private DatePickerPresenter? _presenter;
private bool _areControlsAvailable;
@@ -256,7 +257,7 @@ namespace Avalonia.Controls
///
/// Raised when the changes
///
- public event EventHandler SelectedDateChanged;
+ public event EventHandler? SelectedDateChanged;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
@@ -307,16 +308,16 @@ namespace Avalonia.Controls
}
}
- private void OnDismissPicker(object sender, EventArgs e)
+ private void OnDismissPicker(object? sender, EventArgs e)
{
- _popup.Close();
+ _popup!.Close();
Focus();
}
- private void OnConfirmed(object sender, EventArgs e)
+ private void OnConfirmed(object? sender, EventArgs e)
{
- _popup.Close();
- SelectedDate = _presenter.Date;
+ _popup!.Close();
+ SelectedDate = _presenter!.Date;
}
private void SetGrid()
@@ -325,7 +326,7 @@ namespace Avalonia.Controls
return;
var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
- var columns = new List<(TextBlock, int)>
+ var columns = new List<(TextBlock?, int)>
{
(_monthText, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1),
(_yearText, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@@ -366,11 +367,11 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
- Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
- Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+ Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
- _spacer1.IsVisible = isSpacer1Visible;
- _spacer2.IsVisible = isSpacer2Visible;
+ _spacer1!.IsVisible = isSpacer1Visible;
+ _spacer2!.IsVisible = isSpacer2Visible;
}
private void SetSelectedDateText()
@@ -382,37 +383,46 @@ namespace Avalonia.Controls
{
PseudoClasses.Set(":hasnodate", false);
var selDate = SelectedDate.Value;
- _monthText.Text = selDate.ToString(MonthFormat);
- _yearText.Text = selDate.ToString(YearFormat);
- _dayText.Text = selDate.ToString(DayFormat);
+ _monthText!.Text = selDate.ToString(MonthFormat);
+ _yearText!.Text = selDate.ToString(YearFormat);
+ _dayText!.Text = selDate.ToString(DayFormat);
}
else
{
PseudoClasses.Set(":hasnodate", true);
- _monthText.Text = "month";
- _yearText.Text = "year";
- _dayText.Text = "day";
+ _monthText!.Text = "month";
+ _yearText!.Text = "year";
+ _dayText!.Text = "day";
}
}
- private void OnFlyoutButtonClicked(object sender, RoutedEventArgs e)
+ private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{
if (_presenter == null)
- throw new InvalidOperationException("No DatePickerPresenter found");
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now;
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
+
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
- protected virtual void OnSelectedDateChanged(object sender, DatePickerSelectedValueChangedEventArgs e)
+ protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs e)
{
SelectedDateChanged?.Invoke(sender, e);
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index eec4615736..0caba64758 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
@@ -78,23 +78,23 @@ namespace Avalonia.Controls
x.YearVisible, (x, v) => x.YearVisible = v);
// Template Items
- private Grid _pickerContainer;
- private Button _acceptButton;
- private Button _dismissButton;
- private Rectangle _spacer1;
- private Rectangle _spacer2;
- private Panel _monthHost;
- private Panel _yearHost;
- private Panel _dayHost;
- private DateTimePickerPanel _monthSelector;
- private DateTimePickerPanel _yearSelector;
- private DateTimePickerPanel _daySelector;
- private Button _monthUpButton;
- private Button _dayUpButton;
- private Button _yearUpButton;
- private Button _monthDownButton;
- private Button _dayDownButton;
- private Button _yearDownButton;
+ private Grid? _pickerContainer;
+ private Button? _acceptButton;
+ private Button? _dismissButton;
+ private Rectangle? _spacer1;
+ private Rectangle? _spacer2;
+ private Panel? _monthHost;
+ private Panel? _yearHost;
+ private Panel? _dayHost;
+ private DateTimePickerPanel? _monthSelector;
+ private DateTimePickerPanel? _yearSelector;
+ private DateTimePickerPanel? _daySelector;
+ private Button? _monthUpButton;
+ private Button? _dayUpButton;
+ private Button? _yearUpButton;
+ private Button? _monthDownButton;
+ private Button? _dayDownButton;
+ private Button? _yearDownButton;
private DateTimeOffset _date;
private string _dayFormat = "%d";
@@ -308,9 +308,12 @@ namespace Avalonia.Controls
e.Handled = true;
break;
case Key.Tab:
- var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next);
- KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
- e.Handled = true;
+ if (FocusManager.Instance?.Current is IInputElement focus)
+ {
+ var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
+ KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
+ e.Handled = true;
+ }
break;
case Key.Enter:
Date = _syncDate;
@@ -332,13 +335,13 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
- _monthSelector.MaximumValue = 12;
+ _monthSelector!.MaximumValue = 12;
_monthSelector.MinimumValue = 1;
_monthSelector.ItemFormat = MonthFormat;
- _daySelector.ItemFormat = DayFormat;
+ _daySelector!.ItemFormat = DayFormat;
- _yearSelector.MaximumValue = MaxYear.Year;
+ _yearSelector!.MaximumValue = MaxYear.Year;
_yearSelector.MinimumValue = MinYear.Year;
_yearSelector.ItemFormat = YearFormat;
@@ -375,7 +378,7 @@ namespace Avalonia.Controls
private void SetGrid()
{
var fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
- var columns = new List<(Panel, int)>
+ var columns = new List<(Panel?, int)>
{
(_monthHost, MonthVisible ? fmt.IndexOf("m", StringComparison.OrdinalIgnoreCase) : -1),
(_yearHost, YearVisible ? fmt.IndexOf("y", StringComparison.OrdinalIgnoreCase) : -1),
@@ -383,7 +386,7 @@ namespace Avalonia.Controls
};
columns.Sort((x, y) => x.Item2 - y.Item2);
- _pickerContainer.ColumnDefinitions.Clear();
+ _pickerContainer!.ColumnDefinitions.Clear();
var columnIndex = 0;
@@ -416,18 +419,18 @@ namespace Avalonia.Controls
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
- Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
- Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+ Grid.SetColumn(_spacer1!, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2!, isSpacer2Visible ? 3 : 0);
- _spacer1.IsVisible = isSpacer1Visible;
- _spacer2.IsVisible = isSpacer2Visible;
+ _spacer1!.IsVisible = isSpacer1Visible;
+ _spacer2!.IsVisible = isSpacer2Visible;
}
private void SetInitialFocus()
{
- int monthCol = MonthVisible ? Grid.GetColumn(_monthHost) : int.MaxValue;
- int dayCol = DayVisible ? Grid.GetColumn(_dayHost) : int.MaxValue;
- int yearCol = YearVisible ? Grid.GetColumn(_yearHost) : int.MaxValue;
+ int monthCol = MonthVisible ? Grid.GetColumn(_monthHost!) : int.MaxValue;
+ int dayCol = DayVisible ? Grid.GetColumn(_dayHost!) : int.MaxValue;
+ int yearCol = YearVisible ? Grid.GetColumn(_yearHost!) : int.MaxValue;
if (monthCol < dayCol && monthCol < yearCol)
{
@@ -443,39 +446,39 @@ namespace Avalonia.Controls
}
}
- private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
+ private void OnDismissButtonClicked(object? sender, RoutedEventArgs e)
{
OnDismiss();
}
- private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
+ private void OnAcceptButtonClicked(object? sender, RoutedEventArgs e)
{
Date = _syncDate;
OnConfirmed();
}
- private void OnSelectorButtonClick(object sender, RoutedEventArgs e)
+ private void OnSelectorButtonClick(object? sender, RoutedEventArgs e)
{
if (sender == _monthUpButton)
- _monthSelector.ScrollUp();
+ _monthSelector!.ScrollUp();
else if (sender == _monthDownButton)
- _monthSelector.ScrollDown();
+ _monthSelector!.ScrollDown();
else if (sender == _yearUpButton)
- _yearSelector.ScrollUp();
+ _yearSelector!.ScrollUp();
else if (sender == _yearDownButton)
- _yearSelector.ScrollDown();
+ _yearSelector!.ScrollDown();
else if (sender == _dayUpButton)
- _daySelector.ScrollUp();
+ _daySelector!.ScrollUp();
else if (sender == _dayDownButton)
- _daySelector.ScrollDown();
+ _daySelector!.ScrollDown();
}
- private void OnYearChanged(object sender, EventArgs e)
+ private void OnYearChanged(object? sender, EventArgs e)
{
if (_suppressUpdateSelection)
return;
- int maxDays = _calendar.GetDaysInMonth(_yearSelector.SelectedValue, _syncDate.Month);
+ int maxDays = _calendar.GetDaysInMonth(_yearSelector!.SelectedValue, _syncDate.Month);
var newDate = new DateTimeOffset(_yearSelector.SelectedValue, _syncDate.Month,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@@ -487,7 +490,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
- _daySelector.FormatDate = newDate.Date;
+ _daySelector!.FormatDate = newDate.Date;
if (_daySelector.MaximumValue != maxDays)
_daySelector.MaximumValue = maxDays;
@@ -497,19 +500,19 @@ namespace Avalonia.Controls
_suppressUpdateSelection = false;
}
- private void OnDayChanged(object sender, EventArgs e)
+ private void OnDayChanged(object? sender, EventArgs e)
{
if (_suppressUpdateSelection)
return;
- _syncDate = new DateTimeOffset(_syncDate.Year, _syncDate.Month, _daySelector.SelectedValue, 0, 0, 0, _syncDate.Offset);
+ _syncDate = new DateTimeOffset(_syncDate.Year, _syncDate.Month, _daySelector!.SelectedValue, 0, 0, 0, _syncDate.Offset);
}
- private void OnMonthChanged(object sender, EventArgs e)
+ private void OnMonthChanged(object? sender, EventArgs e)
{
if (_suppressUpdateSelection)
return;
- int maxDays = _calendar.GetDaysInMonth(_syncDate.Year, _monthSelector.SelectedValue);
+ int maxDays = _calendar.GetDaysInMonth(_syncDate.Year, _monthSelector!.SelectedValue);
var newDate = new DateTimeOffset(_syncDate.Year, _monthSelector.SelectedValue,
_syncDate.Day > maxDays ? maxDays : _syncDate.Day, 0, 0, 0, _syncDate.Offset);
@@ -521,7 +524,7 @@ namespace Avalonia.Controls
_suppressUpdateSelection = true;
- _daySelector.FormatDate = newDate.Date;
+ _daySelector!.FormatDate = newDate.Date;
_syncDate = newDate;
if (_daySelector.MaximumValue != maxDays)
@@ -534,6 +537,9 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
+ if (_monthSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2);
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
index e16e609a15..5948d81810 100644
--- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs
@@ -58,7 +58,7 @@ namespace Avalonia.Controls.Primitives
private Vector _offset;
private bool _hasInit;
private bool _suppressUpdateOffset;
- private ListBoxItem _pressedItem;
+ private ListBoxItem? _pressedItem;
public DateTimePickerPanel()
{
@@ -271,9 +271,9 @@ namespace Avalonia.Controls.Primitives
public Size Viewport => new Size(0, ItemHeight);
- public event EventHandler ScrollInvalidated;
+ public event EventHandler? ScrollInvalidated;
- public event EventHandler SelectionChanged;
+ public event EventHandler? SelectionChanged;
protected override Size MeasureOverride(Size availableSize)
{
@@ -523,40 +523,44 @@ namespace Avalonia.Controls.Primitives
return newValue;
}
- private void OnItemPointerDown(object sender, PointerPressedEventArgs e)
+ private void OnItemPointerDown(object? sender, PointerPressedEventArgs e)
{
- if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
+ if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
+ e.Source is IVisual source)
{
- _pressedItem = GetItemFromSource((IVisual)e.Source);
+ _pressedItem = GetItemFromSource(source);
e.Handled = true;
}
}
- private void OnItemPointerUp(object sender, PointerReleasedEventArgs e)
+ private void OnItemPointerUp(object? sender, PointerReleasedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonReleased &&
- _pressedItem != null)
+ _pressedItem != null &&
+ e.Source is IVisual source &&
+ GetItemFromSource(source) is ListBoxItem item &&
+ item.Tag is int tag)
{
- SelectedValue = (int)GetItemFromSource((IVisual)e.Source).Tag;
+ SelectedValue = tag;
_pressedItem = null;
e.Handled = true;
}
}
//Helper to get ListBoxItem from pointerevent source
- private ListBoxItem GetItemFromSource(IVisual src)
+ private ListBoxItem? GetItemFromSource(IVisual src)
{
var item = src;
while (item != null && !(item is ListBoxItem))
{
item = item.VisualParent;
}
- return (ListBoxItem)item;
+ return (ListBoxItem?)item;
}
public bool BringIntoView(IControl target, Rect targetRect) { return false; }
- public IControl GetControlInDirection(NavigationDirection direction, IControl from) { return null; }
+ public IControl? GetControlInDirection(NavigationDirection direction, IControl? from) { return null; }
public void RaiseScrollInvalidated(EventArgs e)
{
diff --git a/src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs b/src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs
index 639b187ad8..629b1a1766 100644
--- a/src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs
+++ b/src/Avalonia.Controls/DateTimePickers/PickerPresenterBase.cs
@@ -16,10 +16,10 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnDismiss()
{
- Dismissed.Invoke(this, EventArgs.Empty);
+ Dismissed?.Invoke(this, EventArgs.Empty);
}
- public event EventHandler Confirmed;
- public event EventHandler Dismissed;
+ public event EventHandler? Confirmed;
+ public event EventHandler? Dismissed;
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index 6b3f66912f..3aab49d556 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
+using Avalonia.Layout;
using System;
using System.Globalization;
@@ -49,18 +50,18 @@ namespace Avalonia.Controls
defaultBindingMode: BindingMode.TwoWay);
// Template Items
- private TimePickerPresenter _presenter;
- private Button _flyoutButton;
- private Border _firstPickerHost;
- private Border _secondPickerHost;
- private Border _thirdPickerHost;
- private TextBlock _hourText;
- private TextBlock _minuteText;
- private TextBlock _periodText;
- private Rectangle _firstSplitter;
- private Rectangle _secondSplitter;
- private Grid _contentGrid;
- private Popup _popup;
+ private TimePickerPresenter? _presenter;
+ private Button? _flyoutButton;
+ private Border? _firstPickerHost;
+ private Border? _secondPickerHost;
+ private Border? _thirdPickerHost;
+ private TextBlock? _hourText;
+ private TextBlock? _minuteText;
+ private TextBlock? _periodText;
+ private Rectangle? _firstSplitter;
+ private Rectangle? _secondSplitter;
+ private Grid? _contentGrid;
+ private Popup? _popup;
private TimeSpan? _selectedTime;
private int _minuteIncrement = 1;
@@ -142,7 +143,7 @@ namespace Avalonia.Controls
///
/// Raised when the property changes
///
- public event EventHandler SelectedTimeChanged;
+ public event EventHandler? SelectedTimeChanged;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
@@ -200,15 +201,15 @@ namespace Avalonia.Controls
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD);
- _thirdPickerHost.IsVisible = !use24HourClock;
- _secondSplitter.IsVisible = !use24HourClock;
+ _thirdPickerHost!.IsVisible = !use24HourClock;
+ _secondSplitter!.IsVisible = !use24HourClock;
- Grid.SetColumn(_firstPickerHost, 0);
- Grid.SetColumn(_secondPickerHost, 2);
+ Grid.SetColumn(_firstPickerHost!, 0);
+ Grid.SetColumn(_secondPickerHost!, 2);
Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
- Grid.SetColumn(_firstSplitter, 1);
+ Grid.SetColumn(_firstSplitter!, 1);
Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
}
@@ -220,7 +221,7 @@ namespace Avalonia.Controls
var time = SelectedTime;
if (time.HasValue)
{
- var newTime = SelectedTime.Value;
+ var newTime = SelectedTime!.Value;
if (ClockIdentifier == "12HourClock")
{
@@ -252,30 +253,42 @@ namespace Avalonia.Controls
SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime));
}
- private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e)
+ private void OnFlyoutButtonClicked(object? sender, Interactivity.RoutedEventArgs e)
{
+ if (_presenter == null)
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
+
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
+
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
- private void OnDismissPicker(object sender, EventArgs e)
+ private void OnDismissPicker(object? sender, EventArgs e)
{
- _popup.Close();
+ _popup!.Close();
Focus();
}
- private void OnConfirmed(object sender, EventArgs e)
+ private void OnConfirmed(object? sender, EventArgs e)
{
- _popup.Close();
- SelectedTime = _presenter.Time;
+ _popup!.Close();
+ SelectedTime = _presenter!.Time;
}
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 920eeeb406..30b6144388 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -40,20 +40,20 @@ namespace Avalonia.Controls
}
// TemplateItems
- private Grid _pickerContainer;
- private Button _acceptButton;
- private Button _dismissButton;
- private Rectangle _spacer2;
- private Panel _periodHost;
- private DateTimePickerPanel _hourSelector;
- private DateTimePickerPanel _minuteSelector;
- private DateTimePickerPanel _periodSelector;
- private Button _hourUpButton;
- private Button _minuteUpButton;
- private Button _periodUpButton;
- private Button _hourDownButton;
- private Button _minuteDownButton;
- private Button _periodDownButton;
+ private Grid? _pickerContainer;
+ private Button? _acceptButton;
+ private Button? _dismissButton;
+ private Rectangle? _spacer2;
+ private Panel? _periodHost;
+ private DateTimePickerPanel? _hourSelector;
+ private DateTimePickerPanel? _minuteSelector;
+ private DateTimePickerPanel? _periodSelector;
+ private Button? _hourUpButton;
+ private Button? _minuteUpButton;
+ private Button? _periodUpButton;
+ private Button? _hourDownButton;
+ private Button? _minuteDownButton;
+ private Button? _periodDownButton;
// Backing Fields
private TimeSpan _time;
@@ -156,9 +156,12 @@ namespace Avalonia.Controls
e.Handled = true;
break;
case Key.Tab:
- var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next);
- KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
- e.Handled = true;
+ if (FocusManager.Instance?.Current is IInputElement focus)
+ {
+ var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
+ KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
+ e.Handled = true;
+ }
break;
case Key.Enter:
OnConfirmed();
@@ -170,9 +173,9 @@ namespace Avalonia.Controls
protected override void OnConfirmed()
{
- var hr = _hourSelector.SelectedValue;
- var min = _minuteSelector.SelectedValue;
- var per = _periodSelector.SelectedValue;
+ var hr = _hourSelector!.SelectedValue;
+ var min = _minuteSelector!.SelectedValue;
+ var per = _periodSelector!.SelectedValue;
if (ClockIdentifier == "12HourClock")
{
@@ -190,20 +193,20 @@ namespace Avalonia.Controls
return;
bool clock12 = ClockIdentifier == "12HourClock";
- _hourSelector.MaximumValue = clock12 ? 12 : 23;
+ _hourSelector!.MaximumValue = clock12 ? 12 : 23;
_hourSelector.MinimumValue = clock12 ? 1 : 0;
_hourSelector.ItemFormat = "%h";
var hr = Time.Hours;
_hourSelector.SelectedValue = !clock12 ? hr :
hr > 12 ? hr - 12 : hr == 0 ? 12 : hr;
- _minuteSelector.MaximumValue = 59;
+ _minuteSelector!.MaximumValue = 59;
_minuteSelector.MinimumValue = 0;
_minuteSelector.Increment = MinuteIncrement;
_minuteSelector.SelectedValue = Time.Minutes;
_minuteSelector.ItemFormat = "mm";
- _periodSelector.MaximumValue = 1;
+ _periodSelector!.MaximumValue = 1;
_periodSelector.MinimumValue = 0;
_periodSelector.SelectedValue = hr >= 12 ? 1 : 0;
@@ -216,43 +219,46 @@ namespace Avalonia.Controls
bool use24HourClock = ClockIdentifier == "24HourClock";
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
- _pickerContainer.ColumnDefinitions = new ColumnDefinitions(columnsD);
+ _pickerContainer!.ColumnDefinitions = new ColumnDefinitions(columnsD);
- _spacer2.IsVisible = !use24HourClock;
- _periodHost.IsVisible = !use24HourClock;
+ _spacer2!.IsVisible = !use24HourClock;
+ _periodHost!.IsVisible = !use24HourClock;
Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3);
Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4);
}
- private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
+ private void OnDismissButtonClicked(object? sender, RoutedEventArgs e)
{
OnDismiss();
}
- private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
+ private void OnAcceptButtonClicked(object? sender, RoutedEventArgs e)
{
OnConfirmed();
}
- private void OnSelectorButtonClick(object sender, RoutedEventArgs e)
+ private void OnSelectorButtonClick(object? sender, RoutedEventArgs e)
{
if (sender == _hourUpButton)
- _hourSelector.ScrollUp();
+ _hourSelector!.ScrollUp();
else if (sender == _hourDownButton)
- _hourSelector.ScrollDown();
+ _hourSelector!.ScrollDown();
else if (sender == _minuteUpButton)
- _minuteSelector.ScrollUp();
+ _minuteSelector!.ScrollUp();
else if (sender == _minuteDownButton)
- _minuteSelector.ScrollDown();
+ _minuteSelector!.ScrollDown();
else if (sender == _periodUpButton)
- _periodSelector.ScrollUp();
+ _periodSelector!.ScrollUp();
else if (sender == _periodDownButton)
- _periodSelector.ScrollDown();
+ _periodSelector!.ScrollDown();
}
internal double GetOffsetForPopup()
{
+ if (_hourSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2);
}
diff --git a/src/Avalonia.Controls/Decorator.cs b/src/Avalonia.Controls/Decorator.cs
index 8959f0e306..bd866ee8d8 100644
--- a/src/Avalonia.Controls/Decorator.cs
+++ b/src/Avalonia.Controls/Decorator.cs
@@ -11,8 +11,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty ChildProperty =
- AvaloniaProperty.Register(nameof(Child));
+ public static readonly StyledProperty ChildProperty =
+ AvaloniaProperty.Register(nameof(Child));
///
/// Defines the property.
@@ -33,7 +33,7 @@ namespace Avalonia.Controls
/// Gets or sets the decorated control.
///
[Content]
- public IControl Child
+ public IControl? Child
{
get { return GetValue(ChildProperty); }
set { SetValue(ChildProperty, value); }
@@ -66,8 +66,8 @@ namespace Avalonia.Controls
/// The event args.
private void ChildChanged(AvaloniaPropertyChangedEventArgs e)
{
- var oldChild = (Control)e.OldValue;
- var newChild = (Control)e.NewValue;
+ var oldChild = (Control?)e.OldValue;
+ var newChild = (Control?)e.NewValue;
if (oldChild != null)
{
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index 37b8691ce9..eb09ff397a 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Controls
string sharedSizeGroupId = SharedSizeGroup;
if (sharedSizeGroupId != null)
{
- SharedSizeScope privateSharedSizeScope = PrivateSharedSizeScope;
+ SharedSizeScope? privateSharedSizeScope = PrivateSharedSizeScope;
if (privateSharedSizeScope != null)
{
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
@@ -104,7 +104,7 @@ namespace Avalonia.Controls
///
internal static void OnIsSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{
- if ((bool)e.NewValue)
+ if ((bool)e.NewValue!)
{
SharedSizeScope sharedStatesCollection = new SharedSizeScope();
d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection);
@@ -131,8 +131,8 @@ namespace Avalonia.Controls
}
else
{
- GridUnitType oldUnitType = ((GridLength)e.OldValue).GridUnitType;
- GridUnitType newUnitType = ((GridLength)e.NewValue).GridUnitType;
+ GridUnitType oldUnitType = ((GridLength)e.OldValue!).GridUnitType;
+ GridUnitType newUnitType = ((GridLength)e.NewValue!).GridUnitType;
if (oldUnitType != newUnitType)
{
@@ -300,7 +300,7 @@ namespace Avalonia.Controls
///
internal abstract double UserMaxSizeValueCache { get; }
- internal Grid Parent { get; set; }
+ internal Grid? Parent { get; set; }
///
/// SetFlags is used to set or unset one or multiple
@@ -326,7 +326,7 @@ namespace Avalonia.Controls
if (definition.Parent != null)
{
- string sharedSizeGroupId = (string)e.NewValue;
+ string sharedSizeGroupId = (string)e.NewValue!;
if (definition._sharedState != null)
{
@@ -338,7 +338,7 @@ namespace Avalonia.Controls
if ((definition._sharedState == null) && (sharedSizeGroupId != null))
{
- SharedSizeScope privateSharedSizeScope = definition.PrivateSharedSizeScope;
+ SharedSizeScope? privateSharedSizeScope = definition.PrivateSharedSizeScope;
if (privateSharedSizeScope != null)
{
// if definition is not registered and both: shared size group id AND private shared scope
@@ -402,7 +402,7 @@ namespace Avalonia.Controls
if (definition.Parent != null)
{
- SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue;
+ SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue!;
if (definition._sharedState != null)
{
@@ -429,9 +429,9 @@ namespace Avalonia.Controls
///
/// Private getter of shared state collection dynamic property.
///
- private SharedSizeScope PrivateSharedSizeScope
+ private SharedSizeScope? PrivateSharedSizeScope
{
- get { return (SharedSizeScope)GetValue(PrivateSharedSizeScopeProperty); }
+ get { return (SharedSizeScope?)GetValue(PrivateSharedSizeScopeProperty); }
}
///
@@ -462,7 +462,7 @@ namespace Avalonia.Controls
private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations
private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case)
- private SharedSizeState _sharedState; // reference to shared state object this instance is registered with
+ private SharedSizeState? _sharedState; // reference to shared state object this instance is registered with
[System.Flags]
private enum Flags : byte
@@ -488,7 +488,7 @@ namespace Avalonia.Controls
// check that sharedSizeGroup is not default
Debug.Assert(sharedSizeGroup != null);
- SharedSizeState sharedState = _registry[sharedSizeGroup] as SharedSizeState;
+ SharedSizeState? sharedState = _registry[sharedSizeGroup] as SharedSizeState;
if (sharedState == null)
{
sharedState = new SharedSizeState(this, sharedSizeGroup);
@@ -567,7 +567,7 @@ namespace Avalonia.Controls
{
for (int i = 0, count = _registry.Count; i < count; ++i)
{
- Grid parentGrid = (Grid)(_registry[i].Parent);
+ Grid parentGrid = (Grid)(_registry[i].Parent!);
parentGrid.Invalidate();
}
_broadcastInvalidation = false;
@@ -645,7 +645,7 @@ namespace Avalonia.Controls
/// OnLayoutUpdated handler. Validates that all participating definitions
/// have updated min size value. Forces another layout update cycle if needed.
///
- private void OnLayoutUpdated(object sender, EventArgs e)
+ private void OnLayoutUpdated(object? sender, EventArgs e)
{
double sharedMinSize = 0;
@@ -707,14 +707,14 @@ namespace Avalonia.Controls
if(!measureIsValid)
{
- definitionBase.Parent.InvalidateMeasure();
+ definitionBase.Parent!.InvalidateMeasure();
}
else if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
{
// if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size
- definitionBase.Parent.InvalidateArrange();
+ definitionBase.Parent!.InvalidateArrange();
}
// now we can restore the invariant, and clear the layout flag
@@ -724,7 +724,7 @@ namespace Avalonia.Controls
_minSize = sharedMinSize;
- _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
+ _layoutUpdatedHost!.LayoutUpdated -= _layoutUpdated;
_layoutUpdatedHost = null;
_broadcastInvalidation = true;
@@ -743,7 +743,7 @@ namespace Avalonia.Controls
private readonly EventHandler _layoutUpdated;
// Control for which layout updated event handler is registered
- private Control _layoutUpdatedHost;
+ private Control? _layoutUpdatedHost;
// "true" when broadcasting of invalidation is needed
private bool _broadcastInvalidation;
@@ -762,8 +762,8 @@ namespace Avalonia.Controls
/// Private shared size scope property holds a collection of shared state objects for the a given shared size scope.
///
///
- internal static readonly AttachedProperty PrivateSharedSizeScopeProperty =
- AvaloniaProperty.RegisterAttached(
+ internal static readonly AttachedProperty PrivateSharedSizeScopeProperty =
+ AvaloniaProperty.RegisterAttached(
"PrivateSharedSizeScope",
defaultValue: null,
inherits: true);
diff --git a/src/Avalonia.Controls/DefinitionList.cs b/src/Avalonia.Controls/DefinitionList.cs
index ad0e2513c4..c850647bf4 100644
--- a/src/Avalonia.Controls/DefinitionList.cs
+++ b/src/Avalonia.Controls/DefinitionList.cs
@@ -2,8 +2,6 @@ using System.Collections;
using System.Collections.Specialized;
using Avalonia.Collections;
-#nullable enable
-
namespace Avalonia.Controls
{
public abstract class DefinitionList : AvaloniaList where T : DefinitionBase
@@ -36,7 +34,7 @@ namespace Avalonia.Controls
}
}
- internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ internal void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
var idx = 0;
@@ -62,7 +60,7 @@ namespace Avalonia.Controls
for (var i = 0; i < count; i++)
{
- var definition = (DefinitionBase) items[i];
+ var definition = (DefinitionBase) items[i]!;
if (wasRemoved)
{
diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
index 11d3b1792a..45cd1d727e 100644
--- a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
+++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs
@@ -1,8 +1,6 @@
using System;
using Avalonia.Controls.Primitives;
-#nullable enable
-
namespace Avalonia.Controls.Diagnostics
{
///
diff --git a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs
index 4acf2a217f..9fddb91231 100644
--- a/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs
+++ b/src/Avalonia.Controls/Diagnostics/ToolTipDiagnostics.cs
@@ -1,6 +1,4 @@
-#nullable enable
-
-namespace Avalonia.Controls.Diagnostics
+namespace Avalonia.Controls.Diagnostics
{
///
/// Helper class to provide diagnostics information for .
diff --git a/src/Avalonia.Controls/Documents/Bold.cs b/src/Avalonia.Controls/Documents/Bold.cs
new file mode 100644
index 0000000000..7d0a9130ae
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Bold.cs
@@ -0,0 +1,17 @@
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Bold element - markup helper for indicating bolded content.
+ /// Equivalent to a Span with FontWeight property set to FontWeights.Bold.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Bold : Span
+ {
+ static Bold()
+ {
+ FontWeightProperty.OverrideDefaultValue(FontWeight.Bold);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs
new file mode 100644
index 0000000000..5b63f95432
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Inline.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Inline element.
+ ///
+ public abstract class Inline : TextElement
+ {
+ ///
+ /// AvaloniaProperty for property.
+ ///
+ public static readonly StyledProperty TextDecorationsProperty =
+ AvaloniaProperty.Register(
+ nameof(TextDecorations));
+
+ ///
+ /// AvaloniaProperty for property.
+ ///
+ public static readonly StyledProperty BaselineAlignmentProperty =
+ AvaloniaProperty.Register(
+ nameof(BaselineAlignment),
+ BaselineAlignment.Baseline);
+
+ ///
+ /// The TextDecorations property specifies decorations that are added to the text of an element.
+ ///
+ public TextDecorationCollection TextDecorations
+ {
+ get { return GetValue(TextDecorationsProperty); }
+ set { SetValue(TextDecorationsProperty, value); }
+ }
+
+ ///
+ /// Describes how the baseline for a text-based element is positioned on the vertical axis,
+ /// relative to the established baseline for text.
+ ///
+ public BaselineAlignment BaselineAlignment
+ {
+ get { return GetValue(BaselineAlignmentProperty); }
+ set { SetValue(BaselineAlignmentProperty, value); }
+ }
+
+ internal abstract int BuildRun(StringBuilder stringBuilder, IList> textStyleOverrides, int firstCharacterIndex);
+
+ internal abstract int AppendText(StringBuilder stringBuilder);
+
+ protected TextRunProperties CreateTextRunProperties()
+ {
+ return new GenericTextRunProperties(new Typeface(FontFamily, FontStyle, FontWeight), FontSize,
+ TextDecorations, Foreground, Background, BaselineAlignment);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(TextDecorations):
+ case nameof(BaselineAlignment):
+ Invalidate();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs
new file mode 100644
index 0000000000..45c715c13a
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/InlineCollection.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Text;
+using Avalonia.Collections;
+using Avalonia.LogicalTree;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// A collection of s.
+ ///
+ [WhitespaceSignificantCollection]
+ public class InlineCollection : AvaloniaList
+ {
+ private string? _text = string.Empty;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public InlineCollection(ILogical parent) : base(0)
+ {
+ ResetBehavior = ResetBehavior.Remove;
+
+ this.ForEachItem(
+ x =>
+ {
+ ((ISetLogicalParent)x).SetParent(parent);
+ x.Invalidated += Invalidate;
+ Invalidate();
+ },
+ x =>
+ {
+ ((ISetLogicalParent)x).SetParent(null);
+ x.Invalidated -= Invalidate;
+ Invalidate();
+ },
+ () => throw new NotSupportedException());
+ }
+
+ public bool HasComplexContent => Count > 0;
+
+ ///
+ /// Gets or adds the text held by the inlines collection.
+ ///
+ /// Can be null for complex content.
+ ///
+ ///
+ public string? Text
+ {
+ get
+ {
+ if (!HasComplexContent)
+ {
+ return _text;
+ }
+
+ var builder = new StringBuilder();
+
+ foreach(var inline in this)
+ {
+ inline.AppendText(builder);
+ }
+
+ return builder.ToString();
+ }
+ set
+ {
+ if (HasComplexContent)
+ {
+ Add(new Run(value));
+ }
+ else
+ {
+ _text = value;
+ }
+ }
+ }
+
+ ///
+ /// Add a text segment to the collection.
+ ///
+ /// For non complex content this appends the text to the end of currently held text.
+ /// For complex content this adds a to the collection.
+ ///
+ ///
+ ///
+ public void Add(string text)
+ {
+ if (HasComplexContent)
+ {
+ Add(new Run(text));
+ }
+ else
+ {
+ _text += text;
+ }
+ }
+
+ public override void Add(Inline item)
+ {
+ if (!HasComplexContent)
+ {
+ base.Add(new Run(_text));
+
+ _text = string.Empty;
+ }
+
+ base.Add(item);
+ }
+
+ ///
+ /// Raised when an inline in the collection changes.
+ ///
+ public event EventHandler? Invalidated;
+
+ ///
+ /// Raises the event.
+ ///
+ protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty);
+
+ private void Invalidate(object? sender, EventArgs e) => Invalidate();
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Italic.cs b/src/Avalonia.Controls/Documents/Italic.cs
new file mode 100644
index 0000000000..e9f4698fc4
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Italic.cs
@@ -0,0 +1,17 @@
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Italic element - markup helper for indicating italicized content.
+ /// Equivalent to a Span with FontStyle property set to FontStyles.Italic.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Italic : Span
+ {
+ static Italic()
+ {
+ FontStyleProperty.OverrideDefaultValue(FontStyle.Italic);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/LineBreak.cs b/src/Avalonia.Controls/Documents/LineBreak.cs
new file mode 100644
index 0000000000..5e0cd1d387
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/LineBreak.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// LineBreak element that forces a line breaking.
+ ///
+ [TrimSurroundingWhitespace]
+ public class LineBreak : Inline
+ {
+ ///
+ /// Creates a new LineBreak instance.
+ ///
+ public LineBreak()
+ {
+ }
+
+ internal override int BuildRun(StringBuilder stringBuilder,
+ IList> textStyleOverrides, int firstCharacterIndex)
+ {
+ var length = AppendText(stringBuilder);
+
+ textStyleOverrides.Add(new ValueSpan(firstCharacterIndex, length,
+ CreateTextRunProperties()));
+
+ return length;
+ }
+
+ internal override int AppendText(StringBuilder stringBuilder)
+ {
+ var text = Environment.NewLine;
+
+ stringBuilder.Append(text);
+
+ return text.Length;
+ }
+ }
+}
+
diff --git a/src/Avalonia.Controls/Documents/Run.cs b/src/Avalonia.Controls/Documents/Run.cs
new file mode 100644
index 0000000000..a7dd5fd94f
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Run.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Data;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// A terminal element in text flow hierarchy - contains a uniformatted run of unicode characters
+ ///
+ public class Run : Inline
+ {
+ ///
+ /// Initializes an instance of Run class.
+ ///
+ public Run()
+ {
+ }
+
+ ///
+ /// Initializes an instance of Run class specifying its text content.
+ ///
+ ///
+ /// Text content assigned to the Run.
+ ///
+ public Run(string? text)
+ {
+ Text = text;
+ }
+
+ ///
+ /// Dependency property backing Text.
+ ///
+ ///
+ /// Note that when a TextRange that intersects with this Run gets modified (e.g. by editing
+ /// a selection in RichTextBox), we will get two changes to this property since we delete
+ /// and then insert when setting the content of a TextRange.
+ ///
+ public static readonly StyledProperty TextProperty = AvaloniaProperty.Register (
+ nameof (Text), defaultBindingMode: BindingMode.TwoWay);
+
+ ///
+ /// The content spanned by this TextElement.
+ ///
+ [Content]
+ public string? Text {
+ get { return GetValue (TextProperty); }
+ set { SetValue (TextProperty, value); }
+ }
+
+ internal override int BuildRun(StringBuilder stringBuilder,
+ IList> textStyleOverrides, int firstCharacterIndex)
+ {
+ var length = AppendText(stringBuilder);
+
+ textStyleOverrides.Add(new ValueSpan(firstCharacterIndex, length,
+ CreateTextRunProperties()));
+
+ return length;
+ }
+
+ internal override int AppendText(StringBuilder stringBuilder)
+ {
+ var text = Text ?? "";
+
+ stringBuilder.Append(text);
+
+ return text.Length;
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(Text):
+ Invalidate();
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs
new file mode 100644
index 0000000000..c086997b07
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Span.cs
@@ -0,0 +1,95 @@
+using System.Collections.Generic;
+using System.Text;
+using Avalonia.Media.TextFormatting;
+using Avalonia.Metadata;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Span element used for grouping other Inline elements.
+ ///
+ public class Span : Inline
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty InlinesProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Inlines),
+ o => o.Inlines);
+
+ ///
+ /// Initializes a new instance of a Span element.
+ ///
+ public Span()
+ {
+ Inlines = new InlineCollection(this);
+
+ Inlines.Invalidated += (s, e) => Invalidate();
+ }
+
+ ///
+ /// Gets or sets the inlines.
+ ///
+ [Content]
+ public InlineCollection Inlines { get; }
+
+ internal override int BuildRun(StringBuilder stringBuilder, IList> textStyleOverrides, int firstCharacterIndex)
+ {
+ var length = 0;
+
+ if (Inlines.HasComplexContent)
+ {
+ foreach (var inline in Inlines)
+ {
+ var inlineLength = inline.BuildRun(stringBuilder, textStyleOverrides, firstCharacterIndex);
+
+ firstCharacterIndex += inlineLength;
+
+ length += inlineLength;
+ }
+ }
+ else
+ {
+ if (Inlines.Text == null)
+ {
+ return length;
+ }
+
+ stringBuilder.Append(Inlines.Text);
+
+ length = Inlines.Text.Length;
+
+ textStyleOverrides.Add(new ValueSpan(firstCharacterIndex, length,
+ CreateTextRunProperties()));
+ }
+
+ return length;
+ }
+
+ internal override int AppendText(StringBuilder stringBuilder)
+ {
+ if (Inlines.HasComplexContent)
+ {
+ var length = 0;
+
+ foreach (var inline in Inlines)
+ {
+ length += inline.AppendText(stringBuilder);
+ }
+
+ return length;
+ }
+
+ if (Inlines.Text == null)
+ {
+ return 0;
+ }
+
+ stringBuilder.Append(Inlines.Text);
+
+ return Inlines.Text.Length;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/TextElement.cs b/src/Avalonia.Controls/Documents/TextElement.cs
new file mode 100644
index 0000000000..4083524881
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/TextElement.cs
@@ -0,0 +1,129 @@
+using System;
+using Avalonia.Media;
+
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// TextElement is an base class for content in text based controls.
+ /// TextElements span other content, applying property values or providing structural information.
+ ///
+ public abstract class TextElement : StyledElement
+ {
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty BackgroundProperty =
+ AvaloniaProperty.Register(nameof(Background), inherits: true);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontFamilyProperty =
+ TextBlock.FontFamilyProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontSizeProperty =
+ TextBlock.FontSizeProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontStyleProperty =
+ TextBlock.FontStyleProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty FontWeightProperty =
+ TextBlock.FontWeightProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty ForegroundProperty =
+ TextBlock.ForegroundProperty.AddOwner();
+
+ ///
+ /// Gets or sets a brush used to paint the control's background.
+ ///
+ public IBrush? Background
+ {
+ get { return GetValue(BackgroundProperty); }
+ set { SetValue(BackgroundProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font family.
+ ///
+ public FontFamily FontFamily
+ {
+ get { return GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font size.
+ ///
+ public double FontSize
+ {
+ get { return GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font style.
+ ///
+ public FontStyle FontStyle
+ {
+ get { return GetValue(FontStyleProperty); }
+ set { SetValue(FontStyleProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the font weight.
+ ///
+ public FontWeight FontWeight
+ {
+ get { return GetValue(FontWeightProperty); }
+ set { SetValue(FontWeightProperty, value); }
+ }
+
+ ///
+ /// Gets or sets a brush used to paint the text.
+ ///
+ public IBrush? Foreground
+ {
+ get { return GetValue(ForegroundProperty); }
+ set { SetValue(ForegroundProperty, value); }
+ }
+
+ ///
+ /// Raised when the visual representation of the text element changes.
+ ///
+ public event EventHandler? Invalidated;
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof(Background):
+ case nameof(FontFamily):
+ case nameof(FontSize):
+ case nameof(FontStyle):
+ case nameof(FontWeight):
+ case nameof(Foreground):
+ Invalidate();
+ break;
+ }
+ }
+
+ ///
+ /// Raises the event.
+ ///
+ protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty);
+ }
+}
diff --git a/src/Avalonia.Controls/Documents/Underline.cs b/src/Avalonia.Controls/Documents/Underline.cs
new file mode 100644
index 0000000000..fcd46c8439
--- /dev/null
+++ b/src/Avalonia.Controls/Documents/Underline.cs
@@ -0,0 +1,15 @@
+namespace Avalonia.Controls.Documents
+{
+ ///
+ /// Underline element - markup helper for indicating superscript content.
+ /// Equivalent to a Span with TextDecorations property set to TextDecorations.Underlined.
+ /// Can contain other inline elements.
+ ///
+ public sealed class Underline : Span
+ {
+ static Underline()
+ {
+ TextDecorationsProperty.OverrideDefaultValue(Media.TextDecorations.Underline);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index e2afbd3bdc..08d559a5c1 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -12,7 +12,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
private double _scaling = 1;
private Size _clientSize;
- public IInputRoot InputRoot { get; private set; }
+ public IInputRoot? InputRoot { get; private set; }
public bool IsDisposed { get; private set; }
public virtual void Dispose()
@@ -47,12 +47,12 @@ namespace Avalonia.Controls.Embedding.Offscreen
}
}
- public Action Input { get; set; }
- public Action Paint { get; set; }
- public Action Resized { get; set; }
- public Action ScalingChanged { get; set; }
+ public Action? Input { get; set; }
+ public Action? Paint { get; set; }
+ public Action? Resized { get; set; }
+ public Action? ScalingChanged { get; set; }
- public Action TransparencyLevelChanged { get; set; }
+ public Action? TransparencyLevelChanged { get; set; }
///
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
@@ -63,18 +63,18 @@ namespace Avalonia.Controls.Embedding.Offscreen
public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
- public virtual void SetCursor(ICursorImpl cursor)
+ public virtual void SetCursor(ICursorImpl? cursor)
{
}
- public Action Closed { get; set; }
- public Action LostFocus { get; set; }
+ public Action? Closed { get; set; }
+ public Action? LostFocus { get; set; }
public abstract IMouseDevice MouseDevice { get; }
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { }
public WindowTransparencyLevel TransparencyLevel { get; private set; }
- public IPopupImpl CreatePopup() => null;
+ public IPopupImpl? CreatePopup() => null;
}
}
diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs
index b9c79e5749..020b162864 100644
--- a/src/Avalonia.Controls/Expander.cs
+++ b/src/Avalonia.Controls/Expander.cs
@@ -1,11 +1,8 @@
using System.Threading;
-
using Avalonia.Animation;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
-#nullable enable
-
namespace Avalonia.Controls
{
///
diff --git a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
index 57861163d6..30bdb4c60e 100644
--- a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
+++ b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
- private IDisposable _subscription;
+ private IDisposable? _subscription;
static ExperimentalAcrylicBorder()
{
@@ -46,11 +46,14 @@ namespace Avalonia.Controls
{
base.OnAttachedToVisualTree(e);
- var tl = (e.Root as TopLevel);
+ var tl = (TopLevel)e.Root;
_subscription = tl.GetObservable(TopLevel.ActualTransparencyLevelProperty)
.Subscribe(x =>
{
+ if (tl.PlatformImpl is null)
+ return;
+
switch (x)
{
case WindowTransparencyLevel.Transparent:
diff --git a/src/Avalonia.Controls/Flyouts/Flyout.cs b/src/Avalonia.Controls/Flyouts/Flyout.cs
index 974f1418b2..df3fe28a29 100644
--- a/src/Avalonia.Controls/Flyouts/Flyout.cs
+++ b/src/Avalonia.Controls/Flyouts/Flyout.cs
@@ -1,8 +1,6 @@
using Avalonia.Controls.Primitives;
using Avalonia.Metadata;
-#nullable enable
-
namespace Avalonia.Controls
{
public class Flyout : FlyoutBase
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index a08b82f512..d024f86b32 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -8,8 +8,6 @@ using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Logging;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider
@@ -267,7 +265,7 @@ namespace Avalonia.Controls.Primitives
return true;
}
- private void PlacementTarget_DetachedFromVisualTree(object sender, VisualTreeAttachmentEventArgs e)
+ private void PlacementTarget_DetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
_ = HideCore(false);
}
@@ -335,7 +333,7 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnOpened()
{
- Opened?.Invoke(this, null);
+ Opened?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnClosing(CancelEventArgs args)
@@ -345,7 +343,7 @@ namespace Avalonia.Controls.Primitives
protected virtual void OnClosed()
{
- Closed?.Invoke(this, null);
+ Closed?.Invoke(this, EventArgs.Empty);
}
///
@@ -368,14 +366,14 @@ namespace Avalonia.Controls.Primitives
return popup;
}
- private void OnPopupOpened(object sender, EventArgs e)
+ private void OnPopupOpened(object? sender, EventArgs e)
{
IsOpen = true;
_popupHostChangedHandler?.Invoke(Popup!.Host);
}
- private void OnPopupClosing(object sender, CancelEventArgs e)
+ private void OnPopupClosing(object? sender, CancelEventArgs e)
{
if (IsOpen)
{
@@ -383,7 +381,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void OnPopupClosed(object sender, EventArgs e)
+ private void OnPopupClosed(object? sender, EventArgs e)
{
HideCore(false);
@@ -391,7 +389,7 @@ namespace Avalonia.Controls.Primitives
}
// This method is handling both popup logical tree and target logical tree.
- private void OnPlacementTargetOrPopupKeyUp(object sender, KeyEventArgs e)
+ private void OnPlacementTargetOrPopupKeyUp(object? sender, KeyEventArgs e)
{
if (!e.Handled
&& IsOpen
@@ -530,10 +528,10 @@ namespace Avalonia.Controls.Primitives
}
}
- private static void OnControlContextRequested(object sender, ContextRequestedEventArgs e)
+ private static void OnControlContextRequested(object? sender, ContextRequestedEventArgs e)
{
- var control = (Control)sender;
if (!e.Handled
+ && sender is Control control
&& control.ContextFlyout is FlyoutBase flyout)
{
if (control.ContextMenu != null)
diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyout.cs b/src/Avalonia.Controls/Flyouts/MenuFlyout.cs
index 5b2e323f4f..97fda68051 100644
--- a/src/Avalonia.Controls/Flyouts/MenuFlyout.cs
+++ b/src/Avalonia.Controls/Flyouts/MenuFlyout.cs
@@ -4,8 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Metadata;
-#nullable enable
-
namespace Avalonia.Controls
{
public class MenuFlyout : FlyoutBase
@@ -18,7 +16,7 @@ namespace Avalonia.Controls
///
/// Defines the property
///
- public static readonly DirectProperty ItemsProperty =
+ public static readonly DirectProperty ItemsProperty =
ItemsControl.ItemsProperty.AddOwner(x => x.Items,
(x, v) => x.Items = v);
@@ -35,7 +33,7 @@ namespace Avalonia.Controls
/// Gets or sets the items of the MenuFlyout
///
[Content]
- public IEnumerable Items
+ public IEnumerable? Items
{
get => _items;
set => SetAndRaise(ItemsProperty, ref _items, value);
@@ -51,7 +49,7 @@ namespace Avalonia.Controls
}
private Classes? _classes;
- private IEnumerable _items;
+ private IEnumerable? _items;
private IDataTemplate? _itemTemplate;
protected override Control CreatePresenter()
diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
index 3a45c85c70..bcd859100a 100644
--- a/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
+++ b/src/Avalonia.Controls/Flyouts/MenuFlyoutPresenter.cs
@@ -43,5 +43,18 @@ namespace Avalonia.Controls
{
return new MenuItemContainerGenerator(this);
}
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ foreach (var i in LogicalChildren)
+ {
+ if (i is MenuItem menuItem)
+ {
+ menuItem.IsSubMenuOpen = false;
+ }
+ }
+ }
}
}
diff --git a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
index 6220a20a82..70d9dbec08 100644
--- a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs
@@ -17,27 +17,27 @@ namespace Avalonia.Controls.Generators
///
/// Gets or sets the data template used to display the items in the control.
///
- IDataTemplate ItemTemplate { get; set; }
+ IDataTemplate? ItemTemplate { get; set; }
///
/// Gets the ContainerType, or null if its an untyped ContainerGenerator.
///
- Type ContainerType { get; }
+ Type? ContainerType { get; }
///
/// Signaled whenever new containers are materialized.
///
- event EventHandler Materialized;
+ event EventHandler? Materialized;
///
/// Event raised whenever containers are dematerialized.
///
- event EventHandler Dematerialized;
+ event EventHandler? Dematerialized;
///
/// Event raised whenever containers are recycled.
///
- event EventHandler Recycled;
+ event EventHandler? Recycled;
///
/// Creates a container control for an item.
@@ -90,13 +90,13 @@ namespace Avalonia.Controls.Generators
///
/// The index.
/// The container, or null if no container created.
- IControl ContainerFromIndex(int index);
+ IControl? ContainerFromIndex(int index);
///
/// Gets the index of the specified container control.
///
/// The container.
/// The index of the container, or -1 if not found.
- int IndexFromContainer(IControl container);
+ int IndexFromContainer(IControl? container);
}
}
diff --git a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
index 335e0df5f7..f15987fe79 100644
--- a/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Generators
///
/// Gets the container index for the tree.
///
- TreeContainerIndex Index { get; }
+ TreeContainerIndex? Index { get; }
///
/// Updates the index based on the parent .
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
index f16834f6e1..79a9e16879 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
@@ -20,27 +20,25 @@ namespace Avalonia.Controls.Generators
/// The owner control.
public ItemContainerGenerator(IControl owner)
{
- Contract.Requires(owner != null);
-
- Owner = owner;
+ Owner = owner ?? throw new ArgumentNullException(nameof(owner));
}
///
public IEnumerable Containers => _containers.Values;
///
- public event EventHandler Materialized;
+ public event EventHandler? Materialized;
///
- public event EventHandler Dematerialized;
+ public event EventHandler? Dematerialized;
///
- public event EventHandler Recycled;
+ public event EventHandler? Recycled;
///
/// Gets or sets the data template used to display the items in the control.
///
- public IDataTemplate ItemTemplate { get; set; }
+ public IDataTemplate? ItemTemplate { get; set; }
///
/// Gets the owner control.
@@ -48,12 +46,12 @@ namespace Avalonia.Controls.Generators
public IControl Owner { get; }
///
- public virtual Type ContainerType => null;
+ public virtual Type? ContainerType => null;
///
public ItemContainerInfo Materialize(int index, object item)
{
- var container = new ItemContainerInfo(CreateContainer(item), item, index);
+ var container = new ItemContainerInfo(CreateContainer(item)!, item, index);
_containers.Add(container.Index, container);
Materialized?.Invoke(this, new ItemContainerEventArgs(container));
@@ -104,9 +102,7 @@ namespace Avalonia.Controls.Generators
{
for (var i = startingIndex; i < startingIndex + count; ++i)
{
- ItemContainerInfo found;
-
- if (_containers.TryGetValue(i, out found))
+ if (_containers.TryGetValue(i, out var found))
{
result.Add(found);
}
@@ -154,15 +150,15 @@ namespace Avalonia.Controls.Generators
}
///
- public IControl ContainerFromIndex(int index)
+ public IControl? ContainerFromIndex(int index)
{
- ItemContainerInfo result;
+ ItemContainerInfo? result;
_containers.TryGetValue(index, out result);
return result?.ContainerControl;
}
///
- public int IndexFromContainer(IControl container)
+ public int IndexFromContainer(IControl? container)
{
foreach (var i in _containers)
{
@@ -180,7 +176,7 @@ namespace Avalonia.Controls.Generators
///
/// The item.
/// The created container control.
- protected virtual IControl CreateContainer(object item)
+ protected virtual IControl? CreateContainer(object item)
{
var result = item as IControl;
diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
index d1fbb25cb1..635f3a7d37 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs
@@ -19,13 +19,10 @@ namespace Avalonia.Controls.Generators
public ItemContainerGenerator(
IControl owner,
AvaloniaProperty contentProperty,
- AvaloniaProperty contentTemplateProperty)
+ AvaloniaProperty? contentTemplateProperty)
: base(owner)
{
- Contract.Requires(owner != null);
- Contract.Requires(contentProperty != null);
-
- ContentProperty = contentProperty;
+ ContentProperty = contentProperty ?? throw new ArgumentNullException(nameof(contentProperty));
ContentTemplateProperty = contentTemplateProperty;
}
@@ -40,10 +37,10 @@ namespace Avalonia.Controls.Generators
///
/// Gets the container's ContentTemplate property.
///
- protected AvaloniaProperty ContentTemplateProperty { get; }
+ protected AvaloniaProperty? ContentTemplateProperty { get; }
///
- protected override IControl CreateContainer(object item)
+ protected override IControl? CreateContainer(object item)
{
var container = item as T;
diff --git a/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs
index d3cf70e0f8..bfcb474515 100644
--- a/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/MenuItemContainerGenerator.cs
@@ -12,7 +12,7 @@
}
///
- protected override IControl CreateContainer(object item)
+ protected override IControl? CreateContainer(object item)
{
var separator = item as Separator;
return separator != null ? separator : base.CreateContainer(item);
diff --git a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
index 840a1f66f1..c6b0bda9af 100644
--- a/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TabItemContainerGenerator.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Controls.Generators
protected override IControl CreateContainer(object item)
{
- var tabItem = (TabItem)base.CreateContainer(item);
+ var tabItem = (TabItem)base.CreateContainer(item)!;
tabItem.Bind(TabItem.TabStripPlacementProperty, new OwnerBinding(
tabItem,
@@ -28,7 +28,7 @@ namespace Avalonia.Controls.Generators
if (tabItem.HeaderTemplate == null)
{
- tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding(
+ tabItem.Bind(TabItem.HeaderTemplateProperty, new OwnerBinding(
tabItem,
TabControl.ItemTemplateProperty));
}
@@ -50,7 +50,7 @@ namespace Avalonia.Controls.Generators
if (!(tabItem.Content is IControl))
{
- tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding(
+ tabItem.Bind(TabItem.ContentTemplateProperty, new OwnerBinding(
tabItem,
TabControl.ContentTemplateProperty));
}
@@ -62,8 +62,8 @@ namespace Avalonia.Controls.Generators
{
private readonly TabItem _item;
private readonly StyledProperty _ownerProperty;
- private IDisposable _ownerSubscription;
- private IDisposable _propertySubscription;
+ private IDisposable? _ownerSubscription;
+ private IDisposable? _propertySubscription;
public OwnerBinding(TabItem item, StyledProperty ownerProperty)
{
@@ -82,7 +82,7 @@ namespace Avalonia.Controls.Generators
_ownerSubscription = null;
}
- private void OwnerChanged(ILogical c)
+ private void OwnerChanged(ILogical? c)
{
_propertySubscription?.Dispose();
_propertySubscription = null;
diff --git a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
index f0da370f73..da13416700 100644
--- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
+++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs
@@ -21,12 +21,12 @@ namespace Avalonia.Controls.Generators
///
/// Signaled whenever new containers are materialized.
///
- public event EventHandler Materialized;
+ public event EventHandler? Materialized;
///
/// Event raised whenever containers are dematerialized.
///
- public event EventHandler Dematerialized;
+ public event EventHandler? Dematerialized;
///
/// Gets the currently materialized containers.
@@ -92,7 +92,7 @@ namespace Avalonia.Controls.Generators
///
/// The item.
/// The container, or null of not found.
- public IControl ContainerFromItem(object item)
+ public IControl? ContainerFromItem(object item)
{
if (item != null)
{
@@ -108,7 +108,7 @@ namespace Avalonia.Controls.Generators
///
/// The container.
/// The item, or null of not found.
- public object ItemFromContainer(IControl container)
+ public object? ItemFromContainer(IControl? container)
{
if (container != null)
{
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index 9e65ef5f81..536a5fdd06 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Controls.Generators
public class TreeItemContainerGenerator : ItemContainerGenerator, ITreeItemContainerGenerator
where T : class, IControl, new()
{
- private TreeView _treeView;
+ private TreeView? _treeView;
///
/// Initializes a new instance of the class.
@@ -32,20 +32,15 @@ namespace Avalonia.Controls.Generators
AvaloniaProperty isExpandedProperty)
: base(owner, contentProperty, contentTemplateProperty)
{
- Contract.Requires(owner != null);
- Contract.Requires(contentProperty != null);
- Contract.Requires(itemsProperty != null);
- Contract.Requires(isExpandedProperty != null);
-
- ItemsProperty = itemsProperty;
- IsExpandedProperty = isExpandedProperty;
+ ItemsProperty = itemsProperty ?? throw new ArgumentNullException(nameof(itemsProperty));
+ IsExpandedProperty = isExpandedProperty ?? throw new ArgumentNullException(nameof(isExpandedProperty));
UpdateIndex();
}
///
/// Gets the container index for the tree.
///
- public TreeContainerIndex Index { get; private set; }
+ public TreeContainerIndex? Index { get; private set; }
///
/// Gets the item container's Items property.
@@ -58,7 +53,7 @@ namespace Avalonia.Controls.Generators
protected AvaloniaProperty IsExpandedProperty { get; }
///
- protected override IControl CreateContainer(object item)
+ protected override IControl? CreateContainer(object? item)
{
var container = item as T;
@@ -141,12 +136,12 @@ namespace Avalonia.Controls.Generators
{
private readonly IDataTemplate _inner;
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
- public IControl Build(object param) => _inner.Build(param);
- public bool Match(object data) => _inner.Match(data);
- public InstancedBinding ItemsSelector(object item) => null;
+ public IControl? Build(object? param) => _inner.Build(param);
+ public bool Match(object? data) => _inner.Match(data);
+ public InstancedBinding? ItemsSelector(object item) => null;
}
- private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate primary)
+ private ITreeDataTemplate GetTreeDataTemplate(object item, IDataTemplate? primary)
{
var template = Owner.FindDataTemplate(item, primary) ?? FuncDataTemplate.Default;
var treeTemplate = template as ITreeDataTemplate ?? new WrapperTreeDataTemplate(template);
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 74401c450a..8d246d35f4 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/src/Avalonia.Controls/Grid.cs
@@ -8,6 +8,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Avalonia.Layout;
using Avalonia.Media;
@@ -48,7 +49,7 @@ namespace Avalonia.Controls
/// Column property value.
public static void SetColumn(Control element, int value)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(ColumnProperty, value);
}
@@ -59,7 +60,7 @@ namespace Avalonia.Controls
/// Column property value.
public static int GetColumn(Control element)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(ColumnProperty);
}
@@ -70,7 +71,7 @@ namespace Avalonia.Controls
/// Row property value.
public static void SetRow(Control element, int value)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(RowProperty, value);
}
@@ -81,7 +82,7 @@ namespace Avalonia.Controls
/// Row property value.
public static int GetRow(Control element)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(RowProperty);
}
@@ -92,7 +93,7 @@ namespace Avalonia.Controls
/// ColumnSpan property value.
public static void SetColumnSpan(Control element, int value)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(ColumnSpanProperty, value);
}
@@ -103,7 +104,7 @@ namespace Avalonia.Controls
/// ColumnSpan property value.
public static int GetColumnSpan(Control element)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(ColumnSpanProperty);
}
@@ -114,7 +115,7 @@ namespace Avalonia.Controls
/// RowSpan property value.
public static void SetRowSpan(Control element, int value)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(RowSpanProperty, value);
}
@@ -125,7 +126,7 @@ namespace Avalonia.Controls
/// RowSpan property value.
public static int GetRowSpan(Control element)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(RowSpanProperty);
}
@@ -136,7 +137,7 @@ namespace Avalonia.Controls
/// IsSharedSizeScope property value.
public static void SetIsSharedSizeScope(Control element, bool value)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
element.SetValue(IsSharedSizeScopeProperty, value);
}
@@ -147,7 +148,7 @@ namespace Avalonia.Controls
/// IsSharedSizeScope property value.
public static bool GetIsSharedSizeScope(Control element)
{
- Contract.Requires(element != null);
+ _ = element ?? throw new ArgumentNullException(nameof(element));
return element.GetValue(IsSharedSizeScopeProperty);
}
@@ -573,7 +574,7 @@ namespace Avalonia.Controls
///
///
///
- protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ protected override void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
CellsStructureDirty = true;
base.ChildrenChanged(sender, e);
@@ -598,7 +599,7 @@ namespace Avalonia.Controls
{
double value = 0.0;
- Contract.Requires(_data != null);
+ Debug.Assert(_data != null);
// actual value calculations require structure to be up-to-date
if (!ColumnDefinitionsDirty)
@@ -620,7 +621,7 @@ namespace Avalonia.Controls
{
double value = 0.0;
- Contract.Requires(_data != null);
+ Debug.Assert(_data != null);
// actual value calculations require structure to be up-to-date
if (!RowDefinitionsDirty)
@@ -994,7 +995,7 @@ namespace Avalonia.Controls
}
var children = this.Children;
- Hashtable spanStore = null;
+ Hashtable? spanStore = null;
bool ignoreDesiredSizeV = forceInfinityV;
int i = cellsHead;
@@ -1048,7 +1049,7 @@ namespace Avalonia.Controls
foreach (DictionaryEntry e in spanStore)
{
SpanKey key = (SpanKey)e.Key;
- double requestedSize = (double)e.Value;
+ double requestedSize = (double)e.Value!;
EnsureMinSizeInDefinitionRange(
key.U ? DefinitionsU : DefinitionsV,
@@ -1069,7 +1070,7 @@ namespace Avalonia.Controls
/// true if this is a column span. false if this is a row span.
/// Value to store. If an entry already exists the biggest value is stored.
private static void RegisterSpan(
- ref Hashtable store,
+ ref Hashtable? store,
int start,
int count,
bool u,
@@ -1081,7 +1082,7 @@ namespace Avalonia.Controls
}
SpanKey key = new SpanKey(start, count, u);
- object o = store[key];
+ object? o = store[key];
if (o == null
|| value > (double)o)
@@ -1605,12 +1606,12 @@ namespace Avalonia.Controls
while (minCount > 0 && tempDefinitions[minCount - 1].MeasureSize < 0.0)
{
--minCount;
- tempDefinitions[minCount] = null;
+ tempDefinitions[minCount] = null!;
}
while (maxCount > 0 && tempDefinitions[defCount + maxCount - 1].MeasureSize < 0.0)
{
--maxCount;
- tempDefinitions[defCount + maxCount] = null;
+ tempDefinitions[defCount + maxCount] = null!;
}
}
@@ -2321,7 +2322,7 @@ namespace Avalonia.Controls
/// by adding / removing GridLinesRenderer visual.
/// Returns a reference to GridLinesRenderer visual or null.
///
- private GridLinesRenderer EnsureGridLinesRenderer()
+ private GridLinesRenderer? EnsureGridLinesRenderer()
{
//
// synchronize the state
@@ -2369,16 +2370,16 @@ namespace Avalonia.Controls
grid.InvalidateVisual();
}
- grid.SetFlags((bool)e.NewValue, Flags.ShowGridLinesPropertyValue);
+ grid.SetFlags((bool)e.NewValue!, Flags.ShowGridLinesPropertyValue);
}
private static void OnCellAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{
- Visual child = d as Visual;
+ Visual? child = d as Visual;
if (child != null)
{
- Grid grid = child.GetVisualParent();
+ Grid? grid = child.GetVisualParent();
if (grid != null
&& grid.ExtData != null
&& grid.ListenToNotifications)
@@ -2395,7 +2396,7 @@ namespace Avalonia.Controls
/// true if one or both of x and y are null, in which case result holds
/// the relative sort order.
///
- private static bool CompareNullRefs(object x, object y, out int result)
+ private static bool CompareNullRefs([NotNullWhen(false)] object? x, [NotNullWhen(false)] object? y, out int result)
{
result = 2;
@@ -2426,7 +2427,7 @@ namespace Avalonia.Controls
///
private IReadOnlyList DefinitionsU
{
- get { return (ExtData.DefinitionsU); }
+ get { return (ExtData.DefinitionsU!); }
}
///
@@ -2434,7 +2435,7 @@ namespace Avalonia.Controls
///
private IReadOnlyList DefinitionsV
{
- get { return (ExtData.DefinitionsV); }
+ get { return (ExtData.DefinitionsV!); }
}
///
@@ -2450,7 +2451,7 @@ namespace Avalonia.Controls
if (extData.TempDefinitions == null
|| extData.TempDefinitions.Length < requiredLength)
{
- WeakReference tempDefinitionsWeakRef = (WeakReference)Thread.GetData(s_tempDefinitionsDataSlot);
+ WeakReference? tempDefinitionsWeakRef = (WeakReference?)Thread.GetData(s_tempDefinitionsDataSlot);
if (tempDefinitionsWeakRef == null)
{
extData.TempDefinitions = new DefinitionBase[requiredLength];
@@ -2458,7 +2459,7 @@ namespace Avalonia.Controls
}
else
{
- extData.TempDefinitions = (DefinitionBase[])tempDefinitionsWeakRef.Target;
+ extData.TempDefinitions = (DefinitionBase[]?)tempDefinitionsWeakRef.Target;
if (extData.TempDefinitions == null
|| extData.TempDefinitions.Length < requiredLength)
{
@@ -2515,7 +2516,7 @@ namespace Avalonia.Controls
///
private CellCache[] PrivateCells
{
- get { return (ExtData.CellCachesCollection); }
+ get { return (ExtData.CellCachesCollection!); }
}
///
@@ -2586,7 +2587,7 @@ namespace Avalonia.Controls
///
private ExtendedData ExtData
{
- get { return (_data); }
+ get { return (_data!); }
}
///
@@ -2608,17 +2609,17 @@ namespace Avalonia.Controls
}
// Extended data instantiated on demand, for non-trivial case handling only
- private ExtendedData _data;
+ private ExtendedData? _data;
// Grid validity / property caches dirtiness flags
private Flags _flags;
- private GridLinesRenderer _gridLinesRenderer;
+ private GridLinesRenderer? _gridLinesRenderer;
// Keeps track of definition indices.
- int[] _definitionIndices;
+ int[]? _definitionIndices;
// Stores unrounded values and rounding errors during layout rounding.
- double[] _roundingErrors;
+ double[]? _roundingErrors;
// 5 is an arbitrary constant chosen to end the measure loop
private const int c_layoutLoopMaxCount = 5;
@@ -2635,16 +2636,16 @@ namespace Avalonia.Controls
///
private class ExtendedData
{
- internal ColumnDefinitions ColumnDefinitions; // collection of column definitions (logical tree support)
- internal RowDefinitions RowDefinitions; // collection of row definitions (logical tree support)
- internal IReadOnlyList DefinitionsU; // collection of column definitions used during calc
- internal IReadOnlyList DefinitionsV; // collection of row definitions used during calc
- internal CellCache[] CellCachesCollection; // backing store for logical children
+ internal ColumnDefinitions? ColumnDefinitions; // collection of column definitions (logical tree support)
+ internal RowDefinitions? RowDefinitions; // collection of row definitions (logical tree support)
+ internal IReadOnlyList? DefinitionsU; // collection of column definitions used during calc
+ internal IReadOnlyList? DefinitionsV; // collection of row definitions used during calc
+ internal CellCache[]? CellCachesCollection; // backing store for logical children
internal int CellGroup1; // index of the first cell in first cell group
internal int CellGroup2; // index of the first cell in second cell group
internal int CellGroup3; // index of the first cell in third cell group
internal int CellGroup4; // index of the first cell in forth cell group
- internal DefinitionBase[] TempDefinitions; // temporary array used during layout for various purposes
+ internal DefinitionBase[]? TempDefinitions; // temporary array used during layout for various purposes
// TempDefinitions.Length == Max(definitionsU.Length, definitionsV.Length)
}
@@ -2831,9 +2832,9 @@ namespace Avalonia.Controls
///
///
///
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- SpanKey sk = obj as SpanKey;
+ SpanKey? sk = obj as SpanKey;
return (sk != null
&& sk._start == _start
&& sk._count == _count
@@ -2866,10 +2867,10 @@ namespace Avalonia.Controls
///
private class SpanPreferredDistributionOrderComparer : IComparer
{
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
- DefinitionBase definitionX = x as DefinitionBase;
- DefinitionBase definitionY = y as DefinitionBase;
+ DefinitionBase? definitionX = x as DefinitionBase;
+ DefinitionBase? definitionY = y as DefinitionBase;
int result;
@@ -2908,10 +2909,10 @@ namespace Avalonia.Controls
///
private class SpanMaxDistributionOrderComparer : IComparer
{
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
- DefinitionBase definitionX = x as DefinitionBase;
- DefinitionBase definitionY = y as DefinitionBase;
+ DefinitionBase? definitionX = x as DefinitionBase;
+ DefinitionBase? definitionY = y as DefinitionBase;
int result;
@@ -2954,17 +2955,16 @@ namespace Avalonia.Controls
internal StarDistributionOrderIndexComparer(IReadOnlyList definitions)
{
- Contract.Requires(definitions != null);
- this.definitions = definitions;
+ this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
- DefinitionBase definitionX = null;
- DefinitionBase definitionY = null;
+ DefinitionBase? definitionX = null;
+ DefinitionBase? definitionY = null;
if (indexX != null)
{
@@ -2995,17 +2995,16 @@ namespace Avalonia.Controls
internal DistributionOrderIndexComparer(IReadOnlyList definitions)
{
- Contract.Requires(definitions != null);
- this.definitions = definitions;
+ this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
- DefinitionBase definitionX = null;
- DefinitionBase definitionY = null;
+ DefinitionBase? definitionX = null;
+ DefinitionBase? definitionY = null;
if (indexX != null)
{
@@ -3038,11 +3037,10 @@ namespace Avalonia.Controls
internal RoundingErrorIndexComparer(double[] errors)
{
- Contract.Requires(errors != null);
- this.errors = errors;
+ this.errors = errors ?? throw new ArgumentNullException(nameof(errors));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
@@ -3067,10 +3065,10 @@ namespace Avalonia.Controls
///
private class MinRatioComparer : IComparer
{
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
- DefinitionBase definitionX = x as DefinitionBase;
- DefinitionBase definitionY = y as DefinitionBase;
+ DefinitionBase? definitionX = x as DefinitionBase;
+ DefinitionBase? definitionY = y as DefinitionBase;
int result;
@@ -3090,10 +3088,10 @@ namespace Avalonia.Controls
///
private class MaxRatioComparer : IComparer
{
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
- DefinitionBase definitionX = x as DefinitionBase;
- DefinitionBase definitionY = y as DefinitionBase;
+ DefinitionBase? definitionX = x as DefinitionBase;
+ DefinitionBase? definitionY = y as DefinitionBase;
int result;
@@ -3112,10 +3110,10 @@ namespace Avalonia.Controls
///
private class StarWeightComparer : IComparer
{
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
- DefinitionBase definitionX = x as DefinitionBase;
- DefinitionBase definitionY = y as DefinitionBase;
+ DefinitionBase? definitionX = x as DefinitionBase;
+ DefinitionBase? definitionY = y as DefinitionBase;
int result;
@@ -3137,17 +3135,16 @@ namespace Avalonia.Controls
internal MinRatioIndexComparer(IReadOnlyList definitions)
{
- Contract.Requires(definitions != null);
- this.definitions = definitions;
+ this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
- DefinitionBase definitionX = null;
- DefinitionBase definitionY = null;
+ DefinitionBase? definitionX = null;
+ DefinitionBase? definitionY = null;
if (indexX != null)
{
@@ -3178,17 +3175,16 @@ namespace Avalonia.Controls
internal MaxRatioIndexComparer(IReadOnlyList definitions)
{
- Contract.Requires(definitions != null);
- this.definitions = definitions;
+ this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
- DefinitionBase definitionX = null;
- DefinitionBase definitionY = null;
+ DefinitionBase? definitionX = null;
+ DefinitionBase? definitionY = null;
if (indexX != null)
{
@@ -3219,17 +3215,16 @@ namespace Avalonia.Controls
internal StarWeightIndexComparer(IReadOnlyList definitions)
{
- Contract.Requires(definitions != null);
- this.definitions = definitions;
+ this.definitions = definitions ?? throw new ArgumentNullException(nameof(definitions));
}
- public int Compare(object x, object y)
+ public int Compare(object? x, object? y)
{
int? indexX = x as int?;
int? indexY = y as int?;
- DefinitionBase definitionX = null;
- DefinitionBase definitionY = null;
+ DefinitionBase? definitionX = null;
+ DefinitionBase? definitionY = null;
if (indexX != null)
{
diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs
index ee6a146d61..d7022b80ce 100644
--- a/src/Avalonia.Controls/GridLength.cs
+++ b/src/Avalonia.Controls/GridLength.cs
@@ -135,7 +135,7 @@ namespace Avalonia.Controls
///
/// The object with which to test equality.
/// True if the objects are equal, otherwise false.
- public override bool Equals(object o)
+ public override bool Equals(object? o)
{
if (o == null)
{
diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs
index 26c8464576..216e43e1f0 100644
--- a/src/Avalonia.Controls/GridSplitter.cs
+++ b/src/Avalonia.Controls/GridSplitter.cs
@@ -59,7 +59,7 @@ namespace Avalonia.Controls
private static readonly Cursor s_columnSplitterCursor = new Cursor(StandardCursorType.SizeWestEast);
private static readonly Cursor s_rowSplitterCursor = new Cursor(StandardCursorType.SizeNorthSouth);
- private ResizeData _resizeData;
+ private ResizeData? _resizeData;
///
/// Indicates whether the Splitter resizes the Columns, Rows, or Both.
@@ -197,9 +197,9 @@ namespace Avalonia.Controls
///
private void RemovePreviewAdorner()
{
- if (_resizeData.Adorner != null)
+ if (_resizeData?.Adorner != null)
{
- AdornerLayer layer = AdornerLayer.GetAdornerLayer(this);
+ AdornerLayer layer = AdornerLayer.GetAdornerLayer(this)!;
layer.Children.Remove(_resizeData.Adorner);
}
}
@@ -242,7 +242,7 @@ namespace Avalonia.Controls
///
private bool SetupDefinitionsToResize()
{
- int gridSpan = GetValue(_resizeData.ResizeDirection == GridResizeDirection.Columns ?
+ int gridSpan = GetValue(_resizeData!.ResizeDirection == GridResizeDirection.Columns ?
Grid.ColumnSpanProperty :
Grid.RowSpanProperty);
@@ -276,8 +276,8 @@ namespace Avalonia.Controls
// Get count of rows/columns in the resize direction.
int count = _resizeData.ResizeDirection == GridResizeDirection.Columns ?
- _resizeData.Grid.ColumnDefinitions.Count :
- _resizeData.Grid.RowDefinitions.Count;
+ _resizeData.Grid!.ColumnDefinitions.Count :
+ _resizeData.Grid!.RowDefinitions.Count;
if (index1 >= 0 && index2 < count)
{
@@ -322,10 +322,10 @@ namespace Avalonia.Controls
///
private void SetupPreviewAdorner()
{
- if (_resizeData.ShowsPreview)
+ if (_resizeData!.ShowsPreview)
{
// Get the adorner layer and add an adorner to it.
- var adornerLayer = AdornerLayer.GetAdornerLayer(_resizeData.Grid);
+ var adornerLayer = AdornerLayer.GetAdornerLayer(_resizeData.Grid!);
var previewContent = PreviewContent;
@@ -335,7 +335,7 @@ namespace Avalonia.Controls
return;
}
- IControl builtPreviewContent = previewContent?.Build();
+ IControl? builtPreviewContent = previewContent?.Build();
_resizeData.Adorner = new PreviewAdorner(builtPreviewContent);
@@ -409,13 +409,13 @@ namespace Avalonia.Controls
// Set the Translation of the Adorner to the distance from the thumb.
if (_resizeData.ResizeDirection == GridResizeDirection.Columns)
{
- _resizeData.Adorner.OffsetX = Math.Min(
+ _resizeData.Adorner!.OffsetX = Math.Min(
Math.Max(horizontalChange, _resizeData.MinChange),
_resizeData.MaxChange);
}
else
{
- _resizeData.Adorner.OffsetY = Math.Min(
+ _resizeData.Adorner!.OffsetY = Math.Min(
Math.Max(verticalChange, _resizeData.MinChange),
_resizeData.MaxChange);
}
@@ -437,7 +437,7 @@ namespace Avalonia.Controls
if (_resizeData.ShowsPreview)
{
// Update the grid.
- MoveSplitter(_resizeData.Adorner.OffsetX, _resizeData.Adorner.OffsetY);
+ MoveSplitter(_resizeData.Adorner!.OffsetX, _resizeData.Adorner.OffsetY);
RemovePreviewAdorner();
}
@@ -481,14 +481,14 @@ namespace Avalonia.Controls
private void CancelResize()
{
// Restore original column/row lengths.
- if (_resizeData.ShowsPreview)
+ if (_resizeData!.ShowsPreview)
{
RemovePreviewAdorner();
}
else // Reset the columns/rows lengths to the saved values.
{
- SetDefinitionLength(_resizeData.Definition1, _resizeData.OriginalDefinition1Length);
- SetDefinitionLength(_resizeData.Definition2, _resizeData.OriginalDefinition2Length);
+ SetDefinitionLength(_resizeData.Definition1!, _resizeData.OriginalDefinition1Length);
+ SetDefinitionLength(_resizeData.Definition2!, _resizeData.OriginalDefinition2Length);
}
_resizeData = null;
@@ -536,12 +536,12 @@ namespace Avalonia.Controls
///
private void GetDeltaConstraints(out double minDelta, out double maxDelta)
{
- double definition1Len = GetActualLength(_resizeData.Definition1);
- double definition1Min = _resizeData.Definition1.UserMinSizeValueCache;
+ double definition1Len = GetActualLength(_resizeData!.Definition1!);
+ double definition1Min = _resizeData.Definition1!.UserMinSizeValueCache;
double definition1Max = _resizeData.Definition1.UserMaxSizeValueCache;
- double definition2Len = GetActualLength(_resizeData.Definition2);
- double definition2Min = _resizeData.Definition2.UserMinSizeValueCache;
+ double definition2Len = GetActualLength(_resizeData.Definition2!);
+ double definition2Min = _resizeData.Definition2!.UserMinSizeValueCache;
double definition2Max = _resizeData.Definition2.UserMaxSizeValueCache;
// Set MinWidths to be greater than width of splitter.
@@ -565,11 +565,11 @@ namespace Avalonia.Controls
private void SetLengths(double definition1Pixels, double definition2Pixels)
{
// For the case where both definition1 and 2 are stars, update all star values to match their current pixel values.
- if (_resizeData.SplitBehavior == SplitBehavior.Split)
+ if (_resizeData!.SplitBehavior == SplitBehavior.Split)
{
var definitions = _resizeData.ResizeDirection == GridResizeDirection.Columns ?
- (IAvaloniaReadOnlyList)_resizeData.Grid.ColumnDefinitions :
- (IAvaloniaReadOnlyList)_resizeData.Grid.RowDefinitions;
+ (IAvaloniaReadOnlyList)_resizeData.Grid!.ColumnDefinitions :
+ (IAvaloniaReadOnlyList)_resizeData.Grid!.RowDefinitions;
var definitionsCount = definitions.Count;
@@ -595,11 +595,11 @@ namespace Avalonia.Controls
}
else if (_resizeData.SplitBehavior == SplitBehavior.Resize1)
{
- SetDefinitionLength(_resizeData.Definition1, new GridLength(definition1Pixels));
+ SetDefinitionLength(_resizeData.Definition1!, new GridLength(definition1Pixels));
}
else
{
- SetDefinitionLength(_resizeData.Definition2, new GridLength(definition2Pixels));
+ SetDefinitionLength(_resizeData.Definition2!, new GridLength(definition2Pixels));
}
}
@@ -623,8 +623,8 @@ namespace Avalonia.Controls
delta = LayoutHelper.RoundLayoutValue(delta, LayoutHelper.GetLayoutScale(this));
}
- DefinitionBase definition1 = _resizeData.Definition1;
- DefinitionBase definition2 = _resizeData.Definition2;
+ DefinitionBase? definition1 = _resizeData.Definition1;
+ DefinitionBase? definition2 = _resizeData.Definition2;
if (definition1 != null && definition2 != null)
{
@@ -691,7 +691,7 @@ namespace Avalonia.Controls
private readonly TranslateTransform _translation;
private readonly Decorator _decorator;
- public PreviewAdorner(IControl previewControl)
+ public PreviewAdorner(IControl? previewControl)
{
// Add a decorator to perform translations.
_translation = new TranslateTransform();
@@ -762,22 +762,22 @@ namespace Avalonia.Controls
private class ResizeData
{
public bool ShowsPreview;
- public PreviewAdorner Adorner;
+ public PreviewAdorner? Adorner;
// The constraints to keep the Preview within valid ranges.
public double MinChange;
public double MaxChange;
// The grid to Resize.
- public Grid Grid;
+ public Grid? Grid;
// Cache of Resize Direction and Behavior.
public GridResizeDirection ResizeDirection;
public GridResizeBehavior ResizeBehavior;
// The columns/rows to resize.
- public DefinitionBase Definition1;
- public DefinitionBase Definition2;
+ public DefinitionBase? Definition1;
+ public DefinitionBase? Definition2;
// Are the columns/rows star lengths.
public SplitBehavior SplitBehavior;
diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs
index e89bb177dd..d83b996aa0 100644
--- a/src/Avalonia.Controls/HotkeyManager.cs
+++ b/src/Avalonia.Controls/HotkeyManager.cs
@@ -7,29 +7,29 @@ namespace Avalonia.Controls
{
public class HotKeyManager
{
- public static readonly AttachedProperty HotKeyProperty
- = AvaloniaProperty.RegisterAttached("HotKey", typeof(HotKeyManager));
+ public static readonly AttachedProperty HotKeyProperty
+ = AvaloniaProperty.RegisterAttached("HotKey", typeof(HotKeyManager));
class HotkeyCommandWrapper : ICommand
{
- public HotkeyCommandWrapper(ICommandSource control)
+ public HotkeyCommandWrapper(ICommandSource? control)
{
CommandSource = control;
}
- public readonly ICommandSource CommandSource;
+ public readonly ICommandSource? CommandSource;
- private ICommand GetCommand() => CommandSource.Command;
+ private ICommand? GetCommand() => CommandSource?.Command;
- public bool CanExecute(object parameter) =>
- CommandSource.Command?.CanExecute(CommandSource.CommandParameter) == true
+ public bool CanExecute(object? parameter) =>
+ CommandSource?.Command?.CanExecute(CommandSource.CommandParameter) == true
&& CommandSource.IsEffectivelyEnabled;
- public void Execute(object parameter) =>
- GetCommand()?.Execute(CommandSource.CommandParameter);
+ public void Execute(object? parameter) =>
+ GetCommand()?.Execute(CommandSource?.CommandParameter);
#pragma warning disable 67 // Event not used
- public event EventHandler CanExecuteChanged;
+ public event EventHandler? CanExecuteChanged;
#pragma warning restore 67
}
@@ -37,12 +37,12 @@ namespace Avalonia.Controls
class Manager
{
private readonly IControl _control;
- private TopLevel _root;
- private IDisposable _parentSub;
- private IDisposable _hotkeySub;
- private KeyGesture _hotkey;
+ private TopLevel? _root;
+ private IDisposable? _parentSub;
+ private IDisposable? _hotkeySub;
+ private KeyGesture? _hotkey;
private readonly HotkeyCommandWrapper _wrapper;
- private KeyBinding _binding;
+ private KeyBinding? _binding;
public Manager(IControl control)
{
@@ -56,14 +56,14 @@ namespace Avalonia.Controls
_parentSub = AncestorFinder.Create(_control).Subscribe(OnParentChanged);
}
- private void OnParentChanged(TopLevel control)
+ private void OnParentChanged(TopLevel? control)
{
Unregister();
_root = control;
Register();
}
- private void OnHotkeyChanged(KeyGesture hotkey)
+ private void OnHotkeyChanged(KeyGesture? hotkey)
{
if (hotkey == null)
//Subscription will be recreated by static property watcher
@@ -95,8 +95,8 @@ namespace Avalonia.Controls
void Stop()
{
Unregister();
- _parentSub.Dispose();
- _hotkeySub.Dispose();
+ _parentSub?.Dispose();
+ _hotkeySub?.Dispose();
}
}
@@ -118,6 +118,6 @@ namespace Avalonia.Controls
});
}
public static void SetHotKey(AvaloniaObject target, KeyGesture value) => target.SetValue(HotKeyProperty, value);
- public static KeyGesture GetHotKey(AvaloniaObject target) => target.GetValue(HotKeyProperty);
+ public static KeyGesture? GetHotKey(AvaloniaObject target) => target.GetValue(HotKeyProperty);
}
}
diff --git a/src/Avalonia.Controls/IContentControl.cs b/src/Avalonia.Controls/IContentControl.cs
index 889771b355..d28b0afb25 100644
--- a/src/Avalonia.Controls/IContentControl.cs
+++ b/src/Avalonia.Controls/IContentControl.cs
@@ -12,12 +12,12 @@ namespace Avalonia.Controls
///
/// Gets or sets the content to display.
///
- object Content { get; set; }
+ object? Content { get; set; }
///
/// Gets or sets the data template used to display the content of the control.
///
- IDataTemplate ContentTemplate { get; set; }
+ IDataTemplate? ContentTemplate { get; set; }
///
/// Gets or sets the horizontal alignment of the content within the control.
@@ -29,4 +29,4 @@ namespace Avalonia.Controls
///
VerticalAlignment VerticalContentAlignment { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs
index 598e6b6f4e..b501bc15a7 100644
--- a/src/Avalonia.Controls/IControl.cs
+++ b/src/Avalonia.Controls/IControl.cs
@@ -3,8 +3,6 @@ using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls
{
///
diff --git a/src/Avalonia.Controls/IHeadered.cs b/src/Avalonia.Controls/IHeadered.cs
index c208e21c97..8c1b5cfb1b 100644
--- a/src/Avalonia.Controls/IHeadered.cs
+++ b/src/Avalonia.Controls/IHeadered.cs
@@ -8,6 +8,6 @@ namespace Avalonia.Controls
///
/// Gets or set the header.
///
- object Header { get; set; }
+ object? Header { get; set; }
}
}
diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs
index 426f265084..a3200d2b1b 100644
--- a/src/Avalonia.Controls/IMenuElement.cs
+++ b/src/Avalonia.Controls/IMenuElement.cs
@@ -1,8 +1,6 @@
using System.Collections.Generic;
using Avalonia.Input;
-#nullable enable
-
namespace Avalonia.Controls
{
///
diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs
index d92625218a..35e36eb0f4 100644
--- a/src/Avalonia.Controls/IMenuItem.cs
+++ b/src/Avalonia.Controls/IMenuItem.cs
@@ -1,6 +1,4 @@
-#nullable enable
-
-namespace Avalonia.Controls
+namespace Avalonia.Controls
{
///
/// Represents a .
diff --git a/src/Avalonia.Controls/IScrollAnchorProvider.cs b/src/Avalonia.Controls/IScrollAnchorProvider.cs
index 7ba02e99ea..b3ce7ef9e8 100644
--- a/src/Avalonia.Controls/IScrollAnchorProvider.cs
+++ b/src/Avalonia.Controls/IScrollAnchorProvider.cs
@@ -1,6 +1,4 @@
-#nullable enable
-
-namespace Avalonia.Controls
+namespace Avalonia.Controls
{
///
/// Specifies a contract for a scrolling control that supports scroll anchoring.
diff --git a/src/Avalonia.Controls/IVirtualizingPanel.cs b/src/Avalonia.Controls/IVirtualizingPanel.cs
index 8c4dc80934..604a62dcb9 100644
--- a/src/Avalonia.Controls/IVirtualizingPanel.cs
+++ b/src/Avalonia.Controls/IVirtualizingPanel.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Controls
/// Note that this property may remain null if the panel is added to a control that does
/// not act as a virtualizing controller.
///
- IVirtualizingController Controller { get; set; }
+ IVirtualizingController? Controller { get; set; }
///
/// Gets a value indicating whether the panel is full.
diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs
index c448729643..3d67880638 100644
--- a/src/Avalonia.Controls/Image.cs
+++ b/src/Avalonia.Controls/Image.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
@@ -33,6 +35,7 @@ namespace Avalonia.Controls
{
AffectsRender(SourceProperty, StretchProperty, StretchDirectionProperty);
AffectsMeasure(SourceProperty, StretchProperty, StretchDirectionProperty);
+ AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Image);
}
///
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 1ff49326b6..0cd72dc91c 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
@@ -32,8 +33,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly DirectProperty ItemsProperty =
- AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v);
+ public static readonly DirectProperty ItemsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v);
///
/// Defines the property.
@@ -50,13 +51,13 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty ItemTemplateProperty =
- AvaloniaProperty.Register(nameof(ItemTemplate));
+ public static readonly StyledProperty ItemTemplateProperty =
+ AvaloniaProperty.Register(nameof(ItemTemplate));
- private IEnumerable _items = new AvaloniaList();
+ private IEnumerable? _items = new AvaloniaList();
private int _itemCount;
- private IItemContainerGenerator _itemContainerGenerator;
- private EventHandler _childIndexChanged;
+ private IItemContainerGenerator? _itemContainerGenerator;
+ private EventHandler? _childIndexChanged;
///
/// Initializes static members of the class.
@@ -87,13 +88,10 @@ namespace Avalonia.Controls
{
_itemContainerGenerator = CreateItemContainerGenerator();
- if (_itemContainerGenerator != null)
- {
- _itemContainerGenerator.ItemTemplate = ItemTemplate;
- _itemContainerGenerator.Materialized += (_, e) => OnContainersMaterialized(e);
- _itemContainerGenerator.Dematerialized += (_, e) => OnContainersDematerialized(e);
- _itemContainerGenerator.Recycled += (_, e) => OnContainersRecycled(e);
- }
+ _itemContainerGenerator.ItemTemplate = ItemTemplate;
+ _itemContainerGenerator.Materialized += (_, e) => OnContainersMaterialized(e);
+ _itemContainerGenerator.Dematerialized += (_, e) => OnContainersDematerialized(e);
+ _itemContainerGenerator.Recycled += (_, e) => OnContainersRecycled(e);
}
return _itemContainerGenerator;
@@ -104,7 +102,7 @@ namespace Avalonia.Controls
/// Gets or sets the items to display.
///
[Content]
- public IEnumerable Items
+ public IEnumerable? Items
{
get { return _items; }
set { SetAndRaise(ItemsProperty, ref _items, value); }
@@ -131,7 +129,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the data template used to display the items in the control.
///
- public IDataTemplate ItemTemplate
+ public IDataTemplate? ItemTemplate
{
get { return GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
@@ -140,13 +138,15 @@ namespace Avalonia.Controls
///
/// Gets the items presenter control.
///
- public IItemsPresenter Presenter
+ public IItemsPresenter? Presenter
{
get;
protected set;
}
- event EventHandler IChildIndexProvider.ChildIndexChanged
+ private protected bool WrapFocus { get; set; }
+
+ event EventHandler? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
@@ -161,7 +161,7 @@ namespace Avalonia.Controls
}
Presenter = presenter;
- ItemContainerGenerator.Clear();
+ ItemContainerGenerator?.Clear();
if (Presenter is IChildIndexProvider innerProvider)
{
@@ -189,11 +189,11 @@ namespace Avalonia.Controls
/// The collection.
/// The index.
/// The item at the given index or null if the index is out of bounds.
- protected static object ElementAt(IEnumerable items, int index)
+ protected static object? ElementAt(IEnumerable? items, int index)
{
if (index != -1 && index < items.Count())
{
- return items.ElementAt(index) ?? null;
+ return items!.ElementAt(index) ?? null;
}
else
{
@@ -207,7 +207,7 @@ namespace Avalonia.Controls
/// The collection.
/// The item.
/// The index of the item or -1 if the item was not found.
- protected static int IndexOf(IEnumerable items, object item)
+ protected static int IndexOf(IEnumerable? items, object item)
{
if (items != null && item != null)
{
@@ -240,14 +240,8 @@ namespace Avalonia.Controls
/// Creates the for the control.
///
///
- /// An or null.
+ /// An .
///
- ///
- /// Certain controls such as don't actually create item
- /// containers; however they want it to be ItemsControls so that they have an Items
- /// property etc. In this case, a derived class can override this method to return null
- /// in order to disable the creation of item containers.
- ///
protected virtual IItemContainerGenerator CreateItemContainerGenerator()
{
return new ItemContainerGenerator(this);
@@ -282,7 +276,7 @@ namespace Avalonia.Controls
{
// If the item is its own container, then it will be removed from the logical tree
// when it is removed from the Items collection.
- if (container?.ContainerControl != container?.Item)
+ if (container.ContainerControl != container.Item)
{
LogicalChildren.Remove(container.ContainerControl);
}
@@ -311,20 +305,20 @@ namespace Avalonia.Controls
var container = Presenter?.Panel as INavigableContainer;
if (container == null ||
- focus.Current == null ||
+ focus?.Current == null ||
direction == null ||
direction.Value.IsTab())
{
return;
}
- IVisual current = focus.Current;
+ IVisual? current = focus.Current;
while (current != null)
{
if (current.VisualParent == container && current is IInputElement inputElement)
{
- IInputElement next = GetNextControl(container, direction.Value, inputElement, false);
+ var next = GetNextControl(container, direction.Value, inputElement, WrapFocus);
if (next != null)
{
@@ -342,6 +336,11 @@ namespace Avalonia.Controls
base.OnKeyDown(e);
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ItemsControlAutomationPeer(this);
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -406,7 +405,7 @@ namespace Avalonia.Controls
/// Given a collection of items, adds those that are controls to the logical children.
///
/// The items.
- private void AddControlItemsToLogicalChildren(IEnumerable items)
+ private void AddControlItemsToLogicalChildren(IEnumerable? items)
{
var toAdd = new List();
@@ -430,7 +429,7 @@ namespace Avalonia.Controls
/// Given a collection of items, removes those that are controls to from logical children.
///
/// The items.
- private void RemoveControlItemsFromLogicalChildren(IEnumerable items)
+ private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
{
var toRemove = new List();
@@ -454,7 +453,7 @@ namespace Avalonia.Controls
/// Subscribes to an collection.
///
/// The items collection.
- private void SubscribeToItems(IEnumerable items)
+ private void SubscribeToItems(IEnumerable? items)
{
if (items is INotifyCollectionChanged incc)
{
@@ -470,7 +469,7 @@ namespace Avalonia.Controls
{
if (_itemContainerGenerator != null)
{
- _itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue;
+ _itemContainerGenerator.ItemTemplate = (IDataTemplate?)e.NewValue;
// TODO: Rebuild the item containers.
}
}
@@ -497,13 +496,13 @@ namespace Avalonia.Controls
PseudoClasses.Set(":singleitem", itemCount == 1);
}
- protected static IInputElement GetNextControl(
+ protected static IInputElement? GetNextControl(
INavigableContainer container,
NavigationDirection direction,
- IInputElement from,
+ IInputElement? from,
bool wrap)
{
- IInputElement result;
+ IInputElement? result;
var c = from;
do
@@ -525,7 +524,7 @@ namespace Avalonia.Controls
return null;
}
- private void PresenterChildIndexChanged(object sender, ChildIndexChangedEventArgs e)
+ private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)
{
_childIndexChanged?.Invoke(this, e);
}
diff --git a/src/Avalonia.Controls/ItemsSourceView.cs b/src/Avalonia.Controls/ItemsSourceView.cs
index c2d20495ef..945035ab5e 100644
--- a/src/Avalonia.Controls/ItemsSourceView.cs
+++ b/src/Avalonia.Controls/ItemsSourceView.cs
@@ -7,12 +7,9 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Avalonia.Controls.Utils;
-#nullable enable
-
namespace Avalonia.Controls
{
///
@@ -196,7 +193,7 @@ namespace Avalonia.Controls
_collectionChanged?.Invoke(this, args);
}
- private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
OnItemsSourceChanged(e);
}
@@ -239,8 +236,7 @@ namespace Avalonia.Controls
///
/// The index.
/// The item.
- [return: MaybeNull]
- public new T GetAt(int index) => (T)Inner[index];
+ public new T GetAt(int index) => (T)Inner[index]!;
public IEnumerator GetEnumerator() => Inner.Cast().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Inner.GetEnumerator();
diff --git a/src/Avalonia.Controls/Label.cs b/src/Avalonia.Controls/Label.cs
index 9d5eda464f..5c8a6e0a5b 100644
--- a/src/Avalonia.Controls/Label.cs
+++ b/src/Avalonia.Controls/Label.cs
@@ -18,19 +18,19 @@ namespace Avalonia.Controls
///
/// Defines the Direct property
///
- public static readonly DirectProperty
public class LayoutTransformControl : Decorator
{
- public static readonly StyledProperty LayoutTransformProperty =
- AvaloniaProperty.Register(nameof(LayoutTransform));
+ public static readonly StyledProperty LayoutTransformProperty =
+ AvaloniaProperty.Register(nameof(LayoutTransform));
public static readonly StyledProperty UseRenderTransformProperty =
AvaloniaProperty.Register(nameof(UseRenderTransform));
@@ -37,7 +37,7 @@ namespace Avalonia.Controls
///
/// Gets or sets a graphics transformation that should apply to this element when layout is performed.
///
- public ITransform LayoutTransform
+ public ITransform? LayoutTransform
{
get { return GetValue(LayoutTransformProperty); }
set { SetValue(LayoutTransformProperty, value); }
@@ -52,7 +52,7 @@ namespace Avalonia.Controls
set { SetValue(UseRenderTransformProperty, value); }
}
- public IControl TransformRoot => Child;
+ public IControl? TransformRoot => Child;
///
/// Provides the behavior for the "Arrange" pass of layout.
@@ -146,7 +146,7 @@ namespace Avalonia.Controls
return transformedDesiredSize;
}
- IDisposable _renderTransformChangedEvent;
+ IDisposable? _renderTransformChangedEvent;
private void OnUseRenderTransformPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
@@ -158,7 +158,7 @@ namespace Avalonia.Controls
// workaround.
var target = e.Sender as LayoutTransformControl;
- var shouldUseRenderTransform = (bool)e.NewValue;
+ var shouldUseRenderTransform = (bool)e.NewValue!;
if (target != null)
{
if (shouldUseRenderTransform)
@@ -217,7 +217,7 @@ namespace Avalonia.Controls
/// Transformation matrix corresponding to _matrixTransform.
///
private Matrix _transformation;
- private IDisposable _transformChangedEvent = null;
+ private IDisposable? _transformChangedEvent = null;
///
/// Returns true if Size a is smaller than Size b in either dimension.
diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs
index 43b4908482..9b7ae0d324 100644
--- a/src/Avalonia.Controls/ListBox.cs
+++ b/src/Avalonia.Controls/ListBox.cs
@@ -23,13 +23,13 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly DirectProperty ScrollProperty =
- AvaloniaProperty.RegisterDirect(nameof(Scroll), o => o.Scroll);
+ public static readonly DirectProperty ScrollProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Scroll), o => o.Scroll);
///
/// Defines the property.
///
- public static readonly new DirectProperty SelectedItemsProperty =
+ public static readonly new DirectProperty SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty;
///
@@ -50,7 +50,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty VirtualizationModeProperty =
ItemsPresenter.VirtualizationModeProperty.AddOwner();
- private IScrollable _scroll;
+ private IScrollable? _scroll;
///
/// Initializes static members of the class.
@@ -64,14 +64,14 @@ namespace Avalonia.Controls
///
/// Gets the scroll information for the .
///
- public IScrollable Scroll
+ public IScrollable? Scroll
{
get { return _scroll; }
private set { SetAndRaise(ScrollProperty, ref _scroll, value); }
}
///
- public new IList SelectedItems
+ public new IList? SelectedItems
{
get => base.SelectedItems;
set => base.SelectedItems = value;
diff --git a/src/Avalonia.Controls/ListBoxItem.cs b/src/Avalonia.Controls/ListBoxItem.cs
index 4fe5f4de40..66a46cab4a 100644
--- a/src/Avalonia.Controls/ListBoxItem.cs
+++ b/src/Avalonia.Controls/ListBoxItem.cs
@@ -1,6 +1,6 @@
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
-using Avalonia.Input;
namespace Avalonia.Controls
{
@@ -34,5 +34,10 @@ namespace Avalonia.Controls
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ListItemAutomationPeer(this);
+ }
}
}
diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs
index f59cef8223..ad64c61ebe 100644
--- a/src/Avalonia.Controls/MaskedTextBox.cs
+++ b/src/Avalonia.Controls/MaskedTextBox.cs
@@ -8,8 +8,6 @@ using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Styling;
-#nullable enable
-
namespace Avalonia.Controls
{
public class MaskedTextBox : TextBox, IStyleable
@@ -241,7 +239,7 @@ namespace Avalonia.Controls
switch (e.Key)
{
case Key.Delete:
- if (CaretIndex < Text.Length)
+ if (CaretIndex < Text?.Length)
{
if (MaskProvider.RemoveAt(CaretIndex))
{
@@ -286,7 +284,7 @@ namespace Avalonia.Controls
{
void UpdateMaskProvider()
{
- MaskProvider = new MaskedTextProvider(Mask, Culture, true, PromptChar, PasswordChar, AsciiOnly) { ResetOnSpace = ResetOnSpace, ResetOnPrompt = ResetOnPrompt };
+ MaskProvider = new MaskedTextProvider(Mask!, Culture, true, PromptChar, PasswordChar, AsciiOnly) { ResetOnSpace = ResetOnSpace, ResetOnPrompt = ResetOnPrompt };
if (Text != null)
{
MaskProvider.Set(Text);
@@ -383,11 +381,11 @@ namespace Avalonia.Controls
}
}
- if (CaretIndex < Text.Length)
+ if (CaretIndex < Text?.Length)
{
CaretIndex = GetNextCharacterPosition(CaretIndex);
- if (MaskProvider.InsertAt(e.Text, CaretIndex))
+ if (MaskProvider.InsertAt(e.Text!, CaretIndex))
{
CaretIndex++;
}
diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs
index 706be376a9..611811f170 100644
--- a/src/Avalonia.Controls/Menu.cs
+++ b/src/Avalonia.Controls/Menu.cs
@@ -1,3 +1,5 @@
+using Avalonia.Automation;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@@ -5,8 +7,6 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
-#nullable enable
-
namespace Avalonia.Controls
{
///
@@ -37,6 +37,8 @@ namespace Avalonia.Controls
static Menu()
{
ItemsPanelProperty.OverrideDefaultValue(typeof(Menu), DefaultPanel);
+ AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue
/// The event sender.
/// The event args.
- private void PopupClosed(object sender, EventArgs e)
+ private void PopupClosed(object? sender, EventArgs e)
{
SelectedItem = null;
}
diff --git a/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs b/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
index 2c3dac8482..4cc6a20082 100644
--- a/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
+++ b/src/Avalonia.Controls/MenuItemAccessKeyHandler.cs
@@ -19,7 +19,7 @@ namespace Avalonia.Controls
///
/// The window to which the handler belongs.
///
- private IInputRoot _owner;
+ private IInputRoot? _owner;
///
/// Gets or sets the window's main menu.
@@ -27,7 +27,7 @@ namespace Avalonia.Controls
///
/// This property is ignored as a menu item cannot have a main menu.
///
- public IMainMenu MainMenu { get; set; }
+ public IMainMenu? MainMenu { get; set; }
///
/// Sets the owner of the access key handler.
@@ -38,7 +38,7 @@ namespace Avalonia.Controls
///
public void SetOwner(IInputRoot owner)
{
- Contract.Requires(owner != null);
+ _ = owner ?? throw new ArgumentNullException(nameof(owner));
if (_owner != null)
{
@@ -84,7 +84,7 @@ namespace Avalonia.Controls
///
/// The event sender.
/// The event args.
- protected virtual void OnTextInput(object sender, TextInputEventArgs e)
+ protected virtual void OnTextInput(object? sender, TextInputEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.Text))
{
diff --git a/src/Avalonia.Controls/Mixins/SelectableMixin.cs b/src/Avalonia.Controls/Mixins/SelectableMixin.cs
index c9e2b684cb..fa964c0056 100644
--- a/src/Avalonia.Controls/Mixins/SelectableMixin.cs
+++ b/src/Avalonia.Controls/Mixins/SelectableMixin.cs
@@ -40,7 +40,7 @@ namespace Avalonia.Controls.Mixins
public static void Attach(AvaloniaProperty isSelected)
where TControl : class, IControl
{
- Contract.Requires(isSelected != null);
+ _ = isSelected ?? throw new ArgumentNullException(nameof(isSelected));
isSelected.Changed.Subscribe(x =>
{
diff --git a/src/Avalonia.Controls/NativeControlHost.cs b/src/Avalonia.Controls/NativeControlHost.cs
index e18b7a5ba2..191b60f303 100644
--- a/src/Avalonia.Controls/NativeControlHost.cs
+++ b/src/Avalonia.Controls/NativeControlHost.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using Avalonia.Controls.Platform;
using Avalonia.Platform;
using Avalonia.Threading;
@@ -9,10 +10,10 @@ namespace Avalonia.Controls
{
public class NativeControlHost : Control
{
- private TopLevel _currentRoot;
- private INativeControlHostImpl _currentHost;
- private INativeControlHostControlTopLevelAttachment _attachment;
- private IPlatformHandle _nativeControlHandle;
+ private TopLevel? _currentRoot;
+ private INativeControlHostImpl? _currentHost;
+ private INativeControlHostControlTopLevelAttachment? _attachment;
+ private IPlatformHandle? _nativeControlHandle;
private bool _queuedForDestruction;
private bool _queuedForMoveResize;
private readonly List _propertyChangedSubscriptions = new List();
@@ -35,7 +36,7 @@ namespace Avalonia.Controls
UpdateHost();
}
- private void PropertyChangedHandler(object sender, AvaloniaPropertyChangedEventArgs e)
+ private void PropertyChangedHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.IsEffectiveValueChange && (e.Property == BoundsProperty || e.Property == IsVisibleProperty))
EnqueueForMoveResize();
@@ -58,9 +59,8 @@ namespace Avalonia.Controls
{
_queuedForMoveResize = false;
_currentHost = (_currentRoot?.PlatformImpl as ITopLevelImplWithNativeControlHost)?.NativeControlHost;
- var needsAttachment = _currentHost != null;
- if (needsAttachment)
+ if (_currentHost != null)
{
// If there is an existing attachment, ensure that we are attached to the proper host or destroy the attachment
if (_attachment != null && _attachment.AttachedTo != _currentHost)
@@ -119,6 +119,8 @@ namespace Avalonia.Controls
private Rect? GetAbsoluteBounds()
{
+ Debug.Assert(_currentRoot is not null);
+
var bounds = Bounds;
var position = this.TranslatePoint(default, _currentRoot);
if (position == null)
@@ -140,9 +142,8 @@ namespace Avalonia.Controls
return false;
var bounds = GetAbsoluteBounds();
- var needsShow = IsEffectivelyVisible && bounds.HasValue;
- if (needsShow)
+ if (IsEffectivelyVisible && bounds.HasValue)
{
if (bounds.Value.IsEmpty)
return false;
diff --git a/src/Avalonia.Controls/NativeMenu.Export.cs b/src/Avalonia.Controls/NativeMenu.Export.cs
index 6bfe5ebc82..fe650ab41e 100644
--- a/src/Avalonia.Controls/NativeMenu.Export.cs
+++ b/src/Avalonia.Controls/NativeMenu.Export.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Controls
class NativeMenuInfo
{
public bool ChangingIsExported { get; set; }
- public ITopLevelNativeMenuExporter Exporter { get; }
+ public ITopLevelNativeMenuExporter? Exporter { get; }
public NativeMenuInfo(TopLevel target)
{
diff --git a/src/Avalonia.Controls/NativeMenu.cs b/src/Avalonia.Controls/NativeMenu.cs
index 58ee99722f..5ff4148e5a 100644
--- a/src/Avalonia.Controls/NativeMenu.cs
+++ b/src/Avalonia.Controls/NativeMenu.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Controls
{
private readonly AvaloniaList _items =
new AvaloniaList { ResetBehavior = ResetBehavior.Remove };
- private NativeMenuItem _parent;
+ private NativeMenuItem? _parent;
[Content]
public IList Items => _items;
@@ -23,7 +23,7 @@ namespace Avalonia.Controls
/// Use this event to add, remove or modify menu items before a menu is
/// shown or a hotkey is pressed.
///
- public event EventHandler NeedsUpdate;
+ public event EventHandler? NeedsUpdate;
///
/// Raised before the menu is opened.
@@ -31,7 +31,7 @@ namespace Avalonia.Controls
///
/// Do not update the menu in this event; use .
///
- public event EventHandler Opening;
+ public event EventHandler? Opening;
///
/// Raised after the menu is closed.
@@ -39,7 +39,7 @@ namespace Avalonia.Controls
///
/// Do not update the menu in this event; use .
///
- public event EventHandler Closed;
+ public event EventHandler? Closed;
public NativeMenu()
{
@@ -68,7 +68,7 @@ namespace Avalonia.Controls
throw new InvalidOperationException("NativeMenuItem already has a parent");
}
- private void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void ItemsChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
foreach (NativeMenuItemBase i in e.OldItems)
@@ -78,10 +78,10 @@ namespace Avalonia.Controls
i.Parent = this;
}
- public static readonly DirectProperty ParentProperty =
- AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v);
+ public static readonly DirectProperty ParentProperty =
+ AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v);
- public NativeMenuItem Parent
+ public NativeMenuItem? Parent
{
get => _parent;
set => SetAndRaise(ParentProperty, ref _parent, value);
diff --git a/src/Avalonia.Controls/NativeMenuBar.cs b/src/Avalonia.Controls/NativeMenuBar.cs
index 5d2e1087a7..ae473f8500 100644
--- a/src/Avalonia.Controls/NativeMenuBar.cs
+++ b/src/Avalonia.Controls/NativeMenuBar.cs
@@ -27,9 +27,9 @@ namespace Avalonia.Controls
menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable);
}
- private static void OnMenuItemClick(object sender, RoutedEventArgs e)
+ private static void OnMenuItemClick(object? sender, RoutedEventArgs e)
{
- (((MenuItem)sender).DataContext as INativeMenuItemExporterEventsImplBridge)?.RaiseClicked();
+ (((MenuItem)sender!).DataContext as INativeMenuItemExporterEventsImplBridge)?.RaiseClicked();
}
}
}
diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs
index 4d048f0fb0..265e7119eb 100644
--- a/src/Avalonia.Controls/NativeMenuItem.cs
+++ b/src/Avalonia.Controls/NativeMenuItem.cs
@@ -9,23 +9,23 @@ namespace Avalonia.Controls
{
public class NativeMenuItem : NativeMenuItemBase, INativeMenuItemExporterEventsImplBridge
{
- private string _header;
- private KeyGesture _gesture;
+ private string? _header;
+ private KeyGesture? _gesture;
private bool _isEnabled = true;
- private ICommand _command;
+ private ICommand? _command;
private bool _isChecked = false;
private NativeMenuItemToggleType _toggleType;
- private IBitmap _icon;
+ private IBitmap? _icon;
private readonly CanExecuteChangedSubscriber _canExecuteChangedSubscriber;
- private NativeMenu _menu;
+ private NativeMenu? _menu;
static NativeMenuItem()
{
MenuProperty.Changed.Subscribe(args =>
{
var item = (NativeMenuItem)args.Sender;
- var value = args.NewValue.GetValueOrDefault();
+ var value = args.NewValue.GetValueOrDefault()!;
if (value.Parent != null && value.Parent != item)
throw new InvalidOperationException("NativeMenu already has a parent");
value.Parent = item;
@@ -59,44 +59,44 @@ namespace Avalonia.Controls
Header = header;
}
- public static readonly DirectProperty MenuProperty =
- AvaloniaProperty.RegisterDirect(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v);
+ public static readonly DirectProperty MenuProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v);
[Content]
- public NativeMenu Menu
+ public NativeMenu? Menu
{
get => _menu;
set
{
- if (value.Parent != null && value.Parent != this)
+ if (value != null && value.Parent != null && value.Parent != this)
throw new InvalidOperationException("NativeMenu already has a parent");
SetAndRaise(MenuProperty, ref _menu, value);
}
}
- public static readonly DirectProperty IconProperty =
- AvaloniaProperty.RegisterDirect(nameof(Icon), o => o.Icon, (o, v) => o.Icon = v);
+ public static readonly DirectProperty IconProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Icon), o => o.Icon, (o, v) => o.Icon = v);
- public IBitmap Icon
+ public IBitmap? Icon
{
get => _icon;
set => SetAndRaise(IconProperty, ref _icon, value);
}
- public static readonly DirectProperty HeaderProperty =
- AvaloniaProperty.RegisterDirect(nameof(Header), o => o.Header, (o, v) => o.Header = v);
+ public static readonly DirectProperty HeaderProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Header), o => o.Header, (o, v) => o.Header = v);
- public string Header
+ public string? Header
{
get => _header;
set => SetAndRaise(HeaderProperty, ref _header, value);
}
- public static readonly DirectProperty GestureProperty =
- AvaloniaProperty.RegisterDirect(nameof(Gesture), o => o.Gesture, (o, v) => o.Gesture = v);
+ public static readonly DirectProperty GestureProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Gesture), o => o.Gesture, (o, v) => o.Gesture = v);
- public KeyGesture Gesture
+ public KeyGesture? Gesture
{
get => _gesture;
set => SetAndRaise(GestureProperty, ref _gesture, value);
@@ -126,7 +126,7 @@ namespace Avalonia.Controls
set => SetAndRaise(ToggleTypeProperty, ref _toggleType, value);
}
- public static readonly DirectProperty CommandProperty =
+ public static readonly DirectProperty CommandProperty =
Button.CommandProperty.AddOwner(
menuItem => menuItem.Command,
(menuItem, command) => menuItem.Command = command,
@@ -135,7 +135,7 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty CommandParameterProperty =
+ public static readonly StyledProperty CommandParameterProperty =
Button.CommandParameterProperty.AddOwner
- public object CommandParameter
+ public object? CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
@@ -184,7 +184,7 @@ namespace Avalonia.Controls
///
/// Occurs when a is clicked.
///
- public event EventHandler Click;
+ public event EventHandler? Click;
[Obsolete("Use Click event.")]
public event EventHandler Clicked
diff --git a/src/Avalonia.Controls/NativeMenuItemBase.cs b/src/Avalonia.Controls/NativeMenuItemBase.cs
index 47eb86cdc3..a5e3308e3b 100644
--- a/src/Avalonia.Controls/NativeMenuItemBase.cs
+++ b/src/Avalonia.Controls/NativeMenuItemBase.cs
@@ -4,17 +4,17 @@ namespace Avalonia.Controls
{
public class NativeMenuItemBase : AvaloniaObject
{
- private NativeMenu _parent;
+ private NativeMenu? _parent;
internal NativeMenuItemBase()
{
}
- public static readonly DirectProperty ParentProperty =
- AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v);
+ public static readonly DirectProperty ParentProperty =
+ AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v);
- public NativeMenu Parent
+ public NativeMenu? Parent
{
get => _parent;
set => SetAndRaise(ParentProperty, ref _parent, value);
diff --git a/src/Avalonia.Controls/Notifications/INotification.cs b/src/Avalonia.Controls/Notifications/INotification.cs
index dc69a12887..fa08233097 100644
--- a/src/Avalonia.Controls/Notifications/INotification.cs
+++ b/src/Avalonia.Controls/Notifications/INotification.cs
@@ -10,12 +10,12 @@ namespace Avalonia.Controls.Notifications
///
/// Gets the Title of the notification.
///
- string Title { get; }
+ string? Title { get; }
///
/// Gets the notification message.
///
- string Message { get; }
+ string? Message { get; }
///
/// Gets the of the notification.
@@ -31,11 +31,11 @@ namespace Avalonia.Controls.Notifications
///
/// Gets an Action to be run when the notification is clicked.
///
- Action OnClick { get; }
+ Action? OnClick { get; }
///
/// Gets an Action to be run when the notification is closed.
///
- Action OnClose { get; }
+ Action? OnClose { get; }
}
}
diff --git a/src/Avalonia.Controls/Notifications/Notification.cs b/src/Avalonia.Controls/Notifications/Notification.cs
index 9bb514971b..376df175f3 100644
--- a/src/Avalonia.Controls/Notifications/Notification.cs
+++ b/src/Avalonia.Controls/Notifications/Notification.cs
@@ -21,12 +21,12 @@ namespace Avalonia.Controls.Notifications
/// Use for notifications that will remain open.
/// An Action to call when the notification is clicked.
/// An Action to call when the notification is closed.
- public Notification(string title,
- string message,
+ public Notification(string? title,
+ string? message,
NotificationType type = NotificationType.Information,
TimeSpan? expiration = null,
- Action onClick = null,
- Action onClose = null)
+ Action? onClick = null,
+ Action? onClose = null)
{
Title = title;
Message = message;
@@ -37,10 +37,10 @@ namespace Avalonia.Controls.Notifications
}
///
- public string Title { get; private set; }
+ public string? Title { get; private set; }
///
- public string Message { get; private set; }
+ public string? Message { get; private set; }
///
public NotificationType Type { get; private set; }
@@ -49,9 +49,9 @@ namespace Avalonia.Controls.Notifications
public TimeSpan Expiration { get; private set; }
///
- public Action OnClick { get; private set; }
+ public Action? OnClick { get; private set; }
///
- public Action OnClose { get; private set; }
+ public Action? OnClose { get; private set; }
}
}
diff --git a/src/Avalonia.Controls/Notifications/NotificationCard.cs b/src/Avalonia.Controls/Notifications/NotificationCard.cs
index ad883c8076..a103adf185 100644
--- a/src/Avalonia.Controls/Notifications/NotificationCard.cs
+++ b/src/Avalonia.Controls/Notifications/NotificationCard.cs
@@ -37,8 +37,11 @@ namespace Avalonia.Controls.Notifications
RaiseEvent(new RoutedEventArgs(NotificationClosedEvent));
});
+ // Disabling nullable checking because of https://github.com/dotnet/reactive/issues/1525
+#pragma warning disable CS8620
this.GetObservable(ContentProperty)
.OfType()
+#pragma warning restore CS8620
.Subscribe(x =>
{
switch (x.Type)
@@ -102,7 +105,7 @@ namespace Avalonia.Controls.Notifications
///
/// Raised when the has closed.
///
- public event EventHandler NotificationClosed
+ public event EventHandler? NotificationClosed
{
add { AddHandler(NotificationClosedEvent, value); }
remove { RemoveHandler(NotificationClosedEvent, value); }
@@ -110,13 +113,13 @@ namespace Avalonia.Controls.Notifications
public static bool GetCloseOnClick(Button obj)
{
- Contract.Requires(obj != null);
+ _ = obj ?? throw new ArgumentNullException(nameof(obj));
return (bool)obj.GetValue(CloseOnClickProperty);
}
public static void SetCloseOnClick(Button obj, bool value)
{
- Contract.Requires(obj != null);
+ _ = obj ?? throw new ArgumentNullException(nameof(obj));
obj.SetValue(CloseOnClickProperty, value);
}
@@ -129,7 +132,7 @@ namespace Avalonia.Controls.Notifications
private static void OnCloseOnClickPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{
var button = (Button)d;
- var value = (bool)e.NewValue;
+ var value = (bool)e.NewValue!;
if (value)
{
button.Click += Button_Click;
@@ -143,10 +146,10 @@ namespace Avalonia.Controls.Notifications
///
/// Called when a button inside the Notification is clicked.
///
- private static void Button_Click(object sender, RoutedEventArgs e)
+ private static void Button_Click(object? sender, RoutedEventArgs e)
{
var btn = sender as ILogical;
- var notification = btn.GetLogicalAncestors().OfType().FirstOrDefault();
+ var notification = btn?.GetLogicalAncestors().OfType().FirstOrDefault();
notification?.Close();
}
diff --git a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
index 8f5c6faf40..9499995da3 100644
--- a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
+++ b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls.Notifications
[PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")]
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest
{
- private IList _items;
+ private IList? _items;
///
/// Defines the property.
@@ -107,7 +107,7 @@ namespace Avalonia.Controls.Notifications
{
notification.OnClose?.Invoke();
- _items.Remove(sender);
+ _items?.Remove(sender);
};
}
@@ -121,9 +121,9 @@ namespace Avalonia.Controls.Notifications
(sender as NotificationCard)?.Close();
};
- _items.Add(notificationControl);
+ _items?.Add(notificationControl);
- if (_items.OfType().Count(i => !i.IsClosing) > MaxItems)
+ if (_items?.OfType().Count(i => !i.IsClosing) > MaxItems)
{
_items.OfType().First(i => !i.IsClosing).Close();
}
diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
index eba381e5fa..f67377b310 100644
--- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
+++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
@@ -46,15 +46,15 @@ namespace Avalonia.Controls
/// Defines the property.
///
[Obsolete]
- public static readonly DirectProperty CultureInfoProperty =
- AvaloniaProperty.RegisterDirect(nameof(CultureInfo), o => o.CultureInfo,
+ public static readonly DirectProperty CultureInfoProperty =
+ AvaloniaProperty.RegisterDirect(nameof(CultureInfo), o => o.CultureInfo,
(o, v) => o.CultureInfo = v, CultureInfo.CurrentCulture);
///
/// Defines the property.
///
- public static readonly DirectProperty NumberFormatProperty =
- AvaloniaProperty.RegisterDirect(nameof(NumberFormat), o => o.NumberFormat,
+ public static readonly DirectProperty NumberFormatProperty =
+ AvaloniaProperty.RegisterDirect(nameof(NumberFormat), o => o.NumberFormat,
(o, v) => o.NumberFormat = v, NumberFormatInfo.CurrentInfo);
///
@@ -97,8 +97,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(nameof(Text), o => o.Text, (o, v) => o.Text = v,
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Text), o => o.Text, (o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
///
@@ -111,8 +111,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty WatermarkProperty =
- AvaloniaProperty.Register(nameof(Watermark));
+ public static readonly StyledProperty WatermarkProperty =
+ AvaloniaProperty.Register(nameof(Watermark));
///
/// Defines the property.
@@ -126,27 +126,27 @@ namespace Avalonia.Controls
public static readonly StyledProperty VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner();
- private IDisposable _textBoxTextChangedSubscription;
+ private IDisposable? _textBoxTextChangedSubscription;
private decimal _value;
- private string _text;
+ private string? _text;
private bool _internalValueSet;
private bool _clipValueToMinMax;
private bool _isSyncingTextAndValueProperties;
private bool _isTextChangedFromUI;
- private CultureInfo _cultureInfo;
+ private CultureInfo? _cultureInfo;
private NumberStyles _parsingNumberStyle = NumberStyles.Any;
- private NumberFormatInfo _numberFormat;
+ private NumberFormatInfo? _numberFormat;
///
/// Gets the Spinner template part.
///
- private Spinner Spinner { get; set; }
+ private Spinner? Spinner { get; set; }
///
/// Gets the TextBox template part.
///
- private TextBox TextBox { get; set; }
+ private TextBox? TextBox { get; set; }
///
/// Gets or sets the ability to perform increment/decrement operations via the keyboard, button spinners, or mouse wheel.
@@ -188,7 +188,7 @@ namespace Avalonia.Controls
/// Gets or sets the current CultureInfo.
///
[Obsolete("CultureInfo is obsolete, please use NumberFormat instead.")]
- public CultureInfo CultureInfo
+ public CultureInfo? CultureInfo
{
get { return _cultureInfo; }
set
@@ -202,7 +202,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the current NumberFormatInfo
///
- public NumberFormatInfo NumberFormat
+ public NumberFormatInfo? NumberFormat
{
get { return _numberFormat; }
set { SetAndRaise(NumberFormatProperty, ref _numberFormat, value); }
@@ -265,7 +265,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the formatted string representation of the value.
///
- public string Text
+ public string? Text
{
get { return _text; }
set { SetAndRaise(TextProperty, ref _text, value); }
@@ -287,7 +287,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the object to use as a watermark if the is null.
///
- public string Watermark
+ public string? Watermark
{
get { return GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
@@ -414,7 +414,7 @@ namespace Avalonia.Controls
///
/// The old value.
/// The new value.
- protected virtual void OnCultureInfoChanged(CultureInfo oldValue, CultureInfo newValue)
+ protected virtual void OnCultureInfoChanged(CultureInfo? oldValue, CultureInfo? newValue)
{
if (IsInitialized)
{
@@ -427,7 +427,7 @@ namespace Avalonia.Controls
///
/// The old value.
/// The new value.
- protected virtual void OnNumberFormatChanged(NumberFormatInfo oldValue, NumberFormatInfo newValue)
+ protected virtual void OnNumberFormatChanged(NumberFormatInfo? oldValue, NumberFormatInfo? newValue)
{
if (IsInitialized)
{
@@ -440,7 +440,7 @@ namespace Avalonia.Controls
///
/// The old value.
/// The new value.
- protected virtual void OnFormatStringChanged(string oldValue, string newValue)
+ protected virtual void OnFormatStringChanged(string? oldValue, string? newValue)
{
if (IsInitialized)
{
@@ -510,7 +510,7 @@ namespace Avalonia.Controls
///
/// The old value.
/// The new value.
- protected virtual void OnTextChanged(string oldValue, string newValue)
+ protected virtual void OnTextChanged(string? oldValue, string? newValue)
{
if (IsInitialized)
{
@@ -706,8 +706,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (CultureInfo)e.OldValue;
- var newValue = (CultureInfo)e.NewValue;
+ var oldValue = (CultureInfo?)e.OldValue;
+ var newValue = (CultureInfo?)e.NewValue;
upDown.OnCultureInfoChanged(oldValue, newValue);
}
}
@@ -720,8 +720,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (NumberFormatInfo)e.OldValue;
- var newValue = (NumberFormatInfo)e.NewValue;
+ var oldValue = (NumberFormatInfo?)e.OldValue;
+ var newValue = (NumberFormatInfo?)e.NewValue;
upDown.OnNumberFormatChanged(oldValue, newValue);
}
}
@@ -734,8 +734,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (decimal)e.OldValue;
- var newValue = (decimal)e.NewValue;
+ var oldValue = (decimal)e.OldValue!;
+ var newValue = (decimal)e.NewValue!;
upDown.OnIncrementChanged(oldValue, newValue);
}
}
@@ -748,8 +748,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (string)e.OldValue;
- var newValue = (string)e.NewValue;
+ var oldValue = (string?)e.OldValue;
+ var newValue = (string?)e.NewValue;
upDown.OnFormatStringChanged(oldValue, newValue);
}
}
@@ -762,8 +762,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (bool)e.OldValue;
- var newValue = (bool)e.NewValue;
+ var oldValue = (bool)e.OldValue!;
+ var newValue = (bool)e.NewValue!;
upDown.OnIsReadOnlyChanged(oldValue, newValue);
}
}
@@ -776,8 +776,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (decimal)e.OldValue;
- var newValue = (decimal)e.NewValue;
+ var oldValue = (decimal)e.OldValue!;
+ var newValue = (decimal)e.NewValue!;
upDown.OnMaximumChanged(oldValue, newValue);
}
}
@@ -790,8 +790,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (decimal)e.OldValue;
- var newValue = (decimal)e.NewValue;
+ var oldValue = (decimal)e.OldValue!;
+ var newValue = (decimal)e.NewValue!;
upDown.OnMinimumChanged(oldValue, newValue);
}
}
@@ -804,8 +804,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (string)e.OldValue;
- var newValue = (string)e.NewValue;
+ var oldValue = (string?)e.OldValue;
+ var newValue = (string?)e.NewValue;
upDown.OnTextChanged(oldValue, newValue);
}
}
@@ -818,8 +818,8 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
- var oldValue = (decimal)e.OldValue;
- var newValue = (decimal)e.NewValue;
+ var oldValue = (decimal)e.OldValue!;
+ var newValue = (decimal)e.NewValue!;
upDown.OnValueChanged(oldValue, newValue);
}
}
@@ -883,7 +883,7 @@ namespace Avalonia.Controls
}
}
- private void OnSpinnerSpin(object sender, SpinEventArgs e)
+ private void OnSpinnerSpin(object? sender, SpinEventArgs e)
{
if (AllowSpin && !IsReadOnly)
{
@@ -914,9 +914,9 @@ namespace Avalonia.Controls
}
}
- public event EventHandler Spinned;
+ public event EventHandler? Spinned;
- private void TextBoxOnPointerPressed(object sender, PointerPressedEventArgs e)
+ private void TextBoxOnPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (e.Pointer.Captured != Spinner)
{
@@ -933,7 +933,7 @@ namespace Avalonia.Controls
///
/// Raised when the changes.
///
- public event EventHandler ValueChanged
+ public event EventHandler? ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
@@ -949,7 +949,7 @@ namespace Avalonia.Controls
///
/// If value should be updated from text.
/// The text.
- private bool SyncTextAndValueProperties(bool updateValueFromText, string text)
+ private bool SyncTextAndValueProperties(bool updateValueFromText, string? text)
{
return SyncTextAndValueProperties(updateValueFromText, text, false);
}
@@ -960,7 +960,7 @@ namespace Avalonia.Controls
/// If value should be updated from text.
/// The text.
/// Force text update.
- private bool SyncTextAndValueProperties(bool updateValueFromText, string text, bool forceTextUpdate)
+ private bool SyncTextAndValueProperties(bool updateValueFromText, string? text, bool forceTextUpdate)
{
if (_isSyncingTextAndValueProperties)
return true;
@@ -1092,7 +1092,7 @@ namespace Avalonia.Controls
///
/// Text to parse.
/// The culture info.
- private static decimal ParsePercent(string text, IFormatProvider cultureInfo)
+ private static decimal ParsePercent(string text, IFormatProvider? cultureInfo)
{
var info = NumberFormatInfo.GetInstance(cultureInfo);
text = text.Replace(info.PercentSymbol, null);
diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs
index b182f9d261..482a7fab84 100644
--- a/src/Avalonia.Controls/Panel.cs
+++ b/src/Avalonia.Controls/Panel.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty BackgroundProperty =
+ public static readonly StyledProperty BackgroundProperty =
Border.BackgroundProperty.AddOwner();
///
@@ -32,7 +32,7 @@ namespace Avalonia.Controls
AffectsRender(BackgroundProperty);
}
- private EventHandler _childIndexChanged;
+ private EventHandler? _childIndexChanged;
///
/// Initializes a new instance of the class.
@@ -51,13 +51,13 @@ namespace Avalonia.Controls
///
/// Gets or Sets Panel background brush.
///
- public IBrush Background
+ public IBrush? Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
}
- event EventHandler IChildIndexProvider.ChildIndexChanged
+ event EventHandler? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
@@ -110,34 +110,34 @@ namespace Avalonia.Controls
///
/// The event sender.
/// The event args.
- protected virtual void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ protected virtual void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
List controls;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- controls = e.NewItems.OfType().ToList();
+ controls = e.NewItems!.OfType().ToList();
LogicalChildren.InsertRange(e.NewStartingIndex, controls);
- VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems.OfType());
+ VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems!.OfType());
break;
case NotifyCollectionChangedAction.Move:
- LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
+ LogicalChildren.MoveRange(e.OldStartingIndex, e.OldItems!.Count, e.NewStartingIndex);
VisualChildren.MoveRange(e.OldStartingIndex, e.OldItems.Count, e.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
- controls = e.OldItems.OfType().ToList();
+ controls = e.OldItems!.OfType().ToList();
LogicalChildren.RemoveAll(controls);
- VisualChildren.RemoveAll(e.OldItems.OfType());
+ VisualChildren.RemoveAll(e.OldItems!.OfType());
break;
case NotifyCollectionChangedAction.Replace:
- for (var i = 0; i < e.OldItems.Count; ++i)
+ for (var i = 0; i < e.OldItems!.Count; ++i)
{
var index = i + e.OldStartingIndex;
- var child = (IControl)e.NewItems[i];
+ var child = (IControl)e.NewItems![i]!;
LogicalChildren[index] = child;
VisualChildren[index] = child;
}
diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index 18348571bf..23ecdb2e7b 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -4,12 +4,11 @@ using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
+using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Platform
{
///
@@ -68,8 +67,8 @@ namespace Avalonia.Controls.Platform
window.Deactivated += WindowDeactivated;
}
- if (_root is TopLevel tl)
- tl.PlatformImpl.LostFocus += TopLevelLostPlatformFocus;
+ if (_root is TopLevel tl && tl.PlatformImpl is ITopLevelImpl pimpl)
+ pimpl.LostFocus += TopLevelLostPlatformFocus;
_inputManagerSubscription = InputManager?.Process.Subscribe(RawInput);
}
@@ -118,7 +117,7 @@ namespace Avalonia.Controls.Platform
protected static TimeSpan MenuShowDelay { get; } = TimeSpan.FromMilliseconds(400);
- protected internal virtual void GotFocus(object sender, GotFocusEventArgs e)
+ protected internal virtual void GotFocus(object? sender, GotFocusEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -128,7 +127,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void LostFocus(object sender, RoutedEventArgs e)
+ protected internal virtual void LostFocus(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -138,7 +137,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void KeyDown(object sender, KeyEventArgs e)
+ protected internal virtual void KeyDown(object? sender, KeyEventArgs e)
{
KeyDown(GetMenuItem(e.Source as IControl), e);
}
@@ -267,7 +266,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void AccessKeyPressed(object sender, RoutedEventArgs e)
+ protected internal virtual void AccessKeyPressed(object? sender, RoutedEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -288,7 +287,7 @@ namespace Avalonia.Controls.Platform
e.Handled = true;
}
- protected internal virtual void PointerEnter(object sender, PointerEventArgs e)
+ protected internal virtual void PointerEnter(object? sender, PointerEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -333,7 +332,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void PointerLeave(object sender, PointerEventArgs e)
+ protected internal virtual void PointerLeave(object? sender, PointerEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -368,12 +367,12 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void PointerPressed(object sender, PointerPressedEventArgs e)
+ protected internal virtual void PointerPressed(object? sender, PointerPressedEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
- var visual = (IVisual)sender;
- if (e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed && item?.HasSubMenu == true)
+ if (sender is IVisual visual &&
+ e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed && item?.HasSubMenu == true)
{
if (item.IsSubMenuOpen)
{
@@ -399,7 +398,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void PointerReleased(object sender, PointerReleasedEventArgs e)
+ protected internal virtual void PointerReleased(object? sender, PointerReleasedEventArgs e)
{
var item = GetMenuItem(e.Source as IControl);
@@ -410,7 +409,7 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void MenuOpened(object sender, RoutedEventArgs e)
+ protected internal virtual void MenuOpened(object? sender, RoutedEventArgs e)
{
if (e.Source == Menu)
{
@@ -428,20 +427,18 @@ namespace Avalonia.Controls.Platform
}
}
- protected internal virtual void RootPointerPressed(object sender, PointerPressedEventArgs e)
+ protected internal virtual void RootPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (Menu?.IsOpen == true)
{
- var control = e.Source as ILogical;
-
- if (!Menu.IsLogicalAncestorOf(control))
+ if (e.Source is ILogical control && !Menu.IsLogicalAncestorOf(control))
{
Menu.Close();
}
}
}
- protected internal virtual void WindowDeactivated(object sender, EventArgs e)
+ protected internal virtual void WindowDeactivated(object? sender, EventArgs e)
{
Menu?.Close();
}
diff --git a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
deleted file mode 100644
index e958b7aa15..0000000000
--- a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-
-namespace Avalonia.Platform
-{
- [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public class ExportWindowingSubsystemAttribute : Attribute
- {
- public ExportWindowingSubsystemAttribute(OperatingSystemType requiredRuntimePlatform, int priority, string name, Type initializationType,
- string initializationMethod, Type environmentChecker = null)
- {
- Name = name;
- InitializationType = initializationType;
- InitializationMethod = initializationMethod;
- EnvironmentChecker = environmentChecker;
- RequiredOS = requiredRuntimePlatform;
- Priority = priority;
- }
-
- public string InitializationMethod { get; private set; }
- public Type EnvironmentChecker { get; }
- public Type InitializationType { get; private set; }
- public string Name { get; private set; }
- public int Priority { get; private set; }
- public OperatingSystemType RequiredOS { get; private set; }
- }
-}
diff --git a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
index c6b1d09849..df13613848 100644
--- a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
+++ b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using Avalonia.Platform;
using Avalonia.VisualTree;
@@ -19,7 +20,8 @@ namespace Avalonia.Controls.Platform
public interface INativeControlHostControlTopLevelAttachment : IDisposable
{
- INativeControlHostImpl AttachedTo { get; set; }
+ INativeControlHostImpl? AttachedTo { get; set; }
+
bool IsCompatibleWith(INativeControlHostImpl host);
void HideWithSize(Size size);
void ShowInBounds(Rect rect);
@@ -27,6 +29,6 @@ namespace Avalonia.Controls.Platform
public interface ITopLevelImplWithNativeControlHost
{
- INativeControlHostImpl NativeControlHost { get; }
+ INativeControlHostImpl? NativeControlHost { get; }
}
}
diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs
index 5bd45057d9..b68391aa52 100644
--- a/src/Avalonia.Controls/Platform/IScreenImpl.cs
+++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
+#nullable enable
+
namespace Avalonia.Platform
{
public interface IScreenImpl
@@ -7,5 +9,11 @@ namespace Avalonia.Platform
int ScreenCount { get; }
IReadOnlyList AllScreens { get; }
+
+ Screen? ScreenFromWindow(IWindowBaseImpl window);
+
+ Screen? ScreenFromPoint(PixelPoint point);
+
+ Screen? ScreenFromRect(PixelRect rect);
}
}
diff --git a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs
index 7bd13ecdb1..1685a6a38c 100644
--- a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs
+++ b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs
@@ -1,7 +1,5 @@
using System.Threading.Tasks;
-#nullable enable
-
namespace Avalonia.Controls.Platform
{
///
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index f4f4d29168..80434882f7 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -82,27 +82,27 @@ namespace Avalonia.Platform
///
/// Gets or sets a method called when the toplevel receives input.
///
- Action Input { get; set; }
+ Action? Input { get; set; }
///
/// Gets or sets a method called when the toplevel requires painting.
///
- Action Paint { get; set; }
+ Action? Paint { get; set; }
///
/// Gets or sets a method called when the toplevel is resized.
///
- Action Resized { get; set; }
+ Action? Resized { get; set; }
///
/// Gets or sets a method called when the toplevel's scaling changes.
///
- Action ScalingChanged { get; set; }
+ Action? ScalingChanged { get; set; }
///
/// Gets or sets a method called when the toplevel's TransparencyLevel changes.
///
- Action TransparencyLevelChanged { get; set; }
+ Action? TransparencyLevelChanged { get; set; }
///
/// Creates a new renderer for the toplevel.
@@ -138,17 +138,17 @@ namespace Avalonia.Platform
/// Sets the cursor associated with the toplevel.
///
/// The cursor. Use null for default cursor
- void SetCursor(ICursorImpl cursor);
+ void SetCursor(ICursorImpl? cursor);
///
/// Gets or sets a method called when the underlying implementation is destroyed.
///
- Action Closed { get; set; }
+ Action? Closed { get; set; }
///
/// Gets or sets a method called when the input focus is lost.
///
- Action LostFocus { get; set; }
+ Action? LostFocus { get; set; }
///
/// Gets a mouse device associated with toplevel
@@ -156,7 +156,7 @@ namespace Avalonia.Platform
[CanBeNull]
IMouseDevice MouseDevice { get; }
- IPopupImpl CreatePopup();
+ IPopupImpl? CreatePopup();
///
/// Sets the hint of the TopLevel.
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs
index 9c29415a6a..bafb973765 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs
@@ -6,6 +6,6 @@ namespace Avalonia.Controls.Platform
{
public interface ITopLevelImplWithTextInputMethod : ITopLevelImpl
{
- public ITextInputMethodImpl TextInputMethod { get; }
+ public ITextInputMethodImpl? TextInputMethod { get; }
}
}
diff --git a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs
index 9b779054f3..9e72a40439 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs
@@ -1,8 +1,6 @@
using System;
using Avalonia.Platform;
-#nullable enable
-
namespace Avalonia.Controls.Platform
{
public interface INativeMenuExporter
@@ -24,6 +22,6 @@ namespace Avalonia.Controls.Platform
public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl
{
- ITopLevelNativeMenuExporter NativeMenuExporter { get; }
+ ITopLevelNativeMenuExporter? NativeMenuExporter { get; }
}
}
diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs
index 9768d149f0..289b569645 100644
--- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs
@@ -1,8 +1,6 @@
using System;
using Avalonia.Controls.Platform;
-#nullable enable
-
namespace Avalonia.Platform
{
public interface ITrayIconImpl : IDisposable
diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
index 5172569726..066f4579c0 100644
--- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
+++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Automation.Peers;
namespace Avalonia.Platform
{
@@ -29,7 +30,7 @@ namespace Avalonia.Platform
///
/// Gets or sets a method called when the window's position changes.
///
- Action PositionChanged { get; set; }
+ Action? PositionChanged { get; set; }
///
/// Activates the window.
@@ -39,12 +40,12 @@ namespace Avalonia.Platform
///
/// Gets or sets a method called when the window is deactivated (loses focus).
///
- Action Deactivated { get; set; }
+ Action? Deactivated { get; set; }
///
/// Gets or sets a method called when the window is activated (receives focus).
///
- Action Activated { get; set; }
+ Action? Activated { get; set; }
///
/// Gets the platform window handle.
diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs
index 17a90eddfe..d4be4f9f45 100644
--- a/src/Avalonia.Controls/Platform/IWindowImpl.cs
+++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Platform
/// Sets the title of the window.
///
/// The title.
- void SetTitle(string title);
+ void SetTitle(string? title);
///
/// Sets the parent of the window.
@@ -50,7 +50,7 @@ namespace Avalonia.Platform
///
/// Sets the icon of this window.
///
- void SetIcon(IWindowIconImpl icon);
+ void SetIcon(IWindowIconImpl? icon);
///
/// Enables or disables the taskbar icon
diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs
index 21882b1271..fa26fe8fdd 100644
--- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs
+++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs
@@ -1,5 +1,3 @@
-#nullable enable
-
namespace Avalonia.Platform
{
public interface IWindowingPlatform
diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs
index 414c523e0d..209d5f03dc 100644
--- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs
+++ b/src/Avalonia.Controls/Platform/InProcessDragSource.cs
@@ -20,17 +20,17 @@ namespace Avalonia.Platform
private readonly Subject _result = new Subject();
private DragDropEffects _allowedEffects;
- private IDataObject _draggedData;
- private IInputRoot _lastRoot;
+ private IDataObject? _draggedData;
+ private IInputRoot? _lastRoot;
private Point _lastPosition;
private StandardCursorType _lastCursorType;
- private object _originalCursor;
+ private object? _originalCursor;
private RawInputModifiers? _initialInputModifiers;
public InProcessDragSource()
{
- _inputManager = AvaloniaLocator.Current.GetService();
- _dragDrop = AvaloniaLocator.Current.GetService();
+ _inputManager = AvaloniaLocator.Current.GetRequiredService();
+ _dragDrop = AvaloniaLocator.Current.GetRequiredService();
}
public async Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects)
@@ -60,9 +60,9 @@ namespace Avalonia.Platform
{
_lastPosition = pt;
- RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData, _allowedEffects, modifiers);
+ RawDragEvent rawEvent = new RawDragEvent(_dragDrop, type, root, pt, _draggedData!, _allowedEffects, modifiers);
var tl = root.GetSelfAndVisualAncestors().OfType().FirstOrDefault();
- tl.PlatformImpl?.Input(rawEvent);
+ tl?.PlatformImpl?.Input?.Invoke(rawEvent);
var effect = GetPreferredEffect(rawEvent.Effects & _allowedEffects, modifiers);
UpdateCursor(root, effect);
@@ -91,7 +91,7 @@ namespace Avalonia.Platform
return StandardCursorType.No;
}
- private void UpdateCursor(IInputRoot root, DragDropEffects effect)
+ private void UpdateCursor(IInputRoot? root, DragDropEffects effect)
{
if (_lastRoot != root)
{
diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
index 6a95c2e047..4e5908456e 100644
--- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
+++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
@@ -36,7 +36,7 @@ namespace Avalonia.Controls.Platform
private readonly DispatcherPriority _priority;
private readonly TimeSpan _interval;
private readonly Action _tick;
- private Timer _timer;
+ private Timer? _timer;
private GCHandle _handle;
public TimerImpl(DispatcherPriority priority, TimeSpan interval, Action tick)
@@ -48,7 +48,7 @@ namespace Avalonia.Controls.Platform
_handle = GCHandle.Alloc(_timer);
}
- private void OnTimer(object state)
+ private void OnTimer(object? state)
{
if (_timer == null)
return;
@@ -66,7 +66,7 @@ namespace Avalonia.Controls.Platform
public void Dispose()
{
_handle.Free();
- _timer.Dispose();
+ _timer?.Dispose();
_timer = null;
}
}
@@ -84,9 +84,9 @@ namespace Avalonia.Controls.Platform
[ThreadStatic] private static bool TlsCurrentThreadIsLoopThread;
public bool CurrentThreadIsLoopThread => TlsCurrentThreadIsLoopThread;
- public event Action Signaled;
+ public event Action? Signaled;
#pragma warning disable CS0067
- public event Action Tick;
+ public event Action? Tick;
#pragma warning restore CS0067
}
diff --git a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
index 9448f16c38..f3104e4360 100644
--- a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
+++ b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
@@ -7,14 +7,14 @@ namespace Avalonia.Controls.Platform
///
public class MountedVolumeInfo : IEquatable
{
- public string VolumeLabel { get; set; }
- public string VolumePath { get; set; }
+ public string? VolumeLabel { get; set; }
+ public string? VolumePath { get; set; }
public ulong VolumeSizeBytes { get; set; }
- public bool Equals(MountedVolumeInfo other)
+ public bool Equals(MountedVolumeInfo? other)
{
- return this.VolumeSizeBytes.Equals(other.VolumeSizeBytes) &&
- this.VolumePath.Equals(other.VolumePath) &&
+ return this.VolumeSizeBytes.Equals(other?.VolumeSizeBytes) &&
+ Equals(this.VolumePath, other.VolumePath) &&
(this.VolumeLabel ?? string.Empty).Equals(other.VolumeLabel ?? string.Empty);
}
}
diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs
index 8e9f64b3ad..ee62316922 100644
--- a/src/Avalonia.Controls/Platform/PlatformManager.cs
+++ b/src/Avalonia.Controls/Platform/PlatformManager.cs
@@ -2,8 +2,6 @@ using System;
using System.Reactive.Disposables;
using Avalonia.Platform;
-#nullable enable
-
namespace Avalonia.Controls.Platform
{
public static partial class PlatformManager
diff --git a/src/Avalonia.Controls/Platform/ScreenHelper.cs b/src/Avalonia.Controls/Platform/ScreenHelper.cs
new file mode 100644
index 0000000000..0bd2be69d0
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/ScreenHelper.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using Avalonia.Utilities;
+
+#nullable enable
+
+namespace Avalonia.Platform
+{
+ public static class ScreenHelper
+ {
+ public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList screens)
+ {
+ foreach (Screen screen in screens)
+ {
+ if (screen.Bounds.ContainsExclusive(point))
+ {
+ return screen;
+ }
+ }
+
+ return null;
+ }
+
+ public static Screen? ScreenFromRect(PixelRect bounds, IReadOnlyList screens)
+ {
+ Screen? currMaxScreen = null;
+ double maxAreaSize = 0;
+
+ foreach (Screen screen in screens)
+ {
+ double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
+ double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
+ double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
+ double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
+ double area = (right - left) * (bottom - top);
+ if (area > maxAreaSize)
+ {
+ maxAreaSize = area;
+ currMaxScreen = screen;
+ }
+ }
+
+ return currMaxScreen;
+ }
+
+ public static Screen? ScreenFromWindow(IWindowBaseImpl window, IReadOnlyList screens)
+ {
+ var rect = new PixelRect(
+ window.Position,
+ PixelSize.FromSize(window.FrameSize ?? window.ClientSize, window.DesktopScaling));
+
+ return ScreenFromRect(rect, screens);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
index 81f43865a7..3d1e7eb5a8 100644
--- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Presenters
Carousel.PageTransitionProperty.AddOwner();
private int _selectedIndex = -1;
- private Task _currentTransition;
+ private Task? _currentTransition;
private int _queuedTransitionIndex = -1;
///
@@ -126,7 +126,7 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Replace:
if (e.OldStartingIndex > SelectedIndex ||
- e.OldStartingIndex + e.OldItems.Count - 1 < SelectedIndex)
+ e.OldStartingIndex + e.OldItems!.Count - 1 < SelectedIndex)
{
return;
}
@@ -171,12 +171,12 @@ namespace Avalonia.Controls.Presenters
if (fromIndex != toIndex)
{
var generator = ItemContainerGenerator;
- IControl from = null;
- IControl to = null;
+ IControl? from = null;
+ IControl? to = null;
if (fromIndex != -1)
{
- from = ItemContainerGenerator.ContainerFromIndex(fromIndex);
+ from = generator.ContainerFromIndex(fromIndex);
}
if (toIndex != -1)
@@ -186,7 +186,7 @@ namespace Avalonia.Controls.Presenters
if (PageTransition != null && (from != null || to != null))
{
- await PageTransition.Start((Visual)from, (Visual)to, fromIndex < toIndex, default);
+ await PageTransition.Start((Visual?)from, (Visual?)to, fromIndex < toIndex, default);
}
else if (to != null)
{
@@ -197,7 +197,7 @@ namespace Avalonia.Controls.Presenters
{
if (IsVirtualized)
{
- Panel.Children.Remove(from);
+ Panel!.Children.Remove(from);
generator.Dematerialize(fromIndex, 1);
}
else
@@ -208,15 +208,15 @@ namespace Avalonia.Controls.Presenters
}
}
- private IControl GetOrCreateContainer(int index)
+ private IControl? GetOrCreateContainer(int index)
{
var container = ItemContainerGenerator.ContainerFromIndex(index);
if (container == null && IsVirtualized)
{
- var item = Items.Cast().ElementAt(index);
+ var item = Items!.Cast().ElementAt(index);
var materialized = ItemContainerGenerator.Materialize(index, item);
- Panel.Children.Add(materialized.ContainerControl);
+ Panel!.Children.Add(materialized.ContainerControl);
container = materialized.ContainerControl;
}
@@ -245,8 +245,8 @@ namespace Avalonia.Controls.Presenters
{
if (_currentTransition == null)
{
- int fromIndex = (int)e.OldValue;
- int toIndex = (int)e.NewValue;
+ int fromIndex = (int)e.OldValue!;
+ int toIndex = (int)e.NewValue!;
for (;;)
{
@@ -268,7 +268,7 @@ namespace Avalonia.Controls.Presenters
}
else
{
- _queuedTransitionIndex = (int)e.NewValue;
+ _queuedTransitionIndex = (int)e.NewValue!;
}
}
}
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index 1a46d84558..9886dd913a 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -20,13 +20,13 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly StyledProperty BackgroundProperty =
+ public static readonly StyledProperty BackgroundProperty =
Border.BackgroundProperty.AddOwner();
///
/// Defines the property.
///
- public static readonly StyledProperty BorderBrushProperty =
+ public static readonly StyledProperty BorderBrushProperty =
Border.BorderBrushProperty.AddOwner();
///
@@ -50,21 +50,21 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly DirectProperty ChildProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty ChildProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(Child),
o => o.Child);
///
/// Defines the property.
///
- public static readonly StyledProperty ContentProperty =
+ public static readonly StyledProperty ContentProperty =
ContentControl.ContentProperty.AddOwner();
///
/// Defines the property.
///
- public static readonly StyledProperty ContentTemplateProperty =
+ public static readonly StyledProperty ContentTemplateProperty =
ContentControl.ContentTemplateProperty.AddOwner();
///
@@ -93,9 +93,9 @@ namespace Avalonia.Controls.Presenters
nameof(RecognizesAccessKey),
cp => cp.RecognizesAccessKey, (cp, value) => cp.RecognizesAccessKey = value);
- private IControl _child;
+ private IControl? _child;
private bool _createdChild;
- private IRecyclingDataTemplate _recyclingDataTemplate;
+ private IRecyclingDataTemplate? _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
private bool _recognizesAccessKey;
@@ -120,7 +120,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets or sets a brush with which to paint the background.
///
- public IBrush Background
+ public IBrush? Background
{
get { return GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value); }
@@ -129,7 +129,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets or sets a brush with which to paint the border.
///
- public IBrush BorderBrush
+ public IBrush? BorderBrush
{
get { return GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
@@ -165,7 +165,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets the control displayed by the presenter.
///
- public IControl Child
+ public IControl? Child
{
get { return _child; }
private set { SetAndRaise(ChildProperty, ref _child, value); }
@@ -175,7 +175,7 @@ namespace Avalonia.Controls.Presenters
/// Gets or sets the content to be displayed by the presenter.
///
[DependsOn(nameof(ContentTemplate))]
- public object Content
+ public object? Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
@@ -184,7 +184,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets or sets the data template used to display the content of the control.
///
- public IDataTemplate ContentTemplate
+ public IDataTemplate? ContentTemplate
{
get { return GetValue(ContentTemplateProperty); }
set { SetValue(ContentTemplateProperty, value); }
@@ -229,7 +229,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets the host content control.
///
- internal IContentPresenterHost Host { get; private set; }
+ internal IContentPresenterHost? Host { get; private set; }
///
public sealed override void ApplyTemplate()
@@ -321,7 +321,7 @@ namespace Avalonia.Controls.Presenters
/// Creates the child control.
///
/// The child control or null.
- protected virtual IControl CreateChild()
+ protected virtual IControl? CreateChild()
{
var content = Content;
var oldChild = Child;
diff --git a/src/Avalonia.Controls/Presenters/IContentPresenter.cs b/src/Avalonia.Controls/Presenters/IContentPresenter.cs
index b1e64c2f25..ab4d61e3bd 100644
--- a/src/Avalonia.Controls/Presenters/IContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/IContentPresenter.cs
@@ -11,11 +11,11 @@ namespace Avalonia.Controls.Presenters
///
/// Gets the control displayed by the presenter.
///
- IControl Child { get; }
+ IControl? Child { get; }
///
/// Gets or sets the content to be displayed by the presenter.
///
- object Content { get; set; }
+ object? Content { get; set; }
}
}
diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
index 873f0d756f..e7da3d4618 100644
--- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs
@@ -5,9 +5,9 @@ namespace Avalonia.Controls.Presenters
{
public interface IItemsPresenter : IPresenter
{
- IEnumerable Items { get; set; }
+ IEnumerable? Items { get; set; }
- IPanel Panel { get; }
+ IPanel? Panel { get; }
void ItemsChanged(NotifyCollectionChangedEventArgs e);
diff --git a/src/Avalonia.Controls/Presenters/ItemContainerSync.cs b/src/Avalonia.Controls/Presenters/ItemContainerSync.cs
index 6e72908e6b..81184f719e 100644
--- a/src/Avalonia.Controls/Presenters/ItemContainerSync.cs
+++ b/src/Avalonia.Controls/Presenters/ItemContainerSync.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Controls.Presenters
{
public static void ItemsChanged(
ItemsPresenterBase owner,
- IEnumerable items,
+ IEnumerable? items,
NotifyCollectionChangedEventArgs e)
{
var generator = owner.ItemContainerGenerator;
@@ -24,7 +24,7 @@ namespace Avalonia.Controls.Presenters
void Add()
{
- if (e.NewStartingIndex + e.NewItems.Count < items.Count())
+ if (e.NewStartingIndex + e.NewItems!.Count < items!.Count())
{
generator.InsertSpace(e.NewStartingIndex, e.NewItems.Count);
}
@@ -34,7 +34,7 @@ namespace Avalonia.Controls.Presenters
void Remove()
{
- RemoveContainers(panel, generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count));
+ RemoveContainers(panel, generator.RemoveRange(e.OldStartingIndex, e.OldItems!.Count));
}
switch (e.Action)
@@ -48,8 +48,8 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Replace:
- RemoveContainers(panel, generator.Dematerialize(e.OldStartingIndex, e.OldItems.Count));
- var containers = AddContainers(owner, e.NewStartingIndex, e.NewItems);
+ RemoveContainers(panel, generator.Dematerialize(e.OldStartingIndex, e.OldItems!.Count));
+ var containers = AddContainers(owner, e.NewStartingIndex, e.NewItems!);
var i = e.NewStartingIndex;
@@ -92,7 +92,7 @@ namespace Avalonia.Controls.Presenters
if (i.ContainerControl != null)
{
- if (i.Index < panel.Children.Count)
+ if (i.Index < panel!.Children.Count)
{
// TODO: This will insert at the wrong place when there are null items.
panel.Children.Insert(i.Index, i.ContainerControl);
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
index be2b1e86f0..4c412e9a41 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Controls.Presenters
internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable
{
private double _crossAxisOffset;
- private IDisposable _subscriptions;
+ private IDisposable? _subscriptions;
///
/// Initializes a new instance of the class.
@@ -46,12 +46,12 @@ namespace Avalonia.Controls.Presenters
///
/// Gets the which will host the items.
///
- public IVirtualizingPanel VirtualizingPanel => Owner.Panel as IVirtualizingPanel;
+ public IVirtualizingPanel? VirtualizingPanel => Owner.Panel as IVirtualizingPanel;
///
/// Gets the items to display.
///
- public IEnumerable Items { get; private set; }
+ public IEnumerable? Items { get; private set; }
///
/// Gets the number of items in .
@@ -100,11 +100,11 @@ namespace Avalonia.Controls.Presenters
{
get
{
- if (IsLogicalScrollEnabled)
+ if (IsLogicalScrollEnabled && Owner.Panel is Panel panel)
{
return Vertical ?
- new Size(Owner.Panel.DesiredSize.Width, ExtentValue) :
- new Size(ExtentValue, Owner.Panel.DesiredSize.Height);
+ new Size(panel.DesiredSize.Width, ExtentValue) :
+ new Size(ExtentValue, panel.DesiredSize.Height);
}
return default;
@@ -118,11 +118,11 @@ namespace Avalonia.Controls.Presenters
{
get
{
- if (IsLogicalScrollEnabled)
+ if (IsLogicalScrollEnabled && Owner.Panel is Panel panel)
{
return Vertical ?
- new Size(Owner.Panel.Bounds.Width, ViewportValue) :
- new Size(ViewportValue, Owner.Panel.Bounds.Height);
+ new Size(panel.Bounds.Width, ViewportValue) :
+ new Size(ViewportValue, panel.Bounds.Height);
}
return default;
@@ -177,7 +177,7 @@ namespace Avalonia.Controls.Presenters
///
/// The items presenter.
/// An .
- public static ItemVirtualizer Create(ItemsPresenter owner)
+ public static ItemVirtualizer? Create(ItemsPresenter owner)
{
if (owner.Panel == null)
{
@@ -186,7 +186,7 @@ namespace Avalonia.Controls.Presenters
var virtualizingPanel = owner.Panel as IVirtualizingPanel;
var scrollContentPresenter = owner.Parent as IScrollable;
- ItemVirtualizer result = null;
+ ItemVirtualizer? result = null;
if (virtualizingPanel != null && scrollContentPresenter is object)
{
@@ -218,7 +218,7 @@ namespace Avalonia.Controls.Presenters
/// The desired size for the control.
public virtual Size MeasureOverride(Size availableSize)
{
- Owner.Panel.Measure(availableSize);
+ Owner.Panel!.Measure(availableSize);
return Owner.Panel.DesiredSize;
}
@@ -232,12 +232,12 @@ namespace Avalonia.Controls.Presenters
if (VirtualizingPanel != null)
{
VirtualizingPanel.CrossAxisOffset = _crossAxisOffset;
- Owner.Panel.Arrange(new Rect(finalSize));
+ Owner.Panel!.Arrange(new Rect(finalSize));
}
else
{
var origin = Vertical ? new Point(-_crossAxisOffset, 0) : new Point(0, _crossAxisOffset);
- Owner.Panel.Arrange(new Rect(origin, finalSize));
+ Owner.Panel!.Arrange(new Rect(origin, finalSize));
}
return finalSize;
@@ -254,7 +254,7 @@ namespace Avalonia.Controls.Presenters
/// The movement direction.
/// The control from which movement begins.
/// The control.
- public virtual IControl GetControlInDirection(NavigationDirection direction, IControl from)
+ public virtual IControl? GetControlInDirection(NavigationDirection direction, IControl? from)
{
return null;
}
@@ -266,10 +266,10 @@ namespace Avalonia.Controls.Presenters
///
/// The items.
/// A description of the change.
- public virtual void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
+ public virtual void ItemsChanged(IEnumerable? items, NotifyCollectionChangedEventArgs e)
{
Items = items;
- ItemCount = items.Count();
+ ItemCount = items?.Count() ?? 0;
}
///
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
index 275f4d418e..c1b6842b88 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
@@ -54,7 +54,7 @@ namespace Avalonia.Controls.Presenters
}
///
- public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
+ public override void ItemsChanged(IEnumerable? items, NotifyCollectionChangedEventArgs e)
{
base.ItemsChanged(items, e);
ItemContainerSync.ItemsChanged(Owner, items, e);
@@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters
if (i.ContainerControl != null)
{
- if (i.Index < panel.Children.Count)
+ if (i.Index < panel!.Children.Count)
{
// TODO: This will insert at the wrong place when there are null items.
panel.Children.Insert(i.Index, i.ContainerControl);
@@ -102,18 +102,5 @@ namespace Avalonia.Controls.Presenters
return result;
}
-
- private void RemoveContainers(IEnumerable items)
- {
- var panel = Owner.Panel;
-
- foreach (var i in items)
- {
- if (i.ContainerControl != null)
- {
- panel.Children.Remove(i.ContainerControl);
- }
- }
- }
}
}
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
index 7d50ef7d33..39a512a773 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
@@ -108,6 +108,8 @@ namespace Avalonia.Controls.Presenters
}
}
+ public new IVirtualizingPanel VirtualizingPanel => base.VirtualizingPanel!;
+
///
public override Size MeasureOverride(Size availableSize)
{
@@ -151,7 +153,7 @@ namespace Avalonia.Controls.Presenters
}
}
- Owner.Panel.Measure(availableSize);
+ Owner.Panel!.Measure(availableSize);
return Owner.Panel.DesiredSize;
}
@@ -163,7 +165,7 @@ namespace Avalonia.Controls.Presenters
}
///
- public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
+ public override void ItemsChanged(IEnumerable? items, NotifyCollectionChangedEventArgs e)
{
base.ItemsChanged(items, e);
@@ -224,18 +226,13 @@ namespace Avalonia.Controls.Presenters
InvalidateScroll();
}
- public override IControl GetControlInDirection(NavigationDirection direction, IControl from)
+ public override IControl? GetControlInDirection(NavigationDirection direction, IControl? from)
{
var generator = Owner.ItemContainerGenerator;
var panel = VirtualizingPanel;
var itemIndex = generator.IndexFromContainer(from);
var vertical = VirtualizingPanel.ScrollDirection == Orientation.Vertical;
- if (itemIndex == -1)
- {
- return null;
- }
-
var newItemIndex = -1;
switch (direction)
@@ -248,6 +245,16 @@ namespace Avalonia.Controls.Presenters
newItemIndex = ItemCount - 1;
break;
+ default:
+ if (itemIndex == -1)
+ {
+ return null;
+ }
+ break;
+ }
+
+ switch (direction)
+ {
case NavigationDirection.Up:
if (vertical)
{
@@ -331,7 +338,7 @@ namespace Avalonia.Controls.Presenters
}
}
- var materialized = generator.Materialize(index, Items.ElementAt(index));
+ var materialized = generator.Materialize(index, Items.ElementAt(index)!);
if (step == 1)
{
@@ -382,7 +389,7 @@ namespace Avalonia.Controls.Presenters
foreach (var container in containers)
{
- var item = Items.ElementAt(itemIndex);
+ var item = Items!.ElementAt(itemIndex)!;
if (!object.Equals(container.Item, item))
{
@@ -427,7 +434,7 @@ namespace Avalonia.Controls.Presenters
var oldItemIndex = FirstIndex + first + i;
var newItemIndex = oldItemIndex + delta + ((panel.Children.Count - count) * sign);
- var item = Items.ElementAt(newItemIndex);
+ var item = Items!.ElementAt(newItemIndex)!;
if (!generator.TryRecycle(oldItemIndex, newItemIndex, item))
{
@@ -506,7 +513,7 @@ namespace Avalonia.Controls.Presenters
///
/// The item index.
/// The container that was brought into view.
- private IControl ScrollIntoViewCore(int index)
+ private IControl? ScrollIntoViewCore(int index)
{
var panel = VirtualizingPanel;
var generator = Owner.ItemContainerGenerator;
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
index 80c9e972d5..265704ceaa 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Controls.Presenters
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
- private EventHandler _scrollInvalidated;
+ private EventHandler? _scrollInvalidated;
///
/// Initializes static members of the class.
@@ -96,7 +96,7 @@ namespace Avalonia.Controls.Presenters
Size IScrollable.Viewport => Virtualizer?.Viewport ?? Bounds.Size;
///
- event EventHandler ILogicalScrollable.ScrollInvalidated
+ event EventHandler? ILogicalScrollable.ScrollInvalidated
{
add => _scrollInvalidated += value;
remove => _scrollInvalidated -= value;
@@ -108,7 +108,7 @@ namespace Avalonia.Controls.Presenters
///
Size ILogicalScrollable.PageScrollSize => Virtualizer?.Viewport ?? new Size(16, 16);
- internal ItemVirtualizer Virtualizer { get; private set; }
+ internal ItemVirtualizer? Virtualizer { get; private set; }
///
bool ILogicalScrollable.BringIntoView(IControl target, Rect targetRect)
@@ -117,7 +117,7 @@ namespace Avalonia.Controls.Presenters
}
///
- IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from)
+ IControl? ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl? from)
{
return Virtualizer?.GetControlInDirection(direction, from);
}
@@ -152,7 +152,7 @@ namespace Avalonia.Controls.Presenters
_scrollInvalidated?.Invoke(this, EventArgs.Empty);
KeyboardNavigation.SetTabNavigation(
- (InputElement)Panel,
+ (InputElement)panel,
KeyboardNavigation.GetTabNavigation(this));
}
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
index aeead7bfd0..f938c8d437 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly DirectProperty ItemsProperty =
+ public static readonly DirectProperty ItemsProperty =
ItemsControl.ItemsProperty.AddOwner(o => o.Items, (o, v) => o.Items = v);
///
@@ -30,14 +30,14 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly StyledProperty ItemTemplateProperty =
+ public static readonly StyledProperty ItemTemplateProperty =
ItemsControl.ItemTemplateProperty.AddOwner();
- private IEnumerable _items;
- private IDisposable _itemsSubscription;
+ private IEnumerable? _items;
+ private IDisposable? _itemsSubscription;
private bool _createdPanel;
- private IItemContainerGenerator _generator;
- private EventHandler _childIndexChanged;
+ private IItemContainerGenerator? _generator;
+ private EventHandler? _childIndexChanged;
///
/// Initializes static members of the class.
@@ -50,7 +50,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets or sets the items to be displayed.
///
- public IEnumerable Items
+ public IEnumerable? Items
{
get
{
@@ -114,7 +114,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets or sets the data template used to display the items in the control.
///
- public IDataTemplate ItemTemplate
+ public IDataTemplate? ItemTemplate
{
get { return GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
@@ -123,7 +123,7 @@ namespace Avalonia.Controls.Presenters
///
/// Gets the panel used to display the items.
///
- public IPanel Panel
+ public IPanel? Panel
{
get;
private set;
@@ -131,7 +131,7 @@ namespace Avalonia.Controls.Presenters
protected bool IsHosted => TemplatedParent is IItemsPresenterHost;
- event EventHandler IChildIndexProvider.ChildIndexChanged
+ event EventHandler? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
@@ -186,7 +186,7 @@ namespace Avalonia.Controls.Presenters
return result;
}
- private void ContainerActionHandler(object sender, ItemContainerEventArgs e)
+ private void ContainerActionHandler(object? sender, ItemContainerEventArgs e)
{
for (var i = 0; i < e.Containers.Count; i++)
{
@@ -197,14 +197,14 @@ namespace Avalonia.Controls.Presenters
///
protected override Size MeasureOverride(Size availableSize)
{
- Panel.Measure(availableSize);
+ Panel!.Measure(availableSize);
return Panel.DesiredSize;
}
///
protected override Size ArrangeOverride(Size finalSize)
{
- Panel.Arrange(new Rect(finalSize));
+ Panel!.Arrange(new Rect(finalSize));
return finalSize;
}
@@ -258,7 +258,7 @@ namespace Avalonia.Controls.Presenters
///
/// The sender.
/// The event args.
- private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (_createdPanel)
{
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index a62ba306ab..97fb4c3f43 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -7,8 +7,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Presenters
{
///
@@ -60,6 +58,12 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsScrollChainingEnabledProperty =
+ ScrollViewer.IsScrollChainingEnabledProperty.AddOwner();
+
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private bool _arranging;
@@ -138,6 +142,20 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
+ ///
+ /// Gets or sets if scroll chaining is enabled. The default value is true.
+ ///
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public bool IsScrollChainingEnabled
+ {
+ get => GetValue(IsScrollChainingEnabledProperty);
+ set => SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
///
IControl? IScrollAnchorProvider.CurrentAnchor
{
@@ -268,8 +286,8 @@ namespace Avalonia.Controls.Presenters
private Size ArrangeWithAnchoring(Size finalSize)
{
var size = new Size(
- CanHorizontallyScroll ? Math.Max(Child.DesiredSize.Width, finalSize.Width) : finalSize.Width,
- CanVerticallyScroll ? Math.Max(Child.DesiredSize.Height, finalSize.Height) : finalSize.Height);
+ CanHorizontallyScroll ? Math.Max(Child!.DesiredSize.Width, finalSize.Width) : finalSize.Width,
+ CanVerticallyScroll ? Math.Max(Child!.DesiredSize.Height, finalSize.Height) : finalSize.Height);
Vector TrackAnchor()
{
@@ -277,7 +295,7 @@ namespace Avalonia.Controls.Presenters
// arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust
// relative positions within Child).
if (_anchorElement != null &&
- TranslateBounds(_anchorElement, Child, out var updatedBounds) &&
+ TranslateBounds(_anchorElement, Child!, out var updatedBounds) &&
updatedBounds.Position != _anchorElementBounds.Position)
{
var offset = updatedBounds.Position - _anchorElementBounds.Position;
@@ -337,13 +355,13 @@ namespace Avalonia.Controls.Presenters
}
Viewport = finalSize;
- Extent = Child.Bounds.Size.Inflate(Child.Margin);
+ Extent = Child!.Bounds.Size.Inflate(Child.Margin);
_isAnchorElementDirty = true;
return finalSize;
}
- private void OnScrollGesture(object sender, ScrollGestureEventArgs e)
+ private void OnScrollGesture(object? sender, ScrollGestureEventArgs e)
{
if (Extent.Height > Viewport.Height || Extent.Width > Viewport.Width)
{
@@ -405,12 +423,15 @@ namespace Avalonia.Controls.Presenters
_activeLogicalGestureScrolls[e.Id] = delta;
}
- Offset = new Vector(x, y);
- e.Handled = true;
+ Vector newOffset = new Vector(x, y);
+ bool offsetChanged = newOffset != Offset;
+ Offset = newOffset;
+
+ e.Handled = !IsScrollChainingEnabled || offsetChanged;
}
}
- private void OnScrollGestureEnded(object sender, ScrollGestureEndedEventArgs e)
+ private void OnScrollGestureEnded(object? sender, ScrollGestureEndedEventArgs e)
=> _activeLogicalGestureScrolls?.Remove(e.Id);
///
@@ -440,8 +461,11 @@ namespace Avalonia.Controls.Presenters
x = Math.Min(x, Extent.Width - Viewport.Width);
}
- Offset = new Vector(x, y);
- e.Handled = true;
+ Vector newOffset = new Vector(x, y);
+ bool offsetChanged = newOffset != Offset;
+ Offset = newOffset;
+
+ e.Handled = !IsScrollChainingEnabled || offsetChanged;
}
}
@@ -455,9 +479,10 @@ namespace Avalonia.Controls.Presenters
base.OnPropertyChanged(change);
}
- private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e)
+ private void BringIntoViewRequested(object? sender, RequestBringIntoViewEventArgs e)
{
- e.Handled = BringDescendantIntoView(e.TargetObject, e.TargetRect);
+ if (e.TargetObject is not null)
+ e.Handled = BringDescendantIntoView(e.TargetObject, e.TargetRect);
}
private void ChildChanged(AvaloniaPropertyChangedEventArgs e)
@@ -496,9 +521,9 @@ namespace Avalonia.Controls.Presenters
}
}
- private void ScrollInvalidated(object sender, EventArgs e)
+ private void ScrollInvalidated(object? sender, EventArgs e)
{
- UpdateFromScrollable((ILogicalScrollable)sender);
+ UpdateFromScrollable((ILogicalScrollable)sender!);
}
private void UpdateFromScrollable(ILogicalScrollable scrollable)
@@ -555,7 +580,7 @@ namespace Avalonia.Controls.Presenters
// We have a candidate, calculate its bounds relative to Child. Because these
// bounds aren't relative to the ScrollContentPresenter itself, if they change
// then we know it wasn't just due to scrolling.
- var unscrolledBounds = TranslateBounds(bestCandidate, Child);
+ var unscrolledBounds = TranslateBounds(bestCandidate, Child!);
_anchorElement = bestCandidate;
_anchorElementBounds = unscrolledBounds;
}
@@ -563,7 +588,7 @@ namespace Avalonia.Controls.Presenters
private bool GetViewportBounds(IControl element, out Rect bounds)
{
- if (TranslateBounds(element, Child, out var childBounds))
+ if (TranslateBounds(element, Child!, out var childBounds))
{
// We want the bounds relative to the new Offset, regardless of whether the child
// control has actually been arranged to this offset yet, so translate first to the
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index ff63e5644f..10ce31088a 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -1,9 +1,10 @@
using System;
-using System.Reactive.Linq;
-using Avalonia.Input.TextInput;
+using System.Collections.Generic;
using Avalonia.Media;
+using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
using Avalonia.Threading;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
using Avalonia.Layout;
using Avalonia.Media.Immutable;
@@ -23,14 +24,14 @@ namespace Avalonia.Controls.Presenters
public static readonly StyledProperty PasswordCharProperty =
AvaloniaProperty.Register(nameof(PasswordChar));
- public static readonly StyledProperty SelectionBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionBrushProperty));
+ public static readonly StyledProperty SelectionBrushProperty =
+ AvaloniaProperty.Register(nameof(SelectionBrushProperty));
- public static readonly StyledProperty SelectionForegroundBrushProperty =
- AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty));
+ public static readonly StyledProperty SelectionForegroundBrushProperty =
+ AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty));
- public static readonly StyledProperty CaretBrushProperty =
- AvaloniaProperty.Register(nameof(CaretBrushProperty));
+ public static readonly StyledProperty CaretBrushProperty =
+ AvaloniaProperty.Register(nameof(CaretBrushProperty));
public static readonly DirectProperty SelectionStartProperty =
TextBox.SelectionStartProperty.AddOwner(
@@ -45,8 +46,8 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
@@ -66,7 +67,7 @@ namespace Avalonia.Controls.Presenters
///
/// Defines the property.
///
- public static readonly StyledProperty BackgroundProperty =
+ public static readonly StyledProperty BackgroundProperty =
Border.BackgroundProperty.AddOwner();
private readonly DispatcherTimer _caretTimer;
@@ -74,29 +75,17 @@ namespace Avalonia.Controls.Presenters
private int _selectionStart;
private int _selectionEnd;
private bool _caretBlink;
- private string _text;
- private FormattedText _formattedText;
+ private string? _text;
+ private TextLayout? _textLayout;
private Size _constraint;
+ private CharacterHit _lastCharacterHit;
+ private Rect _caretBounds;
+ private Point _navigationPosition;
+
static TextPresenter()
{
- AffectsRender(SelectionBrushProperty, TextBlock.ForegroundProperty,
- SelectionForegroundBrushProperty, CaretBrushProperty,
- SelectionStartProperty, SelectionEndProperty);
-
- AffectsMeasure(TextProperty, PasswordCharProperty, RevealPasswordProperty,
- TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
- TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);
-
- Observable.Merge(TextProperty.Changed, TextBlock.ForegroundProperty.Changed,
- TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
- TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed,
- TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed,
- SelectionStartProperty.Changed, SelectionEndProperty.Changed,
- SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed, RevealPasswordProperty.Changed
- ).AddClassHandler((x, _) => x.InvalidateFormattedText());
-
- CaretIndexProperty.Changed.AddClassHandler((x, e) => x.CaretIndexChanged((int)e.NewValue));
+ AffectsRender(CaretBrushProperty, SelectionBrushProperty);
}
public TextPresenter()
@@ -106,10 +95,12 @@ namespace Avalonia.Controls.Presenters
_caretTimer.Tick += CaretTimerTick;
}
+ public event EventHandler? CaretBoundsChanged;
+
///
/// Gets or sets a brush used to paint the control's background.
///
- public IBrush Background
+ public IBrush? Background
{
get => GetValue(BackgroundProperty);
set => SetValue(BackgroundProperty, value);
@@ -119,7 +110,7 @@ namespace Avalonia.Controls.Presenters
/// Gets or sets the text.
///
[Content]
- public string Text
+ public string? Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
@@ -161,10 +152,19 @@ namespace Avalonia.Controls.Presenters
set => TextBlock.SetFontWeight(this, value);
}
+ ///
+ /// Gets or sets the font stretch.
+ ///
+ public FontStretch FontStretch
+ {
+ get => TextBlock.GetFontStretch(this);
+ set => TextBlock.SetFontStretch(this, value);
+ }
+
///
/// Gets or sets a brush used to paint the text.
///
- public IBrush Foreground
+ public IBrush? Foreground
{
get => TextBlock.GetForeground(this);
set => TextBlock.SetForeground(this, value);
@@ -189,13 +189,22 @@ namespace Avalonia.Controls.Presenters
}
///
- /// Gets the used to render the text.
+ /// Gets the used to render the text.
///
- public FormattedText FormattedText
+ public TextLayout TextLayout
{
get
{
- return _formattedText ?? (_formattedText = CreateFormattedText());
+ if (_textLayout != null)
+ {
+ return _textLayout;
+ }
+
+ _textLayout = CreateTextLayout();
+
+ UpdateCaret(_lastCharacterHit);
+
+ return _textLayout;
}
}
@@ -205,11 +214,12 @@ namespace Avalonia.Controls.Presenters
{
return _caretIndex;
}
-
set
{
- value = CoerceCaretIndex(value);
- SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
+ if (value != _caretIndex)
+ {
+ MoveCaretToTextPosition(value);
+ }
}
}
@@ -225,19 +235,19 @@ namespace Avalonia.Controls.Presenters
set => SetValue(RevealPasswordProperty, value);
}
- public IBrush SelectionBrush
+ public IBrush? SelectionBrush
{
get => GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
- public IBrush SelectionForegroundBrush
+ public IBrush? SelectionForegroundBrush
{
get => GetValue(SelectionForegroundBrushProperty);
set => SetValue(SelectionForegroundBrushProperty, value);
}
- public IBrush CaretBrush
+ public IBrush? CaretBrush
{
get => GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
@@ -271,37 +281,26 @@ namespace Avalonia.Controls.Presenters
}
}
- public int GetCaretIndex(Point point)
- {
- var hit = FormattedText.HitTestPoint(point);
- return hit.TextPosition + (hit.IsTrailing ? 1 : 0);
- }
-
///
- /// Creates the used to render the text.
+ /// Creates the used to render the text.
///
/// The constraint of the text.
/// The text to format.
- /// A object.
- private FormattedText CreateFormattedTextInternal(Size constraint, string text)
- {
- return new FormattedText
- {
- Constraint = constraint,
- Typeface = new Typeface(FontFamily, FontStyle, FontWeight),
- FontSize = FontSize,
- Text = text ?? string.Empty,
- TextAlignment = TextAlignment,
- TextWrapping = TextWrapping,
- };
- }
+ ///
+ ///
+ /// A object.
+ private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typeface typeface,
+ IReadOnlyList>? textStyleOverrides)
+ {
+ var foreground = Foreground;
+ var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width;
+ var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height;
+
+ var textLayout = new TextLayout(text, typeface, FontSize, foreground, TextAlignment,
+ TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides,
+ flowDirection: FlowDirection);
- ///
- /// Invalidates .
- ///
- protected void InvalidateFormattedText()
- {
- _formattedText = null;
+ return textLayout;
}
///
@@ -317,84 +316,94 @@ namespace Avalonia.Controls.Presenters
context.FillRectangle(background, new Rect(Bounds.Size));
}
- double top = 0;
- var textSize = FormattedText.Bounds.Size;
+ var top = 0d;
+ var left = 0.0;
- if (Bounds.Height < textSize.Height)
+ var textHeight = TextLayout.Bounds.Height;
+
+ if (Bounds.Height < textHeight)
{
switch (VerticalAlignment)
{
case VerticalAlignment.Center:
- top += (Bounds.Height - textSize.Height) / 2;
+ top += (Bounds.Height - textHeight) / 2;
break;
case VerticalAlignment.Bottom:
- top += (Bounds.Height - textSize.Height);
+ top += (Bounds.Height - textHeight);
break;
}
}
- context.DrawText(Foreground, new Point(0, top), FormattedText);
+ TextLayout.Draw(context, new Point(left, top));
}
public override void Render(DrawingContext context)
{
- FormattedText.Constraint = Bounds.Size;
-
- _constraint = Bounds.Size;
-
var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd;
+ var selectionBrush = SelectionBrush;
- if (selectionStart != selectionEnd)
+ if (selectionStart != selectionEnd && selectionBrush != null)
{
var start = Math.Min(selectionStart, selectionEnd);
var length = Math.Max(selectionStart, selectionEnd) - start;
- var rects = FormattedText.HitTestTextRange(start, length);
+ var rects = TextLayout.HitTestTextRange(start, length);
foreach (var rect in rects)
{
- context.FillRectangle(SelectionBrush, rect);
+ context.FillRectangle(selectionBrush, rect);
}
}
RenderInternal(context);
- if (selectionStart == selectionEnd && _caretBlink)
+ if (selectionStart != selectionEnd || !_caretBlink)
{
- var caretBrush = CaretBrush?.ToImmutable();
+ return;
+ }
+
+ var caretBrush = CaretBrush?.ToImmutable();
+
+ if (caretBrush is null)
+ {
+ var backgroundColor = (Background as ISolidColorBrush)?.Color;
- if (caretBrush is null)
+ if (backgroundColor.HasValue)
{
- var backgroundColor = (Background as ISolidColorBrush)?.Color;
- if (backgroundColor.HasValue)
- {
- byte red = (byte)~(backgroundColor.Value.R);
- byte green = (byte)~(backgroundColor.Value.G);
- byte blue = (byte)~(backgroundColor.Value.B);
+ var red = (byte)~(backgroundColor.Value.R);
+ var green = (byte)~(backgroundColor.Value.G);
+ var blue = (byte)~(backgroundColor.Value.B);
- caretBrush = new ImmutableSolidColorBrush(Color.FromRgb(red, green, blue));
- }
- else
- {
- caretBrush = Brushes.Black;
- }
+ caretBrush = new ImmutableSolidColorBrush(Color.FromRgb(red, green, blue));
+ }
+ else
+ {
+ caretBrush = Brushes.Black;
}
-
- var (p1, p2) = GetCaretPoints();
- context.DrawLine(
- new ImmutablePen(caretBrush, 1),
- p1, p2);
}
- }
- (Point, Point) GetCaretPoints()
+ var (p1, p2) = GetCaretPoints();
+
+ context.DrawLine(new ImmutablePen(caretBrush), p1, p2);
+ }
+
+ private (Point, Point) GetCaretPoints()
{
- var charPos = FormattedText.HitTestTextPosition(CaretIndex);
- var x = Math.Floor(charPos.X) + 0.5;
- var y = Math.Floor(charPos.Y) + 0.5;
- var b = Math.Ceiling(charPos.Bottom) - 0.5;
+ var x = Math.Floor(_caretBounds.X) + 0.5;
+ var y = Math.Floor(_caretBounds.Y) + 0.5;
+ var b = Math.Ceiling(_caretBounds.Bottom) - 0.5;
+
+ var caretIndex = _lastCharacterHit.FirstCharacterIndex + _lastCharacterHit.TrailingLength;
+ var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, _lastCharacterHit.TrailingLength > 0);
+ var textLine = TextLayout.TextLines[lineIndex];
+
+ if (_caretBounds.X > 0 && _caretBounds.X >= textLine.WidthIncludingTrailingWhitespace)
+ {
+ x -= 1;
+ }
+
return (new Point(x, y), new Point(x, b));
}
@@ -412,144 +421,382 @@ namespace Avalonia.Controls.Presenters
InvalidateVisual();
}
- internal void CaretIndexChanged(int caretIndex)
+ internal void CaretChanged()
{
- if (this.GetVisualParent() != null)
+ if (this.GetVisualParent() == null)
{
- if (_caretTimer.IsEnabled)
- {
- _caretBlink = true;
- _caretTimer.Stop();
- _caretTimer.Start();
- InvalidateVisual();
- }
- else
- {
- _caretTimer.Start();
- InvalidateVisual();
- _caretTimer.Stop();
- }
+ return;
+ }
- if (IsMeasureValid)
- {
- var rect = FormattedText.HitTestTextPosition(caretIndex);
- this.BringIntoView(rect);
- }
- else
- {
- // The measure is currently invalid so there's no point trying to bring the
- // current char into view until a measure has been carried out as the scroll
- // viewer extents may not be up-to-date.
- Dispatcher.UIThread.Post(
- () =>
- {
- var rect = FormattedText.HitTestTextPosition(caretIndex);
- this.BringIntoView(rect);
- },
- DispatcherPriority.Render);
- }
+ if (_caretTimer.IsEnabled)
+ {
+ _caretBlink = true;
+ _caretTimer.Stop();
+ _caretTimer.Start();
+ InvalidateVisual();
+ }
+ else
+ {
+ _caretTimer.Start();
+ InvalidateVisual();
+ _caretTimer.Stop();
+ }
+
+ if (IsMeasureValid)
+ {
+ this.BringIntoView(_caretBounds);
+ }
+ else
+ {
+ // The measure is currently invalid so there's no point trying to bring the
+ // current char into view until a measure has been carried out as the scroll
+ // viewer extents may not be up-to-date.
+ Dispatcher.UIThread.Post(
+ () =>
+ {
+ this.BringIntoView(_caretBounds);
+ },
+ DispatcherPriority.Render);
}
}
///
- /// Creates the used to render the text.
+ /// Creates the used to render the text.
///
- /// A object.
- protected virtual FormattedText CreateFormattedText()
+ /// A object.
+ protected virtual TextLayout CreateTextLayout()
{
- FormattedText result = null;
+ TextLayout result;
var text = Text;
- if (PasswordChar != default(char) && !RevealPassword)
- {
- result = CreateFormattedTextInternal(_constraint, new string(PasswordChar, text?.Length ?? 0));
- }
- else
- {
- result = CreateFormattedTextInternal(_constraint, text);
- }
+ var typeface = new Typeface(FontFamily, FontStyle, FontWeight);
- var selectionStart = SelectionStart;
- var selectionEnd = SelectionEnd;
+ var selectionStart = CoerceCaretIndex(SelectionStart);
+ var selectionEnd = CoerceCaretIndex(SelectionEnd);
var start = Math.Min(selectionStart, selectionEnd);
var length = Math.Max(selectionStart, selectionEnd) - start;
+ IReadOnlyList>? textStyleOverrides = null;
+
if (length > 0)
{
- result.Spans = new[]
+ textStyleOverrides = new[]
{
- new FormattedTextStyleSpan(start, length, SelectionForegroundBrush),
+ new ValueSpan(start, length,
+ new GenericTextRunProperties(typeface, FontSize,
+ foregroundBrush: SelectionForegroundBrush ?? Brushes.White))
};
}
+ if (PasswordChar != default(char) && !RevealPassword)
+ {
+ result = CreateTextLayoutInternal(_constraint, new string(PasswordChar, text?.Length ?? 0), typeface,
+ textStyleOverrides);
+ }
+ else
+ {
+ result = CreateTextLayoutInternal(_constraint, text, typeface, textStyleOverrides);
+ }
+
return result;
}
- ///
- /// Measures the control.
- ///
- /// The available size for the control.
- /// The desired size.
- private Size MeasureInternal(Size availableSize)
+ protected virtual void InvalidateTextLayout()
+ {
+ _textLayout = null;
+
+ InvalidateMeasure();
+ }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ _constraint = availableSize;
+
+ _textLayout = null;
+
+ InvalidateArrange();
+
+ var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1);
+
+ return new Size(measuredSize.Width, measuredSize.Height);
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ if (MathUtilities.AreClose(_constraint.Width, finalSize.Width))
+ {
+ return finalSize;
+ }
+
+ _constraint = finalSize;
+
+ _textLayout = null;
+
+ return finalSize;
+ }
+
+ private int CoerceCaretIndex(int value)
+ {
+ var text = Text;
+ var length = text?.Length ?? 0;
+ return Math.Max(0, Math.Min(length, value));
+ }
+
+ private void CaretTimerTick(object? sender, EventArgs e)
{
- if (!string.IsNullOrEmpty(Text))
+ _caretBlink = !_caretBlink;
+
+ InvalidateVisual();
+ }
+
+ public void MoveCaretToTextPosition(int textPosition, bool trailingEdge = false)
+ {
+ var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(textPosition, trailingEdge);
+ var textLine = TextLayout.TextLines[lineIndex];
+
+ var characterHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(textPosition));
+
+ var nextCaretCharacterHit = textLine.GetNextCaretCharacterHit(characterHit);
+
+ if (nextCaretCharacterHit.FirstCharacterIndex <= textPosition)
+ {
+ characterHit = nextCaretCharacterHit;
+ }
+
+ if (textPosition == characterHit.FirstCharacterIndex + characterHit.TrailingLength)
+ {
+ UpdateCaret(characterHit);
+ }
+ else
+ {
+ UpdateCaret(trailingEdge ? characterHit : new CharacterHit(characterHit.FirstCharacterIndex));
+ }
+
+ _navigationPosition = _caretBounds.Position;
+
+ CaretChanged();
+ }
+
+ public void MoveCaretToPoint(Point point)
+ {
+ var hit = TextLayout.HitTestPoint(point);
+
+ UpdateCaret(hit.CharacterHit);
+
+ _navigationPosition = _caretBounds.Position;
+
+ CaretChanged();
+ }
+
+ public void MoveCaretVertical(LogicalDirection direction = LogicalDirection.Forward)
+ {
+ var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(CaretIndex, _lastCharacterHit.TrailingLength > 0);
+
+ if (lineIndex < 0)
+ {
+ return;
+ }
+
+ var (currentX, currentY) = _navigationPosition;
+
+ if (direction == LogicalDirection.Forward)
{
- if (TextWrapping == TextWrapping.Wrap)
+ if (lineIndex + 1 > TextLayout.TextLines.Count - 1)
{
- _constraint = new Size(availableSize.Width, double.PositiveInfinity);
+ return;
}
- else
+
+ var textLine = TextLayout.TextLines[lineIndex];
+
+ currentY += textLine.Height;
+ }
+ else
+ {
+ if (lineIndex - 1 < 0)
{
- _constraint = Size.Infinity;
+ return;
}
- _formattedText = null;
+ var textLine = TextLayout.TextLines[--lineIndex];
- return FormattedText.Bounds.Size;
+ currentY -= textLine.Height;
}
- return new Size();
+ var navigationPosition = _navigationPosition;
+
+ MoveCaretToPoint(new Point(currentX, currentY));
+
+ _navigationPosition = navigationPosition.WithY(_caretBounds.Y);
+
+ CaretChanged();
}
- protected override Size MeasureOverride(Size availableSize)
+ public CharacterHit GetNextCharacterHit(LogicalDirection direction = LogicalDirection.Forward)
{
- var text = Text;
+ if (Text is null)
+ {
+ return default;
+ }
+
+ if (FlowDirection == FlowDirection.RightToLeft)
+ {
+ direction = direction == LogicalDirection.Forward ?
+ LogicalDirection.Backward :
+ LogicalDirection.Forward;
+ }
+
+ var characterHit = _lastCharacterHit;
+ var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+
+ var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false);
- if (!string.IsNullOrEmpty(text))
+ if (lineIndex < 0)
{
- return MeasureInternal(availableSize);
+ return default;
+ }
+
+ if (direction == LogicalDirection.Forward)
+ {
+ while (lineIndex < TextLayout.TextLines.Count)
+ {
+ var textLine = TextLayout.TextLines[lineIndex];
+
+ characterHit = textLine.GetNextCaretCharacterHit(characterHit);
+
+ caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+
+ if (textLine.NewLineLength > 0 && caretIndex == textLine.TextRange.Start + textLine.TextRange.Length)
+ {
+ characterHit = new CharacterHit(caretIndex);
+ }
+
+ if (caretIndex >= Text.Length)
+ {
+ characterHit = new CharacterHit(Text.Length);
+
+ break;
+ }
+
+ if (caretIndex - textLine.NewLineLength == textLine.TextRange.Start + textLine.TextRange.Length)
+ {
+ break;
+ }
+
+ if (caretIndex <= CaretIndex)
+ {
+ lineIndex++;
+
+ continue;
+ }
+
+ break;
+ }
}
else
{
- return new FormattedText
+ while (lineIndex >= 0)
{
- Text = "X",
- Typeface = new Typeface(FontFamily, FontStyle, FontWeight),
- FontSize = FontSize,
- TextAlignment = TextAlignment,
- Constraint = availableSize,
- }.Bounds.Size;
+ var textLine = TextLayout.TextLines[lineIndex];
+
+ characterHit = textLine.GetPreviousCaretCharacterHit(characterHit);
+
+ caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+
+ if (caretIndex >= CaretIndex)
+ {
+ lineIndex--;
+
+ continue;
+ }
+
+ break;
+ }
}
- }
- private int CoerceCaretIndex(int value)
+ return characterHit;
+ }
+
+ public void MoveCaretHorizontal(LogicalDirection direction = LogicalDirection.Forward)
{
- var text = Text;
- var length = text?.Length ?? 0;
- return Math.Max(0, Math.Min(length, value));
+ var characterHit = GetNextCharacterHit(direction);
+
+ UpdateCaret(characterHit);
+
+ _navigationPosition = _caretBounds.Position;
+
+ CaretChanged();
}
- private void CaretTimerTick(object sender, EventArgs e)
+ private void UpdateCaret(CharacterHit characterHit)
{
- _caretBlink = !_caretBlink;
- InvalidateVisual();
+ _lastCharacterHit = characterHit;
+
+ var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
+
+ var lineIndex = TextLayout.GetLineIndexFromCharacterIndex(caretIndex, characterHit.TrailingLength > 0);
+ var textLine = TextLayout.TextLines[lineIndex];
+ var distanceX = textLine.GetDistanceFromCharacterHit(characterHit);
+
+ var distanceY = 0d;
+
+ for (var i = 0; i < lineIndex; i++)
+ {
+ var currentLine = TextLayout.TextLines[i];
+
+ distanceY += currentLine.Height;
+ }
+
+ var caretBounds = new Rect(distanceX, distanceY, 0, textLine.Height);
+
+ if (caretBounds != _caretBounds)
+ {
+ _caretBounds = caretBounds;
+
+ CaretBoundsChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ SetAndRaise(CaretIndexProperty, ref _caretIndex, caretIndex);
}
internal Rect GetCursorRectangle()
{
- var (p1, p2) = GetCaretPoints();
- return new Rect(p1, p2);
+ return _caretBounds;
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _caretTimer.Stop();
+
+ _caretTimer.Tick -= CaretTimerTick;
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ switch (change.Property.Name)
+ {
+ case nameof (TextBlock.Foreground):
+ case nameof (TextBlock.FontSize):
+ case nameof (TextBlock.FontStyle):
+ case nameof (TextBlock.FontWeight):
+ case nameof (TextBlock.FontFamily):
+ case nameof (Text):
+ case nameof (TextAlignment):
+ case nameof (TextWrapping):
+ case nameof (SelectionStart):
+ case nameof (SelectionEnd):
+ case nameof (SelectionForegroundBrush):
+ case nameof (PasswordChar):
+ case nameof (RevealPassword):
+ {
+ InvalidateTextLayout();
+ break;
+ }
+ }
}
}
}
diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs
index 3c82386991..87cf660cad 100644
--- a/src/Avalonia.Controls/Primitives/AccessText.cs
+++ b/src/Avalonia.Controls/Primitives/AccessText.cs
@@ -1,4 +1,6 @@
using System;
+using System.Diagnostics.CodeAnalysis;
+using Avalonia.Automation.Peers;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
@@ -19,7 +21,7 @@ namespace Avalonia.Controls.Primitives
///
/// The access key handler for the current window.
///
- private IAccessKeyHandler _accessKeys;
+ private IAccessKeyHandler? _accessKeys;
///
/// Initializes static members of the class.
@@ -67,7 +69,7 @@ namespace Avalonia.Controls.Primitives
if (underscore != -1 && ShowAccessKey)
{
- var rect = TextLayout.HitTestTextPosition(underscore);
+ var rect = TextLayout!.HitTestTextPosition(underscore);
var offset = new Vector(0, -1.5);
context.DrawLine(
new Pen(Foreground, 1),
@@ -77,9 +79,9 @@ namespace Avalonia.Controls.Primitives
}
///
- protected override TextLayout CreateTextLayout(Size constraint, string text)
+ protected override TextLayout CreateTextLayout(Size constraint, string? text)
{
- return base.CreateTextLayout(constraint, StripAccessKey(text));
+ return base.CreateTextLayout(constraint, RemoveAccessKeyMarker(text));
}
///
@@ -106,30 +108,47 @@ namespace Avalonia.Controls.Primitives
}
}
- ///
- /// Returns a string with the first underscore stripped.
- ///
- /// The text.
- /// The text with the first underscore stripped.
- private string StripAccessKey(string text)
+ protected override AutomationPeer OnCreateAutomationPeer()
{
- var position = text.IndexOf('_');
+ return new NoneAutomationPeer(this);
+ }
- if (position == -1)
+ internal static string? RemoveAccessKeyMarker(string? text)
+ {
+ if (!string.IsNullOrEmpty(text))
{
- return text;
+ var accessKeyMarker = "_";
+ var doubleAccessKeyMarker = accessKeyMarker + accessKeyMarker;
+ int index = FindAccessKeyMarker(text);
+ if (index >= 0 && index < text.Length - 1)
+ text = text.Remove(index, 1);
+ text = text.Replace(doubleAccessKeyMarker, accessKeyMarker);
}
- else
+ return text;
+ }
+
+ private static int FindAccessKeyMarker(string text)
+ {
+ var length = text.Length;
+ var startIndex = 0;
+ while (startIndex < length)
{
- return text.Substring(0, position) + text.Substring(position + 1);
+ int index = text.IndexOf('_', startIndex);
+ if (index == -1)
+ return -1;
+ if (index + 1 < length && text[index + 1] != '_')
+ return index;
+ startIndex = index + 2;
}
+
+ return -1;
}
///
/// Called when the property changes.
///
/// The new text.
- private void TextChanged(string text)
+ private void TextChanged(string? text)
{
var key = (char)0;
diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index 2ead82f95d..353f12118f 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
@@ -4,8 +4,6 @@ using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
@@ -142,12 +140,12 @@ namespace Avalonia.Controls.Primitives
clip.Rect = clipBounds;
}
- private void ChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ private void ChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- foreach (Visual i in e.NewItems)
+ foreach (Visual i in e.NewItems!)
{
UpdateAdornedElement(i, i.GetValue(AdornedElementProperty));
}
diff --git a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
index 7171ecc302..a774368af6 100644
--- a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
+++ b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
@@ -2,8 +2,6 @@
using Avalonia.Rendering;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest
diff --git a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs b/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
index e55bdbb0eb..d44ef5666c 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
@@ -2,8 +2,6 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
diff --git a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
index be3c100ecf..2f051c4fda 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
@@ -1,5 +1,4 @@
using Avalonia.Collections;
-using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.LogicalTree;
@@ -13,7 +12,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the property.
///
- public static readonly StyledProperty HeaderProperty =
+ public static readonly StyledProperty HeaderProperty =
HeaderedContentControl.HeaderProperty.AddOwner();
///
@@ -27,7 +26,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets or sets the content of the control's header.
///
- public object Header
+ public object? Header
{
get { return GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
@@ -36,7 +35,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets the header presenter from the control's template.
///
- public IContentPresenter HeaderPresenter
+ public IContentPresenter? HeaderPresenter
{
get;
private set;
diff --git a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
index 78b5771b80..49fc58c8f5 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
@@ -1,5 +1,4 @@
using Avalonia.Collections;
-using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters;
using Avalonia.LogicalTree;
@@ -13,7 +12,7 @@ namespace Avalonia.Controls.Primitives
///
/// Defines the property.
///
- public static readonly StyledProperty HeaderProperty =
+ public static readonly StyledProperty HeaderProperty =
HeaderedContentControl.HeaderProperty.AddOwner();
///
@@ -27,7 +26,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets or sets the content of the control's header.
///
- public object Header
+ public object? Header
{
get { return GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
@@ -36,7 +35,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets the header presenter from the control's template.
///
- public IContentPresenter HeaderPresenter
+ public IContentPresenter? HeaderPresenter
{
get;
private set;
diff --git a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
index 5c29945735..0b3791be3a 100644
--- a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
+++ b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
@@ -48,7 +48,7 @@ namespace Avalonia.Controls.Primitives
/// This event notifies an attached of a change in
/// one of the scroll properties.
///
- event EventHandler ScrollInvalidated;
+ event EventHandler? ScrollInvalidated;
///
/// Attempts to bring a portion of the target visual into view by scrolling the content.
@@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives
/// The movement direction.
/// The control from which movement begins.
/// The control.
- IControl GetControlInDirection(NavigationDirection direction, IControl from);
+ IControl? GetControlInDirection(NavigationDirection direction, IControl? from);
///
/// Raises the event.
diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs
index ab81fe869e..36d2ae9230 100644
--- a/src/Avalonia.Controls/Primitives/IPopupHost.cs
+++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs
@@ -20,23 +20,23 @@ namespace Avalonia.Controls.Primitives
/// Sets the control to display in the popup.
///
///
- void SetChild(IControl control);
+ void SetChild(IControl? control);
///
/// Gets the presenter from the control's template.
///
- IContentPresenter Presenter { get; }
+ IContentPresenter? Presenter { get; }
///
/// Gets the root of the visual tree in the case where the popup is presented using a
/// separate visual tree.
///
- IVisual HostedVisualTreeRoot { get; }
+ IVisual? HostedVisualTreeRoot { get; }
///
/// Raised when the control's template is applied.
///
- event EventHandler TemplateApplied;
+ event EventHandler? TemplateApplied;
///
/// Configures the position of the popup according to a target control and a set of
diff --git a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
index 74d804f2bf..04c30b0f33 100644
--- a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
+++ b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
@@ -7,8 +7,6 @@ using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
@@ -52,7 +50,7 @@ namespace Avalonia.Controls.Primitives
{
if (InputPassThroughElement is object)
{
- var hit = VisualRoot.GetVisualAt(point, x => x != this);
+ var hit = VisualRoot?.GetVisualAt(point, x => x != this);
if (hit is object)
{
diff --git a/src/Avalonia.Controls/Primitives/OverlayLayer.cs b/src/Avalonia.Controls/Primitives/OverlayLayer.cs
index 5150033a53..6a0408d6d1 100644
--- a/src/Avalonia.Controls/Primitives/OverlayLayer.cs
+++ b/src/Avalonia.Controls/Primitives/OverlayLayer.cs
@@ -7,7 +7,7 @@ namespace Avalonia.Controls.Primitives
public class OverlayLayer : Canvas, ICustomSimpleHitTest
{
public Size AvailableSize { get; private set; }
- public static OverlayLayer GetOverlayLayer(IVisual visual)
+ public static OverlayLayer? GetOverlayLayer(IVisual visual)
{
foreach(var v in visual.GetVisualAncestors())
if(v is VisualLayerManager vlm)
diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
index 403902f676..6251d5cda7 100644
--- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
+++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
@@ -23,15 +23,15 @@ namespace Avalonia.Controls.Primitives
_positioner = new ManagedPopupPositioner(this);
}
- public void SetChild(IControl control)
+ public void SetChild(IControl? control)
{
Content = control;
}
- public IVisual HostedVisualTreeRoot => null;
+ public IVisual? HostedVisualTreeRoot => null;
///
- IInteractive IInteractive.InteractiveParent => Parent;
+ IInteractive? IInteractive.InteractiveParent => Parent;
public void Dispose() => Hide();
@@ -75,7 +75,7 @@ namespace Avalonia.Controls.Primitives
PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All,
Rect? rect = null)
{
- _positionerParameters.ConfigurePosition((TopLevel)_overlayLayer.GetVisualRoot(), target, placement, offset, anchor,
+ _positionerParameters.ConfigurePosition((TopLevel)_overlayLayer.GetVisualRoot()!, target, placement, offset, anchor,
gravity, constraintAdjustment, rect);
UpdatePosition();
}
@@ -126,11 +126,11 @@ namespace Avalonia.Controls.Primitives
double IManagedPopupPositionerPopup.Scaling => 1;
- public static IPopupHost CreatePopupHost(IVisual target, IAvaloniaDependencyResolver dependencyResolver)
+ public static IPopupHost CreatePopupHost(IVisual target, IAvaloniaDependencyResolver? dependencyResolver)
{
var platform = (target.GetVisualRoot() as TopLevel)?.PlatformImpl?.CreatePopup();
if (platform != null)
- return new PopupRoot((TopLevel)target.GetVisualRoot(), platform, dependencyResolver);
+ return new PopupRoot((TopLevel)target.GetVisualRoot()!, platform, dependencyResolver);
var overlayLayer = OverlayLayer.GetOverlayLayer(target);
if (overlayLayer == null)
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index ffab7f86d1..bb546107e0 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -2,6 +2,7 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Presenters;
@@ -14,8 +15,6 @@ using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
@@ -419,7 +418,7 @@ namespace Avalonia.Controls.Primitives
(x, handler) => x.TemplateApplied += handler,
(x, handler) => x.TemplateApplied -= handler).DisposeWith(handlerCleanup);
- if (topLevel is Window window)
+ if (topLevel is Window window && window.PlatformImpl != null)
{
SubscribeToEventHandler(window, WindowDeactivated,
(x, handler) => x.Deactivated += handler,
@@ -429,16 +428,20 @@ namespace Avalonia.Controls.Primitives
(x, handler) => x.LostFocus += handler,
(x, handler) => x.LostFocus -= handler).DisposeWith(handlerCleanup);
- SubscribeToEventHandler>(window.PlatformImpl, WindowPositionChanged,
- (x, handler) => x.PositionChanged += handler,
- (x, handler) => x.PositionChanged -= handler).DisposeWith(handlerCleanup);
-
- if (placementTarget is Layoutable layoutTarget)
+ // Recalculate popup position on parent moved/resized, but not if placement was on pointer
+ if (PlacementMode != PlacementMode.Pointer)
{
- // If the placement target is moved, update the popup position
- SubscribeToEventHandler(layoutTarget, PlacementTargetLayoutUpdated,
- (x, handler) => x.LayoutUpdated += handler,
- (x, handler) => x.LayoutUpdated -= handler).DisposeWith(handlerCleanup);
+ SubscribeToEventHandler>(window.PlatformImpl, WindowPositionChanged,
+ (x, handler) => x.PositionChanged += handler,
+ (x, handler) => x.PositionChanged -= handler).DisposeWith(handlerCleanup);
+
+ if (placementTarget is Layoutable layoutTarget)
+ {
+ // If the placement target is moved, update the popup position
+ SubscribeToEventHandler(layoutTarget, PlacementTargetLayoutUpdated,
+ (x, handler) => x.LayoutUpdated += handler,
+ (x, handler) => x.LayoutUpdated -= handler).DisposeWith(handlerCleanup);
+ }
}
}
else if (topLevel is PopupRoot parentPopupRoot)
@@ -558,6 +561,11 @@ namespace Avalonia.Controls.Primitives
}
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new PopupAutomationPeer(this);
+ }
+
private static IDisposable SubscribeToEventHandler(T target, TEventHandler handler, Action subscribe, Action unsubscribe)
{
subscribe(target, handler);
@@ -567,7 +575,7 @@ namespace Avalonia.Controls.Primitives
private void WindowManagerAddShadowHintChanged(IPopupHost host, bool hint)
{
- if(host is PopupRoot pr)
+ if(host is PopupRoot pr && pr.PlatformImpl is not null)
{
pr.PlatformImpl.SetWindowManagerAddShadowHint(hint);
}
@@ -649,7 +657,17 @@ namespace Avalonia.Controls.Primitives
{
if (PlacementTarget != null)
{
- FocusManager.Instance?.Focus(PlacementTarget);
+ var e = (IControl?)PlacementTarget;
+
+ while (e is object && (!e.Focusable || !e.IsEffectivelyEnabled || !e.IsVisible))
+ {
+ e = e.Parent;
+ }
+
+ if (e is object)
+ {
+ FocusManager.Instance?.Focus(e);
+ }
}
else
{
@@ -672,7 +690,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void PointerPressedDismissOverlay(object sender, PointerPressedEventArgs e)
+ private void PointerPressedDismissOverlay(object? sender, PointerPressedEventArgs e)
{
if (IsLightDismissEnabled && e.Source is IVisual v && !IsChildOrThis(v))
{
@@ -702,7 +720,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void RootTemplateApplied(object sender, TemplateAppliedEventArgs e)
+ private void RootTemplateApplied(object? sender, TemplateAppliedEventArgs e)
{
if (_openState is null)
{
@@ -729,7 +747,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void SetTemplatedParentAndApplyChildTemplates(IControl control)
+ private void SetTemplatedParentAndApplyChildTemplates(IControl? control)
{
if (control != null)
{
@@ -790,7 +808,7 @@ namespace Avalonia.Controls.Primitives
public bool IsPointerOverPopup => ((IInputElement?)_openState?.PopupHost)?.IsPointerOver ?? false;
- private void WindowDeactivated(object sender, EventArgs e)
+ private void WindowDeactivated(object? sender, EventArgs e)
{
if (IsLightDismissEnabled)
{
@@ -798,7 +816,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void ParentClosed(object sender, EventArgs e)
+ private void ParentClosed(object? sender, EventArgs e)
{
if (IsLightDismissEnabled)
{
@@ -814,9 +832,9 @@ namespace Avalonia.Controls.Primitives
private void WindowPositionChanged(PixelPoint pp) => HandlePositionChange();
- private void PlacementTargetLayoutUpdated(object src, EventArgs e) => HandlePositionChange();
+ private void PlacementTargetLayoutUpdated(object? src, EventArgs e) => HandlePositionChange();
- private void ParentPopupPositionChanged(object src, PixelPointEventArgs e) => HandlePositionChange();
+ private void ParentPopupPositionChanged(object? src, PixelPointEventArgs e) => HandlePositionChange();
private IgnoreIsOpenScope BeginIgnoringIsOpen()
{
diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
index 844069965b..340076a407 100644
--- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
+++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs
@@ -448,7 +448,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
{
// We need a better way for tracking the last pointer position
#pragma warning disable CS0618 // Type or member is obsolete
- var pointer = topLevel.PointToClient(topLevel.PlatformImpl.MouseDevice.Position);
+ var pointer = topLevel.PointToClient(topLevel.PlatformImpl?.MouseDevice.Position ?? default);
#pragma warning restore CS0618 // Type or member is obsolete
positionerParameters.Offset = offset;
diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
index 0f0dd7311d..a80a60350e 100644
--- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
+++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs
@@ -106,9 +106,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
{
var screens = _popup.Screens;
- var targetScreen = screens.FirstOrDefault(s => s.Bounds.Contains(anchorRect.TopLeft))
+ var targetScreen = screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(anchorRect.TopLeft))
?? screens.FirstOrDefault(s => s.Bounds.Intersects(anchorRect))
- ?? screens.FirstOrDefault(s => s.Bounds.Contains(parentGeometry.TopLeft))
+ ?? screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(parentGeometry.TopLeft))
?? screens.FirstOrDefault(s => s.Bounds.Intersects(parentGeometry))
?? screens.FirstOrDefault();
diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index c59c7c11d9..2f6df862cf 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Interactivity;
using Avalonia.Media;
@@ -43,8 +44,8 @@ namespace Avalonia.Controls.Primitives
///
/// The dependency resolver to use. If null the default dependency resolver will be used.
///
- public PopupRoot(TopLevel parent, IPopupImpl impl, IAvaloniaDependencyResolver dependencyResolver)
- : base(ValidatingPopupImpl.Wrap(impl), dependencyResolver)
+ public PopupRoot(TopLevel parent, IPopupImpl impl, IAvaloniaDependencyResolver? dependencyResolver)
+ : base(impl, dependencyResolver)
{
_parent = parent;
}
@@ -52,8 +53,7 @@ namespace Avalonia.Controls.Primitives
///
/// Gets the platform-specific window implementation.
///
- [CanBeNull]
- public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl;
+ public new IPopupImpl? PlatformImpl => (IPopupImpl?)base.PlatformImpl;
///
/// Gets the parent control in the event route.
@@ -61,20 +61,24 @@ namespace Avalonia.Controls.Primitives
///
/// Popup events are passed to their parent window. This facilitates this.
///
- IInteractive IInteractive.InteractiveParent => Parent;
+ IInteractive? IInteractive.InteractiveParent => Parent;
///
/// Gets the control that is hosting the popup root.
///
- IVisual IHostedVisualTreeRoot.Host => Parent;
+ IVisual? IHostedVisualTreeRoot.Host => Parent;
///
/// Gets the styling parent of the popup root.
///
- IStyleHost IStyleHost.StylingParent => Parent;
+ IStyleHost? IStyleHost.StylingParent => Parent;
///
- public void Dispose() => PlatformImpl?.Dispose();
+ public void Dispose()
+ {
+ PlatformImpl?.Dispose();
+ HandleClosed();
+ }
private void UpdatePosition()
{
@@ -94,7 +98,7 @@ namespace Avalonia.Controls.Primitives
UpdatePosition();
}
- public void SetChild(IControl control) => Content = control;
+ public void SetChild(IControl? control) => Content = control;
IVisual IPopupHost.HostedVisualTreeRoot => this;
@@ -165,5 +169,10 @@ namespace Avalonia.Controls.Primitives
UpdatePosition();
return ClientSize;
}
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new PopupRootAutomationPeer(this);
+ }
}
}
diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs
index d5d6af8bfa..8460fe3017 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBar.cs
+++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs
@@ -69,11 +69,11 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty ShowDelayProperty =
AvaloniaProperty.Register(nameof(ShowDelay), TimeSpan.FromSeconds(0.5));
- private Button _lineUpButton;
- private Button _lineDownButton;
- private Button _pageUpButton;
- private Button _pageDownButton;
- private DispatcherTimer _timer;
+ private Button? _lineUpButton;
+ private Button? _lineDownButton;
+ private Button? _pageUpButton;
+ private Button? _pageDownButton;
+ private DispatcherTimer? _timer;
private bool _isExpanded;
///
@@ -157,7 +157,7 @@ namespace Avalonia.Controls.Primitives
set => SetValue(ShowDelayProperty, value);
}
- public event EventHandler Scroll;
+ public event EventHandler? Scroll;
///
/// Calculates and updates whether the scrollbar should be visible.
@@ -293,7 +293,7 @@ namespace Avalonia.Controls.Primitives
_timer = new DispatcherTimer(DispatcherPriority.Normal);
_timer.Tick += (sender, args) =>
{
- var senderTimer = (DispatcherTimer)sender;
+ var senderTimer = (DispatcherTimer)sender!;
if (senderTimer.Tag is Action action)
{
@@ -344,22 +344,22 @@ namespace Avalonia.Controls.Primitives
IsExpanded = true;
}
- private void LineUpClick(object sender, RoutedEventArgs e)
+ private void LineUpClick(object? sender, RoutedEventArgs e)
{
SmallDecrement();
}
- private void LineDownClick(object sender, RoutedEventArgs e)
+ private void LineDownClick(object? sender, RoutedEventArgs e)
{
SmallIncrement();
}
- private void PageUpClick(object sender, RoutedEventArgs e)
+ private void PageUpClick(object? sender, RoutedEventArgs e)
{
LargeDecrement();
}
- private void PageDownClick(object sender, RoutedEventArgs e)
+ private void PageDownClick(object? sender, RoutedEventArgs e)
{
LargeIncrement();
}
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 80f3d345a6..c690726e71 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -13,8 +13,6 @@ using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
@@ -116,6 +114,12 @@ namespace Avalonia.Controls.Primitives
"SelectionChanged",
RoutingStrategies.Bubble);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WrapSelectionProperty =
+ AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false);
+
private static readonly IList Empty = Array.Empty();
private string _textSearchTerm = string.Empty;
private DispatcherTimer? _textSearchTimer;
@@ -138,7 +142,7 @@ namespace Avalonia.Controls.Primitives
///
/// Occurs when the control's selection changes.
///
- public event EventHandler SelectionChanged
+ public event EventHandler? SelectionChanged
{
add { AddHandler(SelectionChangedEvent, value); }
remove { RemoveHandler(SelectionChangedEvent, value); }
@@ -323,6 +327,16 @@ namespace Avalonia.Controls.Primitives
set { SetValue(IsTextSearchEnabledProperty, value); }
}
+ ///
+ /// Gets or sets a value which indicates whether to wrap around when the first
+ /// or last item is reached.
+ ///
+ public bool WrapSelection
+ {
+ get { return GetValue(WrapSelectionProperty); }
+ set { SetValue(WrapSelectionProperty, value); }
+ }
+
///
/// Gets or sets the selection mode.
///
@@ -386,9 +400,9 @@ namespace Avalonia.Controls.Primitives
return null;
}
- protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ protected override void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
- base.ItemsCollectionChanged(sender, e);
+ base.ItemsCollectionChanged(sender!, e);
if (AlwaysSelected && SelectedIndex == -1 && ItemCount > 0)
{
@@ -406,7 +420,7 @@ namespace Avalonia.Controls.Primitives
{
base.OnApplyTemplate(e);
- void ExecuteScrollWhenLayoutUpdated(object sender, EventArgs e)
+ void ExecuteScrollWhenLayoutUpdated(object? sender, EventArgs e)
{
LayoutUpdated -= ExecuteScrollWhenLayoutUpdated;
AutoScrollToSelectedItemIfNecessary();
@@ -443,9 +457,7 @@ namespace Avalonia.Controls.Primitives
{
base.OnContainersDematerialized(e);
- var panel = (InputElement)Presenter.Panel;
-
- if (panel != null)
+ if (Presenter?.Panel is InputElement panel)
{
foreach (var container in e.Containers)
{
@@ -519,11 +531,23 @@ namespace Avalonia.Controls.Primitives
_textSearchTerm += e.Text;
- bool match(ItemContainerInfo info) =>
- info.ContainerControl is IContentControl control &&
- control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
+ bool Match(ItemContainerInfo info)
+ {
+ if (info.ContainerControl.IsSet(TextSearch.TextProperty))
+ {
+ var searchText = info.ContainerControl.GetValue(TextSearch.TextProperty);
+
+ if (searchText?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true)
+ {
+ return true;
+ }
+ }
- var info = ItemContainerGenerator.Containers.FirstOrDefault(match);
+ return info.ContainerControl is IContentControl control &&
+ control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
+ }
+
+ var info = ItemContainerGenerator?.Containers.FirstOrDefault(Match);
if (info != null)
{
@@ -584,6 +608,10 @@ namespace Avalonia.Controls.Primitives
var newValue = change.NewValue.GetValueOrDefault();
_selection.SingleSelect = !newValue.HasAllFlags(SelectionMode.Multiple);
}
+ else if (change.Property == WrapSelectionProperty)
+ {
+ WrapFocus = WrapSelection;
+ }
}
///
@@ -594,7 +622,7 @@ namespace Avalonia.Controls.Primitives
/// True if the selection was moved; otherwise false.
protected bool MoveSelection(NavigationDirection direction, bool wrap)
{
- var from = SelectedIndex != -1 ? ItemContainerGenerator.ContainerFromIndex(SelectedIndex) : null;
+ var from = SelectedIndex != -1 ? ItemContainerGenerator?.ContainerFromIndex(SelectedIndex) : null;
return MoveSelection(from, direction, wrap);
}
@@ -610,7 +638,7 @@ namespace Avalonia.Controls.Primitives
if (Presenter?.Panel is INavigableContainer container &&
GetNextControl(container, direction, from, wrap) is IControl next)
{
- var index = ItemContainerGenerator.IndexFromContainer(next);
+ var index = ItemContainerGenerator?.IndexFromContainer(next) ?? -1;
if (index != -1)
{
@@ -688,7 +716,7 @@ namespace Avalonia.Controls.Primitives
if (Presenter?.Panel != null)
{
- var container = ItemContainerGenerator.ContainerFromIndex(index);
+ var container = ItemContainerGenerator?.ContainerFromIndex(index);
KeyboardNavigation.SetTabOnceActiveElement(
(InputElement)Presenter.Panel,
container);
@@ -755,7 +783,7 @@ namespace Avalonia.Controls.Primitives
///
/// The sender.
/// The event args.
- private void OnSelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e)
+ private void OnSelectionModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ISelectionModel.AnchorIndex))
{
@@ -789,11 +817,11 @@ namespace Avalonia.Controls.Primitives
///
/// The sender.
/// The event args.
- private void OnSelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
+ private void OnSelectionModelSelectionChanged(object? sender, SelectionModelSelectionChangedEventArgs e)
{
void Mark(int index, bool selected)
{
- var container = ItemContainerGenerator.ContainerFromIndex(index);
+ var container = ItemContainerGenerator?.ContainerFromIndex(index);
if (container != null)
{
@@ -829,7 +857,7 @@ namespace Avalonia.Controls.Primitives
///
/// The sender.
/// The event args.
- private void OnSelectionModelLostSelection(object sender, EventArgs e)
+ private void OnSelectionModelLostSelection(object? sender, EventArgs e)
{
if (AlwaysSelected && Items is object)
{
@@ -1034,7 +1062,7 @@ namespace Avalonia.Controls.Primitives
_textSearchTimer = null;
}
- private void TextSearchTimer_Tick(object sender, EventArgs e)
+ private void TextSearchTimer_Tick(object? sender, EventArgs e)
{
_textSearchTerm = string.Empty;
StopTextSearchTimer();
diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
index 59975b072d..af681d6930 100644
--- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs
+++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
@@ -6,8 +6,6 @@ using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Styling;
-#nullable enable
-
namespace Avalonia.Controls.Primitives
{
///
@@ -109,7 +107,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when the control's template is applied.
///
- public event EventHandler TemplateApplied
+ public event EventHandler? TemplateApplied
{
add { AddHandler(TemplateAppliedEvent, value); }
remove { RemoveHandler(TemplateAppliedEvent, value); }
diff --git a/src/Avalonia.Controls/Primitives/TextSearch.cs b/src/Avalonia.Controls/Primitives/TextSearch.cs
new file mode 100644
index 0000000000..949532cb16
--- /dev/null
+++ b/src/Avalonia.Controls/Primitives/TextSearch.cs
@@ -0,0 +1,37 @@
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// Allows to customize text searching in .
+ ///
+ public static class TextSearch
+ {
+ ///
+ /// Defines the Text attached property.
+ /// This text will be considered during text search in (such as )
+ ///
+ public static readonly AttachedProperty TextProperty
+ = AvaloniaProperty.RegisterAttached("Text", typeof(TextSearch));
+
+ ///
+ /// Sets the for a control.
+ ///
+ /// The control
+ /// The search text to set
+ public static void SetText(Control control, string text)
+ {
+ control.SetValue(TextProperty, text);
+ }
+
+ ///
+ /// Gets the of a control.
+ ///
+ /// The control
+ /// The property value
+ public static string GetText(Control control)
+ {
+ return control.GetValue(TextProperty);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs
index 91141058e4..cb3195cf52 100644
--- a/src/Avalonia.Controls/Primitives/Thumb.cs
+++ b/src/Avalonia.Controls/Primitives/Thumb.cs
@@ -26,19 +26,19 @@ namespace Avalonia.Controls.Primitives
DragCompletedEvent.AddClassHandler((x, e) => x.OnDragCompleted(e), RoutingStrategies.Bubble);
}
- public event EventHandler DragStarted
+ public event EventHandler? DragStarted
{
add { AddHandler(DragStartedEvent, value); }
remove { RemoveHandler(DragStartedEvent, value); }
}
- public event EventHandler DragDelta
+ public event EventHandler? DragDelta
{
add { AddHandler(DragDeltaEvent, value); }
remove { RemoveHandler(DragDeltaEvent, value); }
}
- public event EventHandler DragCompleted
+ public event EventHandler? DragCompleted
{
add { AddHandler(DragCompletedEvent, value); }
remove { RemoveHandler(DragCompletedEvent, value); }
diff --git a/src/Avalonia.Controls/Primitives/ToggleButton.cs b/src/Avalonia.Controls/Primitives/ToggleButton.cs
index 6b2c566422..148797c53a 100644
--- a/src/Avalonia.Controls/Primitives/ToggleButton.cs
+++ b/src/Avalonia.Controls/Primitives/ToggleButton.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
using Avalonia.Interactivity;
@@ -61,7 +62,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is checked.
///
- public event EventHandler Checked
+ public event EventHandler? Checked
{
add => AddHandler(CheckedEvent, value);
remove => RemoveHandler(CheckedEvent, value);
@@ -70,7 +71,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is unchecked.
///
- public event EventHandler Unchecked
+ public event EventHandler? Unchecked
{
add => AddHandler(UncheckedEvent, value);
remove => RemoveHandler(UncheckedEvent, value);
@@ -79,7 +80,7 @@ namespace Avalonia.Controls.Primitives
///
/// Raised when a is neither checked nor unchecked.
///
- public event EventHandler Indeterminate
+ public event EventHandler? Indeterminate
{
add => AddHandler(IndeterminateEvent, value);
remove => RemoveHandler(IndeterminateEvent, value);
@@ -169,6 +170,11 @@ namespace Avalonia.Controls.Primitives
RaiseEvent(e);
}
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ToggleButtonAutomationPeer(this);
+ }
+
private void OnIsCheckedChanged(AvaloniaPropertyChangedEventArgs e)
{
var newValue = (bool?)e.NewValue;
diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs
index 9399f5fb31..f8d6046101 100644
--- a/src/Avalonia.Controls/Primitives/Track.cs
+++ b/src/Avalonia.Controls/Primitives/Track.cs
@@ -403,8 +403,8 @@ namespace Avalonia.Controls.Primitives
private void ThumbChanged(AvaloniaPropertyChangedEventArgs e)
{
- var oldThumb = (Thumb)e.OldValue;
- var newThumb = (Thumb)e.NewValue;
+ var oldThumb = (Thumb?)e.OldValue;
+ var newThumb = (Thumb?)e.NewValue;
if (oldThumb != null)
{
@@ -424,8 +424,8 @@ namespace Avalonia.Controls.Primitives
private void ButtonChanged(AvaloniaPropertyChangedEventArgs e)
{
- var oldButton = (Button)e.OldValue;
- var newButton = (Button)e.NewValue;
+ var oldButton = (Button?)e.OldValue;
+ var newButton = (Button?)e.NewValue;
if (oldButton != null)
{
@@ -440,7 +440,7 @@ namespace Avalonia.Controls.Primitives
}
}
- private void ThumbDragged(object sender, VectorEventArgs e)
+ private void ThumbDragged(object? sender, VectorEventArgs e)
{
if (IsThumbDragHandled)
return;
diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
index 03a84e26f2..df4edbd252 100644
--- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
+++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Controls.Primitives
private const int LightDismissOverlayZIndex = int.MaxValue - 98;
private const int OverlayZIndex = int.MaxValue - 97;
- private ILogicalRoot _logicalRoot;
+ private ILogicalRoot? _logicalRoot;
private readonly List _layers = new List();
public static readonly StyledProperty ChromeOverlayLayerProperty =
@@ -50,7 +50,7 @@ namespace Avalonia.Controls.Primitives
}
}
- public OverlayLayer OverlayLayer
+ public OverlayLayer? OverlayLayer
{
get
{
@@ -81,7 +81,7 @@ namespace Avalonia.Controls.Primitives
}
}
- T FindLayer() where T : class
+ T? FindLayer() where T : class
{
foreach (var layer in _layers)
if (layer is T match)
@@ -97,7 +97,7 @@ namespace Avalonia.Controls.Primitives
VisualChildren.Add(layer);
if (((ILogical)this).IsAttachedToLogicalTree)
((ILogical)layer).NotifyAttachedToLogicalTree(
- new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
+ new LogicalTreeAttachmentEventArgs(_logicalRoot!, layer, this));
InvalidateArrange();
}
diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs
index a848901bc3..017a053c48 100644
--- a/src/Avalonia.Controls/ProgressBar.cs
+++ b/src/Avalonia.Controls/ProgressBar.cs
@@ -96,7 +96,7 @@ namespace Avalonia.Controls
private double _indeterminateStartingOffset;
private double _indeterminateEndingOffset;
- private Border _indicator;
+ private Border? _indicator;
public static readonly StyledProperty IsIndeterminateProperty =
AvaloniaProperty.Register(nameof(IsIndeterminate));
diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs
index d1743611cd..25330614cf 100644
--- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs
@@ -3,8 +3,10 @@ using Avalonia.Metadata;
[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
+[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Automation")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Embedding")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Presenters")]
@@ -13,3 +15,4 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Templates")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Notifications")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Chrome")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Documents")]
diff --git a/src/Avalonia.Controls/RadioButton.cs b/src/Avalonia.Controls/RadioButton.cs
index 681278d247..9dbc5f040e 100644
--- a/src/Avalonia.Controls/RadioButton.cs
+++ b/src/Avalonia.Controls/RadioButton.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Controls
readonly Dictionary>> s_registeredGroups
= new Dictionary>>();
- public static RadioButtonGroupManager GetOrCreateForRoot(IRenderRoot root)
+ public static RadioButtonGroupManager GetOrCreateForRoot(IRenderRoot? root)
{
if (root == null)
return Default;
@@ -33,7 +33,7 @@ namespace Avalonia.Controls
{
lock (s_registeredGroups)
{
- string groupName = radioButton.GroupName;
+ string groupName = radioButton.GroupName!;
if (!s_registeredGroups.TryGetValue(groupName, out var group))
{
group = new List>();
@@ -71,7 +71,7 @@ namespace Avalonia.Controls
{
lock (s_registeredGroups)
{
- string groupName = radioButton.GroupName;
+ string groupName = radioButton.GroupName!;
if (s_registeredGroups.TryGetValue(groupName, out var group))
{
int i = 0;
@@ -95,21 +95,21 @@ namespace Avalonia.Controls
}
}
- public static readonly DirectProperty GroupNameProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty GroupNameProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(GroupName),
o => o.GroupName,
(o, v) => o.GroupName = v);
- private string _groupName;
- private RadioButtonGroupManager _groupManager;
+ private string? _groupName;
+ private RadioButtonGroupManager? _groupManager;
public RadioButton()
{
this.GetObservable(IsCheckedProperty).Subscribe(IsCheckedChanged);
}
- public string GroupName
+ public string? GroupName
{
get { return _groupName; }
set { SetGroupName(value); }
@@ -127,7 +127,7 @@ namespace Avalonia.Controls
{
if (!string.IsNullOrEmpty(GroupName))
{
- _groupManager?.Remove(this, _groupName);
+ _groupManager?.Remove(this, GroupName);
_groupManager = RadioButtonGroupManager.GetOrCreateForRoot(e.Root);
@@ -142,13 +142,13 @@ namespace Avalonia.Controls
if (!string.IsNullOrEmpty(GroupName))
{
- _groupManager?.Remove(this, _groupName);
+ _groupManager?.Remove(this, GroupName);
}
}
- private void SetGroupName(string newGroupName)
+ private void SetGroupName(string? newGroupName)
{
- string oldGroupName = GroupName;
+ var oldGroupName = GroupName;
if (newGroupName != oldGroupName)
{
if (!string.IsNullOrEmpty(oldGroupName))
@@ -169,7 +169,7 @@ namespace Avalonia.Controls
private void IsCheckedChanged(bool? value)
{
- string groupName = GroupName;
+ var groupName = GroupName;
if (string.IsNullOrEmpty(groupName))
{
var parent = this.GetVisualParent();
diff --git a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
index 562b104b04..18cf96ddca 100644
--- a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
+++ b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
@@ -1,7 +1,5 @@
using Avalonia.Layout;
-#nullable enable
-
namespace Avalonia.Controls
{
public partial class RelativePanel
diff --git a/src/Avalonia.Controls/RelativePanel.cs b/src/Avalonia.Controls/RelativePanel.cs
index c5de004f09..1125f0eb0e 100644
--- a/src/Avalonia.Controls/RelativePanel.cs
+++ b/src/Avalonia.Controls/RelativePanel.cs
@@ -4,8 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.Layout;
-#nullable enable
-
namespace Avalonia.Controls
{
///
diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs
index 419792f004..f4cc91a0e6 100644
--- a/src/Avalonia.Controls/Remote/RemoteServer.cs
+++ b/src/Avalonia.Controls/Remote/RemoteServer.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls.Remote
//TODO: Somehow react on closed connection?
}
- public object Content
+ public object? Content
{
get => _topLevel.Content;
set => _topLevel.Content = value;
diff --git a/src/Avalonia.Controls/Remote/RemoteWidget.cs b/src/Avalonia.Controls/Remote/RemoteWidget.cs
index b839a8769a..a88e0fd3d8 100644
--- a/src/Avalonia.Controls/Remote/RemoteWidget.cs
+++ b/src/Avalonia.Controls/Remote/RemoteWidget.cs
@@ -18,8 +18,8 @@ namespace Avalonia.Controls.Remote
}
private readonly IAvaloniaRemoteTransportConnection _connection;
- private FrameMessage _lastFrame;
- private WriteableBitmap _bitmap;
+ private FrameMessage? _lastFrame;
+ private WriteableBitmap? _bitmap;
public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{
Mode = SizingMode.Local;
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
index 931c27c575..c9fd1dc3b8 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
@@ -21,22 +21,22 @@ namespace Avalonia.Controls.Remote.Server
public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface
{
private readonly IAvaloniaRemoteTransportConnection _transport;
- private LockedFramebuffer _framebuffer;
+ private LockedFramebuffer? _framebuffer;
private object _lock = new object();
private long _lastSentFrame = -1;
private long _lastReceivedFrame = -1;
private long _nextFrameNumber = 1;
- private ClientViewportAllocatedMessage _pendingAllocation;
+ private ClientViewportAllocatedMessage? _pendingAllocation;
private bool _invalidated;
private Vector _dpi = new Vector(96, 96);
- private ProtocolPixelFormat[] _supportedFormats;
+ private ProtocolPixelFormat[]? _supportedFormats;
public RemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport)
{
_transport = transport;
_transport.OnMessage += OnMessage;
- KeyboardDevice = AvaloniaLocator.Current.GetService();
+ KeyboardDevice = AvaloniaLocator.Current.GetRequiredService();
}
private static RawPointerEventType GetAvaloniaEventType (Avalonia.Remote.Protocol.Input.MouseButton button, bool pressed)
@@ -155,7 +155,7 @@ namespace Avalonia.Controls.Remote.Server
ClientViewportAllocatedMessage allocation;
lock (_lock)
{
- allocation = _pendingAllocation;
+ allocation = _pendingAllocation!;
_pendingAllocation = null;
}
_dpi = new Vector(allocation.DpiX, allocation.DpiY);
@@ -173,7 +173,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawPointerEventArgs(
MouseDevice,
0,
- InputRoot,
+ InputRoot!,
RawPointerEventType.Move,
new Point(pointer.X, pointer.Y),
GetAvaloniaInputModifiers(pointer.Modifiers)));
@@ -186,7 +186,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawPointerEventArgs(
MouseDevice,
0,
- InputRoot,
+ InputRoot!,
GetAvaloniaEventType(pressed.Button, true),
new Point(pressed.X, pressed.Y),
GetAvaloniaInputModifiers(pressed.Modifiers)));
@@ -199,7 +199,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawPointerEventArgs(
MouseDevice,
0,
- InputRoot,
+ InputRoot!,
GetAvaloniaEventType(released.Button, false),
new Point(released.X, released.Y),
GetAvaloniaInputModifiers(released.Modifiers)));
@@ -212,7 +212,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawMouseWheelEventArgs(
MouseDevice,
0,
- InputRoot,
+ InputRoot!,
new Point(scroll.X, scroll.Y),
new Vector(scroll.DeltaX, scroll.DeltaY),
GetAvaloniaInputModifiers(scroll.Modifiers)));
@@ -227,7 +227,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice,
0,
- InputRoot,
+ InputRoot!,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key,
GetAvaloniaRawInputModifiers(key.Modifiers)));
@@ -242,7 +242,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice,
0,
- InputRoot,
+ InputRoot!,
text.Text));
}, DispatcherPriority.Input);
}
@@ -257,7 +257,7 @@ namespace Avalonia.Controls.Remote.Server
protected virtual Size Measure(Size constraint)
{
- var l = (ILayoutable) InputRoot;
+ var l = (ILayoutable) InputRoot!;
l.Measure(constraint);
return l.DesiredSize;
}
@@ -294,7 +294,7 @@ namespace Avalonia.Controls.Remote.Server
return new FrameMessage
{
Data = data,
- Format = (ProtocolPixelFormat) format,
+ Format = fmt,
Width = width,
Height = height,
Stride = width * bpp,
diff --git a/src/Avalonia.Controls/RepeatButton.cs b/src/Avalonia.Controls/RepeatButton.cs
index a21725cadf..0415a78721 100644
--- a/src/Avalonia.Controls/RepeatButton.cs
+++ b/src/Avalonia.Controls/RepeatButton.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty DelayProperty =
AvaloniaProperty.Register(nameof(Delay), 300);
- private DispatcherTimer _repeatTimer;
+ private DispatcherTimer? _repeatTimer;
///
/// Gets or sets the amount of time, in milliseconds, of repeating clicks.
@@ -55,10 +55,10 @@ namespace Avalonia.Controls
_repeatTimer.Start();
}
- private void RepeatTimerOnTick(object sender, EventArgs e)
+ private void RepeatTimerOnTick(object? sender, EventArgs e)
{
var interval = TimeSpan.FromMilliseconds(Interval);
- if (_repeatTimer.Interval != interval)
+ if (_repeatTimer!.Interval != interval)
{
_repeatTimer.Interval = interval;
}
diff --git a/src/Avalonia.Controls/Repeater/ElementFactory.cs b/src/Avalonia.Controls/Repeater/ElementFactory.cs
index 644e077221..6b776803d7 100644
--- a/src/Avalonia.Controls/Repeater/ElementFactory.cs
+++ b/src/Avalonia.Controls/Repeater/ElementFactory.cs
@@ -4,7 +4,7 @@ namespace Avalonia.Controls
{
public abstract class ElementFactory : IElementFactory
{
- public IControl Build(object data)
+ public IControl Build(object? data)
{
return GetElementCore(new ElementFactoryGetArgs { Data = data });
}
@@ -14,7 +14,7 @@ namespace Avalonia.Controls
return GetElementCore(args);
}
- public bool Match(object data) => true;
+ public bool Match(object? data) => true;
public void RecycleElement(ElementFactoryRecycleArgs args)
{
diff --git a/src/Avalonia.Controls/Repeater/IElementFactory.cs b/src/Avalonia.Controls/Repeater/IElementFactory.cs
index f424ae29b7..daab7e855f 100644
--- a/src/Avalonia.Controls/Repeater/IElementFactory.cs
+++ b/src/Avalonia.Controls/Repeater/IElementFactory.cs
@@ -12,13 +12,13 @@ namespace Avalonia.Controls
/// Gets or sets the data item for which an appropriate element tree should be realized
/// when calling .
///
- public object Data { get; set; }
+ public object? Data { get; set; }
///
/// Gets or sets the that is expected to be the parent of the
/// realized element from .
///
- public IControl Parent { get; set; }
+ public IControl? Parent { get; set; }
///
/// Gets or sets the index of the item that should be realized.
@@ -36,13 +36,13 @@ namespace Avalonia.Controls
/// Gets or sets the to recycle when calling
/// .
///
- public IControl Element { get; set; }
+ public IControl? Element { get; set; }
///
/// Gets or sets the that is expected to be the parent of the
/// realized element from .
///
- public IControl Parent { get; set; }
+ public IControl? Parent { get; set; }
}
///
diff --git a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
index dd97cde218..eb3b2ea787 100644
--- a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
+++ b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
@@ -13,8 +13,8 @@ namespace Avalonia.Controls
public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate;
- public IControl Build(object param) => GetElement(null, param);
- public bool Match(object data) => _dataTemplate.Match(data);
+ public IControl Build(object? param) => GetElement(null, param);
+ public bool Match(object? data) => _dataTemplate.Match(data);
public IControl GetElement(ElementFactoryGetArgs args)
{
@@ -23,14 +23,14 @@ namespace Avalonia.Controls
public void RecycleElement(ElementFactoryRecycleArgs args)
{
- RecycleElement(args.Parent, args.Element);
+ RecycleElement(args.Parent, args.Element!);
}
- private IControl GetElement(IControl parent, object data)
+ private IControl GetElement(IControl? parent, object? data)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
- IControl element = null;
+ IControl? element = null;
if (recyclePool != null)
{
@@ -41,7 +41,7 @@ namespace Avalonia.Controls
if (element == null)
{
// no element was found in recycle pool, create a new element
- element = selectedTemplate.Build(data);
+ element = selectedTemplate.Build(data)!;
// Associate template with element
element.SetValue(RecyclePool.OriginTemplateProperty, selectedTemplate);
@@ -50,7 +50,7 @@ namespace Avalonia.Controls
return element;
}
- private void RecycleElement(IControl parent, IControl element)
+ private void RecycleElement(IControl? parent, IControl element)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
index b40cf26df5..09c0e58332 100644
--- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
+++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
@@ -31,13 +31,13 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
- public static readonly StyledProperty ItemTemplateProperty =
+ public static readonly StyledProperty ItemTemplateProperty =
ItemsControl.ItemTemplateProperty.AddOwner();
///
/// Defines the property.
///
- public static readonly DirectProperty ItemsProperty =
+ public static readonly DirectProperty ItemsProperty =
ItemsControl.ItemsProperty.AddOwner(o => o.Items, (o, v) => o.Items = v);
///
@@ -60,14 +60,14 @@ namespace Avalonia.Controls
private readonly ViewManager _viewManager;
private readonly ViewportManager _viewportManager;
- private IEnumerable _items;
- private VirtualizingLayoutContext _layoutContext;
- private EventHandler _childIndexChanged;
+ private IEnumerable? _items;
+ private VirtualizingLayoutContext? _layoutContext;
+ private EventHandler? _childIndexChanged;
private bool _isLayoutInProgress;
- private NotifyCollectionChangedEventArgs _processingItemsSourceChange;
- private ItemsRepeaterElementPreparedEventArgs _elementPreparedArgs;
- private ItemsRepeaterElementClearingEventArgs _elementClearingArgs;
- private ItemsRepeaterElementIndexChangedEventArgs _elementIndexChangedArgs;
+ private NotifyCollectionChangedEventArgs? _processingItemsSourceChange;
+ private ItemsRepeaterElementPreparedEventArgs? _elementPreparedArgs;
+ private ItemsRepeaterElementClearingEventArgs? _elementClearingArgs;
+ private ItemsRepeaterElementIndexChangedEventArgs? _elementIndexChangedArgs;
///
/// Initializes a new instance of the class.
@@ -102,7 +102,7 @@ namespace Avalonia.Controls
///
/// Gets or sets an object source used to generate the content of the ItemsRepeater.
///
- public IEnumerable Items
+ public IEnumerable? Items
{
get => _items;
set => SetAndRaise(ItemsProperty, ref _items, value);
@@ -111,7 +111,7 @@ namespace Avalonia.Controls
///
/// Gets or sets the template used to display each item.
///
- public IDataTemplate ItemTemplate
+ public IDataTemplate? ItemTemplate
{
get => GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
@@ -141,14 +141,14 @@ namespace Avalonia.Controls
/// Gets a standardized view of the supported interactions between a given Items object and
/// the ItemsRepeater control and its associated components.
///
- public ItemsSourceView ItemsSourceView { get; private set; }
+ public ItemsSourceView? ItemsSourceView { get; private set; }
- internal IElementFactory ItemTemplateShim { get; set; }
+ internal IElementFactory? ItemTemplateShim { get; set; }
internal Point LayoutOrigin { get; set; }
- internal object LayoutState { get; set; }
- internal IControl MadeAnchor => _viewportManager.MadeAnchor;
+ internal object? LayoutState { get; set; }
+ internal IControl? MadeAnchor => _viewportManager.MadeAnchor;
internal Rect RealizationWindow => _viewportManager.GetLayoutRealizationWindow();
- internal IControl SuggestedAnchor => _viewportManager.SuggestedAnchor;
+ internal IControl? SuggestedAnchor => _viewportManager.SuggestedAnchor;
private bool IsProcessingCollectionChange => _processingItemsSourceChange != null;
@@ -165,7 +165,7 @@ namespace Avalonia.Controls
}
}
- event EventHandler IChildIndexProvider.ChildIndexChanged
+ event EventHandler? IChildIndexProvider.ChildIndexChanged
{
add => _childIndexChanged += value;
remove => _childIndexChanged -= value;
@@ -180,7 +180,7 @@ namespace Avalonia.Controls
bool IChildIndexProvider.TryGetTotalCount(out int count)
{
- count = ItemsSourceView.Count;
+ count = ItemsSourceView?.Count ?? 0;
return true;
}
@@ -192,7 +192,7 @@ namespace Avalonia.Controls
/// outside the range of realized items. Elements are cleared when they become available
/// for re-use.
///
- public event EventHandler ElementClearing;
+ public event EventHandler? ElementClearing;
///
/// Occurs for each realized when the index for the item it
@@ -207,7 +207,7 @@ namespace Avalonia.Controls
/// represents has changed. For example, when another item is added or removed in the data
/// source, the index for items that come after in the ordering will be impacted.
///
- public event EventHandler ElementIndexChanged;
+ public event EventHandler? ElementIndexChanged;
///
/// Occurs each time an element is prepared for use.
@@ -216,7 +216,7 @@ namespace Avalonia.Controls
/// The prepared element might be newly created or an existing element that is being re-
/// used.
///
- public event EventHandler ElementPrepared;
+ public event EventHandler? ElementPrepared;
///
/// Retrieves the index of the item from the data source that corresponds to the specified
@@ -240,7 +240,7 @@ namespace Avalonia.Controls
/// he UIElement that corresponds to the item at the specified index if the item is
/// realized, or null if the item is not realized.
///
- public IControl TryGetElement(int index) => GetElementFromIndexImpl(index);
+ public IControl? TryGetElement(int index) => GetElementFromIndexImpl(index);
///
/// Retrieves the UIElement that corresponds to the item at the specified index in the
@@ -497,9 +497,9 @@ namespace Avalonia.Controls
return -1;
}
- private IControl GetElementFromIndexImpl(int index)
+ private IControl? GetElementFromIndexImpl(int index)
{
- IControl result = null;
+ IControl? result = null;
var children = Children;
for (var i = 0; i < children.Count && result == null; ++i)
@@ -517,7 +517,7 @@ namespace Avalonia.Controls
private IControl GetOrCreateElementImpl(int index)
{
- if (index >= 0 && index >= ItemsSourceView.Count)
+ if (index >= 0 && index >= (ItemsSourceView?.Count ?? 0))
{
throw new ArgumentException("Argument index is invalid.", "index");
}
@@ -544,7 +544,7 @@ namespace Avalonia.Controls
_viewportManager.OnMakeAnchor(element, isAnchorOutsideRealizedRange);
InvalidateMeasure();
- return element;
+ return element!;
}
internal void OnElementPrepared(IControl element, VirtualizationInfo virtInfo)
@@ -608,7 +608,7 @@ namespace Avalonia.Controls
_childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(element));
}
- private void OnDataSourcePropertyChanged(ItemsSourceView oldValue, ItemsSourceView newValue)
+ private void OnDataSourcePropertyChanged(ItemsSourceView? oldValue, ItemsSourceView? newValue)
{
if (_isLayoutInProgress)
{
@@ -664,7 +664,7 @@ namespace Avalonia.Controls
}
}
- private void OnItemTemplateChanged(IDataTemplate oldValue, IDataTemplate newValue)
+ private void OnItemTemplateChanged(IDataTemplate? oldValue, IDataTemplate? newValue)
{
if (_isLayoutInProgress && oldValue != null)
{
@@ -705,12 +705,17 @@ namespace Avalonia.Controls
}
}
- ItemTemplateShim = newValue as IElementFactory ?? new ItemTemplateWrapper(newValue);
+ ItemTemplateShim = newValue switch
+ {
+ IElementFactory factory => factory,
+ null => null,
+ _ => new ItemTemplateWrapper(newValue)
+ };
InvalidateMeasure();
}
- private void OnLayoutChanged(AttachedLayout oldValue, AttachedLayout newValue)
+ private void OnLayoutChanged(AttachedLayout? oldValue, AttachedLayout? newValue)
{
if (_isLayoutInProgress)
{
@@ -751,7 +756,7 @@ namespace Avalonia.Controls
InvalidateMeasure();
}
- private void OnItemsSourceViewChanged(object sender, NotifyCollectionChangedEventArgs args)
+ private void OnItemsSourceViewChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
if (_isLayoutInProgress)
{
diff --git a/src/Avalonia.Controls/Repeater/RecyclePool.cs b/src/Avalonia.Controls/Repeater/RecyclePool.cs
index 28f299043c..9e2da81f99 100644
--- a/src/Avalonia.Controls/Repeater/RecyclePool.cs
+++ b/src/Avalonia.Controls/Repeater/RecyclePool.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Controls
private static ConditionalWeakTable s_pools = new ConditionalWeakTable();
private readonly Dictionary> _elements = new Dictionary>();
- public static RecyclePool GetPoolInstance(IDataTemplate dataTemplate)
+ public static RecyclePool? GetPoolInstance(IDataTemplate dataTemplate)
{
s_pools.TryGetValue(dataTemplate, out var result);
return result;
@@ -30,7 +30,7 @@ namespace Avalonia.Controls
public static void SetPoolInstance(IDataTemplate dataTemplate, RecyclePool value) => s_pools.Add(dataTemplate, value);
- public void PutElement(IControl element, string key, IControl owner)
+ public void PutElement(IControl element, string key, IControl? owner)
{
var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner);
var elementInfo = new ElementInfo(element, ownerAsPanel);
@@ -44,7 +44,7 @@ namespace Avalonia.Controls
pool.Add(elementInfo);
}
- public IControl TryGetElement(string key, IControl owner)
+ public IControl? TryGetElement(string key, IControl? owner)
{
if (_elements.TryGetValue(key, out var elements))
{
@@ -54,10 +54,10 @@ namespace Avalonia.Controls
// the enter/leave cost during recycling.
// TODO: prioritize elements with the same owner to those without an owner.
var elementInfo = elements.FirstOrDefault(x => x.Owner == owner) ?? elements.LastOrDefault();
- elements.Remove(elementInfo);
+ elements.Remove(elementInfo!);
var ownerAsPanel = EnsureOwnerIsPanelOrNull(owner);
- if (elementInfo.Owner != null && elementInfo.Owner != ownerAsPanel)
+ if (elementInfo!.Owner != null && elementInfo.Owner != ownerAsPanel)
{
// Element is still under its parent. remove it from its parent.
var panel = elementInfo.Owner;
@@ -83,7 +83,7 @@ namespace Avalonia.Controls
internal string GetReuseKey(IControl element) => element.GetValue(ReuseKeyProperty);
internal void SetReuseKey(IControl element, string value) => element.SetValue(ReuseKeyProperty, value);
- private IPanel EnsureOwnerIsPanelOrNull(IControl owner)
+ private IPanel? EnsureOwnerIsPanelOrNull(IControl? owner)
{
if (owner is IPanel panel)
{
@@ -99,14 +99,14 @@ namespace Avalonia.Controls
private class ElementInfo
{
- public ElementInfo(IControl element, IPanel owner)
+ public ElementInfo(IControl element, IPanel? owner)
{
Element = element;
Owner = owner;
}
public IControl Element { get; }
- public IPanel Owner { get;}
+ public IPanel? Owner { get;}
}
}
}
diff --git a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
index 9503239e34..c1baa66433 100644
--- a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
+++ b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Templates;
-#nullable enable
-
namespace Avalonia.Controls
{
public class SelectTemplateEventArgs : EventArgs
@@ -71,7 +69,7 @@ namespace Avalonia.Controls
}
var dataTemplate = Templates[templateKey];
- element = dataTemplate.Build(args.Data);
+ element = dataTemplate.Build(args.Data)!;
// Associate ReuseKey with element
RecyclePool.SetReuseKey(element, templateKey);
@@ -82,12 +80,12 @@ namespace Avalonia.Controls
protected override void RecycleElementCore(ElementFactoryRecycleArgs args)
{
- var element = args.Element;
+ var element = args.Element!;
var key = RecyclePool.GetReuseKey(element);
RecyclePool.PutElement(element, key, args.Parent);
}
- protected virtual string OnSelectTemplateKeyCore(object dataContext, IControl owner)
+ protected virtual string OnSelectTemplateKeyCore(object? dataContext, IControl? owner)
{
if (SelectTemplateKey is object)
{
diff --git a/src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs b/src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs
index 733b045e1b..3370b36801 100644
--- a/src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs
+++ b/src/Avalonia.Controls/Repeater/RepeaterLayoutContext.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Controls
set => _owner.LayoutOrigin = value;
}
- protected override object LayoutStateCore
+ protected override object? LayoutStateCore
{
get => _owner.LayoutState;
set => _owner.LayoutState = value;
@@ -57,7 +57,7 @@ namespace Avalonia.Controls
options.HasAllFlags(ElementRealizationOptions.SuppressAutoRecycle));
}
- protected override object GetItemAtCore(int index) => _owner.ItemsSourceView.GetAt(index);
+ protected override object GetItemAtCore(int index) => _owner.ItemsSourceView!.GetAt(index)!;
protected override void RecycleElementCore(ILayoutable element)
{
diff --git a/src/Avalonia.Controls/Repeater/UniqueIdElementPool.cs b/src/Avalonia.Controls/Repeater/UniqueIdElementPool.cs
index 775aa3f113..778b8bebcc 100644
--- a/src/Avalonia.Controls/Repeater/UniqueIdElementPool.cs
+++ b/src/Avalonia.Controls/Repeater/UniqueIdElementPool.cs
@@ -20,7 +20,7 @@ namespace Avalonia.Controls
public void Add(IControl element)
{
var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
- var key = virtInfo.UniqueId;
+ var key = virtInfo.UniqueId!;
if (_elementMap.ContainsKey(key))
{
@@ -30,10 +30,10 @@ namespace Avalonia.Controls
_elementMap.Add(key, element);
}
- public IControl Remove(int index)
+ public IControl? Remove(int index)
{
// Check if there is already a element in the mapping and if so, use it.
- string key = _owner.ItemsSourceView.KeyFromIndex(index);
+ string key = _owner.ItemsSourceView!.KeyFromIndex(index);
if (_elementMap.TryGetValue(key, out var element))
{
diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs
index a7b6cf7f18..b28753b518 100644
--- a/src/Avalonia.Controls/Repeater/ViewManager.cs
+++ b/src/Avalonia.Controls/Repeater/ViewManager.cs
@@ -22,10 +22,10 @@ namespace Avalonia.Controls
private readonly ItemsRepeater _owner;
private readonly List _pinnedPool = new List();
private readonly UniqueIdElementPool _resetPool;
- private IControl _lastFocusedElement;
+ private IControl? _lastFocusedElement;
private bool _isDataSourceStableResetPending;
- private ElementFactoryGetArgs _elementFactoryGetArgs;
- private ElementFactoryRecycleArgs _elementFactoryRecycleArgs;
+ private ElementFactoryGetArgs? _elementFactoryGetArgs;
+ private ElementFactoryRecycleArgs? _elementFactoryRecycleArgs;
private int _firstRealizedElementIndexHeldByLayout = FirstRealizedElementIndexDefault;
private int _lastRealizedElementIndexHeldByLayout = LastRealizedElementIndexDefault;
private bool _eventsSubscribed;
@@ -170,7 +170,7 @@ namespace Avalonia.Controls
_lastFocusedElement = focusedChild;
// Add pin to hold the focused element.
- UpdatePin(focusedChild, true /* addPin */);
+ UpdatePin(focusedChild!, true /* addPin */);
}
else
{
@@ -179,14 +179,14 @@ namespace Avalonia.Controls
}
}
- IControl FindFocusCandidate(int clearedIndex, out IControl focusedChild)
+ IControl? FindFocusCandidate(int clearedIndex, out IControl? focusedChild)
{
// Walk through all the children and find elements with index before and after the cleared index.
// Note that during a delete the next element would now have the same index.
int previousIndex = int.MinValue;
int nextIndex = int.MaxValue;
- IControl nextElement = null;
- IControl previousElement = null;
+ IControl? nextElement = null;
+ IControl? previousElement = null;
foreach (var child in _owner.Children)
{
@@ -287,7 +287,7 @@ namespace Avalonia.Controls
}
}
- public void OnItemsSourceChanged(object sender, NotifyCollectionChangedEventArgs args)
+ public void OnItemsSourceChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
// Note: For items that have been removed, the index will not be touched. It will hold
// the old index before it was removed. It is not valid anymore.
@@ -296,7 +296,7 @@ namespace Avalonia.Controls
case NotifyCollectionChangedAction.Add:
{
var newIndex = args.NewStartingIndex;
- var newCount = args.NewItems.Count;
+ var newCount = args.NewItems!.Count;
EnsureFirstLastRealizedIndices();
if (newIndex <= _lastRealizedElementIndexHeldByLayout)
{
@@ -343,8 +343,8 @@ namespace Avalonia.Controls
// depending on the counts.
var oldStartIndex = args.OldStartingIndex;
var newStartingIndex = args.NewStartingIndex;
- var oldCount = args.OldItems.Count;
- var newCount = args.NewItems.Count;
+ var oldCount = args.OldItems!.Count;
+ var newCount = args.NewItems!.Count;
if (oldStartIndex != newStartingIndex)
{
throw new NotSupportedException("Replace is only allowed with OldStartingIndex equals to NewStartingIndex.");
@@ -388,7 +388,7 @@ namespace Avalonia.Controls
case NotifyCollectionChangedAction.Remove:
{
var oldStartIndex = args.OldStartingIndex;
- var oldCount = args.OldItems.Count;
+ var oldCount = args.OldItems!.Count;
foreach (var element in _owner.Children)
{
var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
@@ -417,7 +417,7 @@ namespace Avalonia.Controls
// running layout, we dont have to clear all the elements again.
if (!_isDataSourceStableResetPending)
{
- if (_owner.ItemsSourceView.HasKeyIndexMapping)
+ if (_owner.ItemsSourceView!.HasKeyIndexMapping)
{
_isDataSourceStableResetPending = true;
}
@@ -482,9 +482,9 @@ namespace Avalonia.Controls
// If an index that is realized is requested by the layout, we unfortunately have to walk the
// children. Not ideal, but a reasonable default to provide consistent behavior between virtualizing
// and non-virtualizing hosts.
- private IControl GetElementIfAlreadyHeldByLayout(int index)
+ private IControl? GetElementIfAlreadyHeldByLayout(int index)
{
- IControl element = null;
+ IControl? element = null;
bool cachedFirstLastIndicesInvalid = _firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault;
bool isRequestedIndexInRealizedRange = (_firstRealizedElementIndexHeldByLayout <= index && index <= _lastRealizedElementIndexHeldByLayout);
@@ -518,9 +518,9 @@ namespace Avalonia.Controls
return element;
}
- private IControl GetElementFromUniqueIdResetPool(int index)
+ private IControl? GetElementFromUniqueIdResetPool(int index)
{
- IControl element = null;
+ IControl? element = null;
// See if you can get it from the reset pool.
if (_isDataSourceStableResetPending)
{
@@ -541,9 +541,9 @@ namespace Avalonia.Controls
return element;
}
- private IControl GetElementFromPinnedElements(int index)
+ private IControl? GetElementFromPinnedElements(int index)
{
- IControl element = null;
+ IControl? element = null;
// See if you can find something among the pinned elements.
for (var i = 0; i < _pinnedPool.Count; ++i)
@@ -583,7 +583,7 @@ namespace Avalonia.Controls
private IControl GetElementFromElementFactory(int index)
{
// The view generator is the provider of last resort.
- var data = _owner.ItemsSourceView.GetAt(index);
+ var data = _owner.ItemsSourceView!.GetAt(index);
var providedElementFactory = _owner.ItemTemplateShim;
IElementFactory GetElementFactory()
@@ -592,7 +592,7 @@ namespace Avalonia.Controls
{
var factory = FuncDataTemplate.Default;
_owner.ItemTemplate = factory;
- return _owner.ItemTemplateShim;
+ return _owner.ItemTemplateShim!;
}
return providedElementFactory;
@@ -697,9 +697,9 @@ namespace Avalonia.Controls
private void UpdateFocusedElement()
{
- IControl focusedElement = null;
+ IControl? focusedElement = null;
- var child = FocusManager.Instance.Current;
+ var child = FocusManager.Instance?.Current;
if (child != null)
{
@@ -713,7 +713,9 @@ namespace Avalonia.Controls
if (parent is ItemsRepeater repeater)
{
var element = child as IControl;
- if (repeater == owner && ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
+ if (repeater == owner &&
+ element is not null &&
+ ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
{
focusedElement = element;
}
@@ -722,7 +724,7 @@ namespace Avalonia.Controls
}
child = parent as IInputElement;
- parent = child.VisualParent;
+ parent = child?.VisualParent;
}
}
@@ -744,7 +746,7 @@ namespace Avalonia.Controls
}
}
- private void OnFocusChanged(object sender, RoutedEventArgs e) => UpdateFocusedElement();
+ private void OnFocusChanged(object? sender, RoutedEventArgs e) => UpdateFocusedElement();
private void EnsureEventSubscriptions()
{
diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs
index 1a90da5830..ec25fcb265 100644
--- a/src/Avalonia.Controls/Repeater/ViewportManager.cs
+++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs
@@ -24,8 +24,8 @@ namespace Avalonia.Controls
private const double CacheBufferPerSideInflationPixelDelta = 40.0;
private readonly ItemsRepeater _owner;
private bool _ensuredScroller;
- private IScrollAnchorProvider _scroller;
- private IControl _makeAnchorElement;
+ private IScrollAnchorProvider? _scroller;
+ private IControl? _makeAnchorElement;
private bool _isAnchorOutsideRealizedRange;
private Rect _visibleWindow;
private Rect _layoutExtent;
@@ -57,7 +57,7 @@ namespace Avalonia.Controls
_owner = owner;
}
- public IControl SuggestedAnchor
+ public IControl? SuggestedAnchor
{
get
{
@@ -97,7 +97,7 @@ namespace Avalonia.Controls
public bool HasScroller => _scroller != null;
- public IControl MadeAnchor => _makeAnchorElement;
+ public IControl? MadeAnchor => _makeAnchorElement;
public double HorizontalCacheLength
{
@@ -213,7 +213,7 @@ namespace Avalonia.Controls
// We just finished a measure pass and have a new extent.
// Let's make sure the scrollers will run its arrange so that they track the anchor.
- ((IControl)_scroller)?.InvalidateArrange();
+ ((IControl?)_scroller)?.InvalidateArrange();
}
public Point GetOrigin() => _layoutExtent.TopLeft;
@@ -294,7 +294,7 @@ namespace Avalonia.Controls
}
}
- private void OnLayoutUpdated(object sender, EventArgs args)
+ private void OnLayoutUpdated(object? sender, EventArgs args)
{
_owner.LayoutUpdated -= OnLayoutUpdated;
_layoutUpdatedSubscribed = false;
@@ -324,7 +324,7 @@ namespace Avalonia.Controls
}
}
- public void OnMakeAnchor(IControl anchor, bool isAnchorOutsideRealizedRange)
+ public void OnMakeAnchor(IControl? anchor, bool isAnchorOutsideRealizedRange)
{
if (_makeAnchorElement != anchor)
{
@@ -346,7 +346,7 @@ namespace Avalonia.Controls
// get the targetChild - i.e the immediate child of this repeater that is being brought into view.
// Note that the element being brought into view could be a descendant.
- var targetChild = GetImmediateChildOfRepeater((IControl)args.TargetObject);
+ var targetChild = GetImmediateChildOfRepeater((IControl)args.TargetObject!);
if (targetChild is null)
{
@@ -382,14 +382,14 @@ namespace Avalonia.Controls
_scroller?.RegisterAnchorCandidate(element);
}
- private IControl GetImmediateChildOfRepeater(IControl descendant)
+ private IControl? GetImmediateChildOfRepeater(IControl descendant)
{
var targetChild = descendant;
- var parent = (IControl)descendant.VisualParent;
+ var parent = (IControl?)descendant.VisualParent;
while (parent != null && parent != _owner)
{
targetChild = parent;
- parent = (IControl)parent.VisualParent;
+ parent = (IControl?)parent.VisualParent;
}
if (parent == null)
@@ -441,7 +441,7 @@ namespace Avalonia.Controls
_ensuredScroller = false;
}
- private void OnEffectiveViewportChanged(object sender, EffectiveViewportChangedEventArgs e)
+ private void OnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId);
UpdateViewport(e.EffectiveViewport);
diff --git a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs
index 7e6b24f1b5..7378dbcfa1 100644
--- a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs
+++ b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Controls
public bool KeepAlive { get; set; }
public bool IsRegisteredAsAnchorCandidate { get; set; }
public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory;
- public string UniqueId { get; private set; }
+ public string? UniqueId { get; private set; }
public void MoveOwnershipToLayoutFromElementFactory(int index, string uniqueId)
{
diff --git a/src/Avalonia.Controls/RequestBringIntoViewEventArgs.cs b/src/Avalonia.Controls/RequestBringIntoViewEventArgs.cs
index dd55de85d4..5396ad67a7 100644
--- a/src/Avalonia.Controls/RequestBringIntoViewEventArgs.cs
+++ b/src/Avalonia.Controls/RequestBringIntoViewEventArgs.cs
@@ -5,7 +5,7 @@ namespace Avalonia.Controls
{
public class RequestBringIntoViewEventArgs : RoutedEventArgs
{
- public IVisual TargetObject { get; set; }
+ public IVisual? TargetObject { get; set; }
public Rect TargetRect { get; set; }
}
diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs
index 8a0a0fa728..a554f82f61 100644
--- a/src/Avalonia.Controls/Screens.cs
+++ b/src/Avalonia.Controls/Screens.cs
@@ -2,54 +2,45 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Platform;
-using Avalonia.Utilities;
using Avalonia.VisualTree;
+#nullable enable
+
namespace Avalonia.Controls
{
public class Screens
{
private readonly IScreenImpl _iScreenImpl;
- public int ScreenCount => _iScreenImpl.ScreenCount;
+ public int ScreenCount => _iScreenImpl?.ScreenCount ?? 0;
public IReadOnlyList All => _iScreenImpl?.AllScreens ?? Array.Empty();
- public Screen Primary => All.FirstOrDefault(x => x.Primary);
+ public Screen? Primary => All.FirstOrDefault(x => x.Primary);
public Screens(IScreenImpl iScreenImpl)
{
_iScreenImpl = iScreenImpl;
}
- public Screen ScreenFromBounds(PixelRect bounds){
-
- Screen currMaxScreen = null;
- double maxAreaSize = 0;
- foreach (Screen screen in All)
- {
- double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
- double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
- double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
- double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
- double area = (right - left) * (bottom - top);
- if (area > maxAreaSize)
- {
- maxAreaSize = area;
- currMaxScreen = screen;
- }
- }
-
- return currMaxScreen;
+ public Screen? ScreenFromBounds(PixelRect bounds)
+ {
+ return _iScreenImpl.ScreenFromRect(bounds);
}
- public Screen ScreenFromPoint(PixelPoint point)
+ public Screen? ScreenFromWindow(IWindowBaseImpl window)
{
- return All.FirstOrDefault(x => x.Bounds.Contains(point));
+ return _iScreenImpl.ScreenFromWindow(window);
}
- public Screen ScreenFromVisual(IVisual visual)
+ public Screen? ScreenFromPoint(PixelPoint point)
+ {
+ return _iScreenImpl.ScreenFromPoint(point);
+ }
+
+ public Screen? ScreenFromVisual(IVisual visual)
{
var tl = visual.PointToScreen(visual.Bounds.TopLeft);
var br = visual.PointToScreen(visual.Bounds.BottomRight);
+
return ScreenFromBounds(new PixelRect(tl, br));
}
}
diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs
index eee6216587..73bb827ad9 100644
--- a/src/Avalonia.Controls/ScrollViewer.cs
+++ b/src/Avalonia.Controls/ScrollViewer.cs
@@ -1,5 +1,6 @@
using System;
using System.Reactive.Linq;
+using Avalonia.Automation.Peers;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@@ -8,7 +9,7 @@ using Avalonia.Interactivity;
namespace Avalonia.Controls
{
///
- /// A control scrolls its content if the content is bigger than the space available.
+ /// A control which scrolls its content if the content is bigger than the space available.
///
public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider
{
@@ -181,6 +182,14 @@ namespace Avalonia.Controls
nameof(AllowAutoHide),
true);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AttachedProperty IsScrollChainingEnabledProperty =
+ AvaloniaProperty.RegisterAttached(
+ nameof(IsScrollChainingEnabled),
+ defaultValue: true);
+
///
/// Defines the event.
///
@@ -191,8 +200,8 @@ namespace Avalonia.Controls
internal const double DefaultSmallChange = 16;
- private IDisposable _childSubscription;
- private ILogicalScrollable _logicalScrollable;
+ private IDisposable? _childSubscription;
+ private ILogicalScrollable? _logicalScrollable;
private Size _extent;
private Vector _offset;
private Size _viewport;
@@ -202,7 +211,7 @@ namespace Avalonia.Controls
private Size _largeChange;
private Size _smallChange = new Size(DefaultSmallChange, DefaultSmallChange);
private bool _isExpanded;
- private IDisposable _scrollBarExpandSubscription;
+ private IDisposable? _scrollBarExpandSubscription;
///
/// Initializes static members of the class.
@@ -224,7 +233,7 @@ namespace Avalonia.Controls
///
/// Occurs when changes are detected to the scroll position, extent, or viewport size.
///
- public event EventHandler ScrollChanged
+ public event EventHandler? ScrollChanged
{
add => AddHandler(ScrollChangedEvent, value);
remove => RemoveHandler(ScrollChangedEvent, value);
@@ -332,7 +341,7 @@ namespace Avalonia.Controls
}
///
- public IControl CurrentAnchor => (Presenter as IScrollAnchorProvider)?.CurrentAnchor;
+ public IControl? CurrentAnchor => (Presenter as IScrollAnchorProvider)?.CurrentAnchor;
///
/// Gets the maximum horizontal scrollbar value.
@@ -418,6 +427,20 @@ namespace Avalonia.Controls
set => SetValue(AllowAutoHideProperty, value);
}
+ ///
+ /// Gets or sets if scroll chaining is enabled. The default value is true.
+ ///
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public bool IsScrollChainingEnabled
+ {
+ get => GetValue(IsScrollChainingEnabledProperty);
+ set => SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
///
/// Scrolls the content up one line.
///
@@ -548,6 +571,36 @@ namespace Avalonia.Controls
return control.GetValue(AllowAutoHideProperty);
}
+ ///
+ /// Sets the value of the IsScrollChainingEnabled attached property.
+ ///
+ /// The control to set the value on.
+ /// The value of the property.
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public static void SetIsScrollChainingEnabled(Control control, bool value)
+ {
+ control.SetValue(IsScrollChainingEnabledProperty, value);
+ }
+
+ ///
+ /// Gets the value of the IsScrollChainingEnabled attached property.
+ ///
+ /// The control to read the value from.
+ /// The value of the property.
+ ///
+ /// After a user hits a scroll limit on an element that has been nested within another scrollable element,
+ /// you can specify whether that parent element should continue the scrolling operation begun in its child element.
+ /// This is called scroll chaining.
+ ///
+ public static bool GetIsScrollChainingEnabled(Control control)
+ {
+ return control.GetValue(IsScrollChainingEnabledProperty);
+ }
+
///
/// Gets the value of the VerticalScrollBarVisibility attached property.
///
@@ -604,7 +657,7 @@ namespace Avalonia.Controls
return double.IsNaN(result) ? 0 : result;
}
- private void ChildChanged(IControl child)
+ private void ChildChanged(IControl? child)
{
if (_logicalScrollable is object)
{
@@ -621,7 +674,7 @@ namespace Avalonia.Controls
CalculatedPropertiesChanged();
}
- private void LogicalScrollInvalidated(object sender, EventArgs e)
+ private void LogicalScrollInvalidated(object? sender, EventArgs e)
{
CalculatedPropertiesChanged();
}
@@ -710,9 +763,14 @@ namespace Avalonia.Controls
_scrollBarExpandSubscription = SubscribeToScrollBars(e);
}
- private IDisposable SubscribeToScrollBars(TemplateAppliedEventArgs e)
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new ScrollViewerAutomationPeer(this);
+ }
+
+ private IDisposable? SubscribeToScrollBars(TemplateAppliedEventArgs e)
{
- static IObservable GetExpandedObservable(ScrollBar scrollBar)
+ static IObservable? GetExpandedObservable(ScrollBar? scrollBar)
{
return scrollBar?.GetObservable(ScrollBar.IsExpandedProperty);
}
@@ -723,7 +781,7 @@ namespace Avalonia.Controls
var horizontalExpanded = GetExpandedObservable(horizontalScrollBar);
var verticalExpanded = GetExpandedObservable(verticalScrollBar);
- IObservable actualExpanded = null;
+ IObservable? actualExpanded = null;
if (horizontalExpanded != null && verticalExpanded != null)
{
@@ -749,7 +807,7 @@ namespace Avalonia.Controls
IsExpanded = isExpanded;
}
- private void OnLayoutUpdated(object sender, EventArgs e) => RaiseScrollChanged();
+ private void OnLayoutUpdated(object? sender, EventArgs e) => RaiseScrollChanged();
private void RaiseScrollChanged()
{
diff --git a/src/Avalonia.Controls/Selection/ISelectionModel.cs b/src/Avalonia.Controls/Selection/ISelectionModel.cs
index 3b8fd0c8b7..3f4ae48263 100644
--- a/src/Avalonia.Controls/Selection/ISelectionModel.cs
+++ b/src/Avalonia.Controls/Selection/ISelectionModel.cs
@@ -3,8 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
-#nullable enable
-
namespace Avalonia.Controls.Selection
{
public interface ISelectionModel : INotifyPropertyChanged
diff --git a/src/Avalonia.Controls/Selection/IndexRange.cs b/src/Avalonia.Controls/Selection/IndexRange.cs
index fa7b44faea..dd3445fb70 100644
--- a/src/Avalonia.Controls/Selection/IndexRange.cs
+++ b/src/Avalonia.Controls/Selection/IndexRange.cs
@@ -6,8 +6,6 @@
using System;
using System.Collections.Generic;
-#nullable enable
-
namespace Avalonia.Controls.Selection
{
internal readonly struct IndexRange : IEquatable