diff --git a/Avalonia.sln b/Avalonia.sln index 0354e20d4f..c3e86734d8 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -77,7 +77,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9 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 @@ -95,7 +95,7 @@ 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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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 @@ -107,7 +107,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 @@ -115,7 +115,6 @@ 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 @@ -1106,7 +1105,7 @@ Global {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|Any CPU.ActiveCfg = Debug|Any CPU {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 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..ede5aec0e8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,6 +1,3 @@ -variables: - MSBuildEnableWorkloadResolver: 'false' - jobs: - job: GetPRNumber @@ -146,6 +143,12 @@ jobs: inputs: version: 6.0.100 + - task: CmdLine@2 + displayName: 'Install Workloads' + inputs: + script: | + dotnet workload install android + - task: CmdLine@2 displayName: 'Install Nuke' inputs: 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..3fccad2641 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -17,5 +17,6 @@ + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 7f24ef35bc..dad40cccb7 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -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/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/dirs.proj b/dirs.proj index 594f2c22d3..74d7b482cc 100644 --- a/dirs.proj +++ b/dirs.proj @@ -1,5 +1,7 @@ + + @@ -8,11 +10,8 @@ - - - - + @@ -23,6 +22,10 @@ + + + + diff --git a/global.json b/global.json index b160e4561d..1f93ed27c3 100644 --- a/global.json +++ b/global.json @@ -4,6 +4,7 @@ }, "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/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 MsBuildCommon( @@ -99,7 +100,7 @@ partial class Build : NukeBuild // This is required for VS2019 image on Azure Pipelines .When(Parameters.IsRunningOnWindows && Parameters.IsRunningOnAzure, _ => _ - .AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_8_X64"))) + .AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_11_X64"))) .AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", true) .SetProcessToolPath(MsBuildExe.Value) diff --git a/readme.md b/readme.md index b38552a010..96c7937559 100644 --- a/readme.md +++ b/readme.md @@ -3,13 +3,11 @@
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) -## 📖 About AvaloniaUI +## 📖 About -Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. +Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM. - - -([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) +![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png) To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia! @@ -28,18 +26,15 @@ Install-Package Avalonia.Desktop ## Showcase Examples of UIs built with Avalonia -![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png) - -([Synfonia](https://github.com/jmacato/Synfonia)) + +([Lunacy](https://icons8.com/lunacy)) -![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG) -([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) +![image](https://user-images.githubusercontent.com/4672627/152325740-261c27a3-e6f0-4662-bff7-4796d4940e04.png) +([PlasticSCM](https://www.plasticscm.com/)) -![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG) -([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) +![image](https://user-images.githubusercontent.com/4672627/152326453-14944c4d-33da-4d50-a268-b87f80927adb.png) +([WasabiWallet](https://www.wasabiwallet.io/)) -![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png) -([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ## JetBrains Rider diff --git a/samples/ControlCatalog.Android/Assets/AboutAssets.txt b/samples/ControlCatalog.Android/Assets/AboutAssets.txt deleted file mode 100644 index a9b0638eb1..0000000000 --- a/samples/ControlCatalog.Android/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 your 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"); diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 617b6b6ab0..516acfe4b9 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -1,165 +1,46 @@ - - + - Debug - AnyCPU - 8.0.30703 - 2.0 - {29132311-1848-4FD6-AE0C-4FF841151BD3} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - ControlCatalog.Android - ControlCatalog.Android - 512 - true - Resources\Resource.Designer.cs - Off - False - v11.0 - Properties\AndroidManifest.xml + net6.0-android + 21 + Exe + enable + com.Avalonia.ControlCatalog + 1 + 1.0 + apk + true - - True - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - True - False - False - armeabi-v7a;x86;x86_64 - Xamarin - False - False - False - False - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - False - Full - True - False - False - armeabi-v7a,x86;x86_64 - Xamarin - False - False - False - False - False - - - - - - - - - - - - - - - - - - - - - - - - + Resources\drawable\Icon.png + + + True + False + True + + + + False + False + + + + True + + - + + + - - {7B92AF71-6287-4693-9DCB-BD5B6E927E23} - Avalonia.Android - - - {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 - - - {c42d2fc1-a531-4ed4-84b9-89aec7c962fc} - Avalonia.Themes.Fluent - - - {eb582467-6abb-43a1-b052-e981ba910e3a} - Avalonia.Visuals - - - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} - Avalonia.Styling - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - + + - - - - diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 2ab03551b6..b02083c39d 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -5,10 +5,10 @@ using Avalonia.Android; namespace ControlCatalog.Android { - [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)] + [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] public class MainActivity : AvaloniaActivity { - protected override void OnCreate(Bundle savedInstanceState) + protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); @@ -16,4 +16,3 @@ namespace ControlCatalog.Android } } } - diff --git a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml index 9effda7e79..aa570ec504 100644 --- a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml +++ b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml @@ -1,5 +1,4 @@  - - - - \ No newline at end of file + + + diff --git a/samples/ControlCatalog.Android/Properties/AssemblyInfo.cs b/samples/ControlCatalog.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index baeec94648..0000000000 --- a/samples/ControlCatalog.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// 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.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ControlCatalog.Android")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[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")] diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs deleted file mode 100644 index dccc3f7159..0000000000 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ /dev/null @@ -1,101 +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("ControlCatalog.Android.Resource", IsApplication=true)] - -namespace ControlCatalog.Android -{ - - - [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 Color - { - - // aapt resource value: 0x7F010000 - public const int splash_background = 2130771968; - - static Color() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Color() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7F020000 - public const int Icon = 2130837504; - - // aapt resource value: 0x7F020001 - public const int splash_screen = 2130837505; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Style - { - - // aapt resource value: 0x7F030000 - public const int MyTheme = 2130903040; - - // aapt resource value: 0x7F030001 - public const int MyTheme_NoActionBar = 2130903041; - - // aapt resource value: 0x7F030002 - public const int MyTheme_Splash = 2130903042; - - static Style() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Style() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml index e017b6facf..2759d2904a 100644 --- a/samples/ControlCatalog.Android/Resources/values/styles.xml +++ b/samples/ControlCatalog.Android/Resources/values/styles.xml @@ -4,7 +4,7 @@ - diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs index 6d7c6bc116..9729713833 100644 --- a/samples/ControlCatalog.Android/SplashActivity.cs +++ b/samples/ControlCatalog.Android/SplashActivity.cs @@ -10,7 +10,7 @@ namespace ControlCatalog.Android [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] public class SplashActivity : Activity { - protected override void OnCreate(Bundle savedInstanceState) + protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); } diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 2d4fc45171..5968a11b05 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -6,6 +6,12 @@ true + + true + https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json + 7.0.0-* + + @@ -15,6 +21,14 @@ + + + + + + + + en 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/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 3d869bb723..facce2aa82 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -22,8 +22,8 @@ - - + + @@ -40,6 +40,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/ButtonPage.xaml b/samples/ControlCatalog/Pages/ButtonPage.xaml deleted file mode 100644 index 2ce1469118..0000000000 --- a/samples/ControlCatalog/Pages/ButtonPage.xaml +++ /dev/null @@ -1,45 +0,0 @@ - - - A button control - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/ControlCatalog/Pages/ButtonsPage.xaml b/samples/ControlCatalog/Pages/ButtonsPage.xaml new file mode 100644 index 0000000000..7de0872eae --- /dev/null +++ b/samples/ControlCatalog/Pages/ButtonsPage.xaml @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A standard button control + + + + + + + + + + + + + + + + + + + + + + + + + + + + A button control with multiple states: checked, unchecked or indeterminate. + + + + + + + + + + + + + + A button control that raises its Click event repeatedly when it is pressed and held. + + + + + + + + + + + + + + + + A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a Button and the secondary part opens a flyout. + + + + + + + + + + + + + + + + + + + + + + + + A button with primary and secondary parts that can each be pressed separately. The primary part behaves like a ToggleButton with two states and the secondary part opens a flyout. + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/ButtonPage.xaml.cs b/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs similarity index 90% rename from samples/ControlCatalog/Pages/ButtonPage.xaml.cs rename to samples/ControlCatalog/Pages/ButtonsPage.xaml.cs index 5e555c8c91..5594bab176 100644 --- a/samples/ControlCatalog/Pages/ButtonPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs @@ -3,11 +3,11 @@ using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public class ButtonPage : UserControl + public class ButtonsPage : UserControl { private int repeatButtonClickCount = 0; - public ButtonPage() + public ButtonsPage() { InitializeComponent(); diff --git a/samples/ControlCatalog/Pages/ClipboardPage.xaml b/samples/ControlCatalog/Pages/ClipboardPage.xaml new file mode 100644 index 0000000000..4199a9780f --- /dev/null +++ b/samples/ControlCatalog/Pages/ClipboardPage.xaml @@ -0,0 +1,23 @@ + + + Example of ClipboardPage capabilities + + @@ -30,6 +31,7 @@ + SelectionMode="{Binding SelectionMode^}" + WrapSelection="{Binding WrapSelection}"/> diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 4edb0f137a..caad8b0854 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; @@ -49,25 +50,33 @@ namespace ControlCatalog.Pages context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, workingAreaRect); - var text = new FormattedText() { Typeface = new Typeface("Arial"), FontSize = 18 }; - text.Text = $"Bounds: {screen.Bounds.TopLeft} {screen.Bounds.Width}:{screen.Bounds.Height}"; - context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text); - - text.Text = $"WorkArea: {screen.WorkingArea.TopLeft} {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"; - context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text); + var formattedText = CreateFormattedText($"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height)); - text.Text = $"Scaling: {screen.PixelDensity * 100}%"; - context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text); - - text.Text = $"Primary: {screen.Primary}"; - context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text); - - text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}"; - context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); + formattedText = + CreateFormattedText($"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 20)); + + formattedText = CreateFormattedText($"Scaling: {screen.PixelDensity * 100}%"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); + + formattedText = CreateFormattedText($"Primary: {screen.Primary}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); + + formattedText = + CreateFormattedText( + $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}"); + context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 80)); } context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); } + + private FormattedText CreateFormattedText(string textToFormat) + { + return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, + Typeface.Default, 12, Brushes.Green); + } } } diff --git a/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml index 4db755b6fc..6afe6dd135 100644 --- a/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml +++ b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml @@ -11,7 +11,7 @@ - + diff --git a/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs new file mode 100644 index 0000000000..bbe970afd6 --- /dev/null +++ b/samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using Avalonia.Controls; +using Avalonia.Controls.Selection; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class ComboBoxPageViewModel : ViewModelBase + { + private bool _wrapSelection; + + public bool WrapSelection + { + get => _wrapSelection; + set => this.RaiseAndSetIfChanged(ref _wrapSelection, value); + } + } +} diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs index 7f2d6e9572..59489ebcc0 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -14,6 +14,7 @@ namespace ControlCatalog.ViewModels private bool _toggle; private bool _alwaysSelected; private bool _autoScrollToSelectedItem = true; + private bool _wrapSelection; private int _counter; private IObservable _selectionMode; @@ -85,6 +86,12 @@ namespace ControlCatalog.ViewModels set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value); } + public bool WrapSelection + { + get => _wrapSelection; + set => this.RaiseAndSetIfChanged(ref _wrapSelection, value); + } + public MiniCommand AddItemCommand { get; } public MiniCommand RemoveItemCommand { get; } public MiniCommand SelectRandomItemCommand { get; } diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index a4c6299278..4a8fb819ca 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -57,6 +57,9 @@ + + + diff --git a/samples/RenderDemo/Pages/CustomSkiaPage.cs b/samples/RenderDemo/Pages/CustomSkiaPage.cs index 2e59d934a1..9c524a7932 100644 --- a/samples/RenderDemo/Pages/CustomSkiaPage.cs +++ b/samples/RenderDemo/Pages/CustomSkiaPage.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Globalization; using Avalonia; using Avalonia.Controls; using Avalonia.Media; @@ -41,7 +42,10 @@ namespace RenderDemo.Pages { var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas; if (canvas == null) - context.DrawText(Brushes.Black, new Point(), _noSkia.PlatformImpl); + using (var c = new DrawingContext(context, false)) + { + c.DrawText(_noSkia, new Point()); + } else { canvas.Save(); @@ -108,10 +112,9 @@ namespace RenderDemo.Pages public override void Render(DrawingContext context) { - var noSkia = new FormattedText() - { - Text = "Current rendering API is not Skia" - }; + var noSkia = new FormattedText("Current rendering API is not Skia", CultureInfo.CurrentCulture, + FlowDirection.LeftToRight, Typeface.Default, 12, Brushes.Black); + context.Custom(new CustomDrawOp(new Rect(0, 0, Bounds.Width, Bounds.Height), noSkia)); Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background); } diff --git a/samples/RenderDemo/Pages/FormattedTextPage.axaml b/samples/RenderDemo/Pages/FormattedTextPage.axaml new file mode 100644 index 0000000000..92775bec9e --- /dev/null +++ b/samples/RenderDemo/Pages/FormattedTextPage.axaml @@ -0,0 +1,7 @@ + + diff --git a/samples/RenderDemo/Pages/FormattedTextPage.axaml.cs b/samples/RenderDemo/Pages/FormattedTextPage.axaml.cs new file mode 100644 index 0000000000..25e29c67a9 --- /dev/null +++ b/samples/RenderDemo/Pages/FormattedTextPage.axaml.cs @@ -0,0 +1,60 @@ +using System.Globalization; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media; + +namespace RenderDemo.Pages +{ + public class FormattedTextPage : UserControl + { + public FormattedTextPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public override void Render(DrawingContext context) + { + const string testString = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"; + + // Create the initial formatted text string. + var formattedText = new FormattedText( + testString, + CultureInfo.GetCultureInfo("en-us"), + FlowDirection.LeftToRight, + new Typeface("Verdana"), + 32, + Brushes.Black) { MaxTextWidth = 300, MaxTextHeight = 240 }; + + // Set a maximum width and height. If the text overflows these values, an ellipsis "..." appears. + + // Use a larger font size beginning at the first (zero-based) character and continuing for 5 characters. + // The font size is calculated in terms of points -- not as device-independent pixels. + formattedText.SetFontSize(36 * (96.0 / 72.0), 0, 5); + + // Use a Bold font weight beginning at the 6th character and continuing for 11 characters. + formattedText.SetFontWeight(FontWeight.Bold, 6, 11); + + var gradient = new LinearGradientBrush + { + GradientStops = + new GradientStops { new GradientStop(Colors.Orange, 0), new GradientStop(Colors.Teal, 1) }, + StartPoint = new RelativePoint(0,0, RelativeUnit.Relative), + EndPoint = new RelativePoint(0,1, RelativeUnit.Relative) + }; + + // Use a linear gradient brush beginning at the 6th character and continuing for 11 characters. + formattedText.SetForegroundBrush(gradient, 6, 11); + + // Use an Italic font style beginning at the 28th character and continuing for 28 characters. + formattedText.SetFontStyle(FontStyle.Italic, 28, 28); + + context.DrawText(formattedText, new Point(10, 0)); + } + } +} diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs index 857358f6b2..7f85606957 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs @@ -13,6 +13,7 @@ namespace RenderDemo.Pages private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; + private char[] _characters = new char[1]; private float _fontSize = 20; private int _direction = 10; @@ -38,7 +39,7 @@ namespace RenderDemo.Pages private void UpdateGlyphRun() { - var c = (uint)_rand.Next(65, 90); + var c = (char)_rand.Next(65, 90); if (_fontSize + _direction > 200) { @@ -54,6 +55,8 @@ namespace RenderDemo.Pages _glyphIndices[0] = _glyphTypeface.GetGlyph(c); + _characters[0] = c; + var scale = (double)_fontSize / _glyphTypeface.DesignEmHeight; var drawingGroup = new DrawingGroup(); @@ -61,7 +64,7 @@ namespace RenderDemo.Pages var glyphRunDrawing = new GlyphRunDrawing { Foreground = Brushes.Black, - GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices), + GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _characters, _glyphIndices) }; drawingGroup.Children.Add(glyphRunDrawing); diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 7e49cb5dfa..b6491a5890 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -25,7 +25,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() @@ -83,6 +83,8 @@ namespace Avalonia.Android if (options.Multiline) outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine; + + outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi; }); //_inputElement.PointerReleased += RestoreSoftKeyboard; diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 2d4f6a305f..308be1325c 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -10,7 +10,6 @@ using Avalonia.Input.Platform; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Rendering; -using Avalonia.PlatformSupport; using Avalonia.Skia; namespace Avalonia @@ -20,9 +19,9 @@ 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 +43,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; diff --git a/src/Android/Avalonia.Android/AppBuilder.cs b/src/Android/Avalonia.Android/AppBuilder.cs deleted file mode 100644 index 04f1ff00d0..0000000000 --- a/src/Android/Avalonia.Android/AppBuilder.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Avalonia.Controls; -using Avalonia.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 5c33dbcea6..6393ef3405 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -1,15 +1,18 @@ - + - monoandroid11.0 + net6.0-android;monoandroid11.0 + 21 true + true + portable - - TargetFramework=netstandard2.0 - + + + + + - - diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index 3c9f373a66..fbadc257e2 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs @@ -1,35 +1,55 @@ using Android.App; using Android.OS; using Android.Views; +using Android.Content.PM; +using AndroidX.AppCompat.App; +using Android.Content.Res; +using AndroidX.Lifecycle; namespace Avalonia.Android { - public abstract class AvaloniaActivity : Activity + public abstract class AvaloniaActivity : AppCompatActivity { internal AvaloniaView View; - object _content; - + internal AvaloniaViewModel _viewModel; protected override void OnCreate(Bundle savedInstanceState) { View = new AvaloniaView(this); - if (_content != null) - View.Content = _content; SetContentView(View); + + _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; + + if (_viewModel.Content != null) + { + View.Content = _viewModel.Content; + } + 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/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/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/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 reference, MethodInfo method, ParameterInfo[] parameters) + public Accessor(WeakReference reference, MethodInfo method) { _ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = method ?? throw new ArgumentNullException(nameof(method)); var returnType = method.ReturnType; - bool hasReturn = returnType != typeof(void); - var signatureTypeCount = (hasReturn ? 1 : 0) + parameters.Length; + var parameters = method.GetParameters(); + + var signatureTypeCount = parameters.Length + 1; var paramTypes = new Type[signatureTypeCount]; + for (var i = 0; i < parameters.Length; i++) { ParameterInfo parameter = parameters[i]; @@ -101,16 +93,9 @@ namespace Avalonia.Data.Core.Plugins paramTypes[i] = parameter.ParameterType; } - if (hasReturn) - { - paramTypes[paramTypes.Length - 1] = returnType; + paramTypes[paramTypes.Length - 1] = returnType; - PropertyType = Expression.GetFuncType(paramTypes); - } - else - { - PropertyType = Expression.GetActionType(paramTypes); - } + PropertyType = Expression.GetDelegateType(paramTypes); if (method.IsStatic) { diff --git a/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs b/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs index 90d1c52ff5..251dfe4351 100644 --- a/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs +++ b/src/Avalonia.Base/Utilities/ImmutableReadOnlyListStructEnumerator.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Avalonia.Utilities { - public struct ImmutableReadOnlyListStructEnumerator : IEnumerator, IEnumerator + public struct ImmutableReadOnlyListStructEnumerator : IEnumerator { private readonly IReadOnlyList _readOnlyList; private int _pos; diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index e6da450ae9..e97d97cc49 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -124,6 +124,9 @@ namespace Avalonia.Build.Tasks var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure", TypeAttributes.Class, asm.MainModule.TypeSystem.Object); asm.MainModule.Types.Add(indexerAccessorClosure); + var trampolineBuilder = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlTrampolines", + TypeAttributes.Class, asm.MainModule.TypeSystem.Object); + asm.MainModule.Types.Add(trampolineBuilder); var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem); var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem, @@ -133,6 +136,7 @@ namespace Avalonia.Build.Tasks AvaloniaXamlIlLanguage.CustomValueConverter, new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)), new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)), + new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)), new DeterministicIdGenerator()); @@ -256,7 +260,8 @@ namespace Avalonia.Build.Tasks true), (closureName, closureBaseType) => populateBuilder.DefineSubType(closureBaseType, closureName, false), - (s, returnType, parameters) => builder.DefineDelegateSubType(s, false, returnType, parameters), + (closureName, returnType, parameterTypes) => + populateBuilder.DefineDelegateSubType(closureName, false, returnType, parameterTypes), res.Uri, res ); diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index a7560c37f2..2c206b53f6 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -43,6 +43,10 @@ MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.Off MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Notifications.NotificationCard.CloseOnClickProperty' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation. +MembersMustExist : Member 'protected Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.CreateFormattedText()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls.Presenters.TextPresenter.FormattedText.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. @@ -63,4 +67,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 64 +Total Issues: 68 diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index d44b2ab0db..8779ae9122 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -14,9 +14,9 @@ namespace Avalonia.Controls public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new() { private static bool s_setupWasAlreadyCalled; - private Action _optionsInitializers; - private Func _appFactory; - private IApplicationLifetime _lifetime; + private Action? _optionsInitializers; + private Func? _appFactory; + private IApplicationLifetime? _lifetime; /// /// Gets or sets the instance. @@ -31,32 +31,32 @@ namespace Avalonia.Controls /// /// Gets the instance being initialized. /// - public Application Instance { get; private set; } + public Application? Instance { get; private set; } /// /// Gets the type of the Instance (even if it's not created yet) /// - public Type ApplicationType { get; private set; } + public Type? ApplicationType { get; private set; } /// /// Gets or sets a method to call the initialize the windowing subsystem. /// - public Action WindowingSubsystemInitializer { get; private set; } + public Action? WindowingSubsystemInitializer { get; private set; } /// /// Gets the name of the currently selected windowing subsystem. /// - public string WindowingSubsystemName { get; private set; } + public string? WindowingSubsystemName { get; private set; } /// /// Gets or sets a method to call the initialize the windowing subsystem. /// - public Action RenderingSubsystemInitializer { get; private set; } + public Action? RenderingSubsystemInitializer { get; private set; } /// /// Gets the name of the currently selected rendering subsystem. /// - public string RenderingSubsystemName { get; private set; } + public string? RenderingSubsystemName { get; private set; } /// /// Gets or sets a method to call after the is setup. @@ -126,7 +126,7 @@ namespace Avalonia.Controls /// The window type. /// A delegate that will be called to create a data context for the window (optional). [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] - public void Start(Func dataContextProvider = null) + public void Start(Func? dataContextProvider = null) where TMainWindow : Window, new() { AfterSetup(builder => @@ -134,7 +134,7 @@ namespace Avalonia.Controls var window = new TMainWindow(); if (dataContextProvider != null) window.DataContext = dataContextProvider(); - ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime) + ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!) .MainWindow = window; }); @@ -155,7 +155,7 @@ namespace Avalonia.Controls public void Start(AppMainDelegate main, string[] args) { Setup(); - main(Instance, args); + main(Instance!, args); } /// @@ -226,8 +226,8 @@ namespace Avalonia.Controls var platformClassName = assemblyName.Replace("Avalonia.", string.Empty) + "Platform"; var platformClassFullName = assemblyName + "." + platformClassName; var platformClass = assembly.GetType(platformClassFullName); - var init = platformClass.GetRuntimeMethod("Initialize", Type.EmptyTypes); - init.Invoke(null, null); + var init = platformClass!.GetRuntimeMethod("Initialize", Type.EmptyTypes); + init!.Invoke(null, null); }; public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); @@ -251,7 +251,7 @@ namespace Avalonia.Controls where constructor.GetParameters().Length == 0 && !constructor.IsStatic select constructor).Single() into constructor select (Action)(() => constructor.Invoke(Array.Empty())); - Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); + Delegate.Combine(moduleInitializers.ToArray())!.DynamicInvoke(); } /// @@ -292,6 +292,11 @@ namespace Avalonia.Controls throw new InvalidOperationException("No rendering system configured."); } + if (_appFactory == null) + { + throw new InvalidOperationException("No Application factory configured."); + } + if (s_setupWasAlreadyCalled && CheckSetup) { throw new InvalidOperationException("Setup was already called on one of AppBuilder instances"); diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 584c3db23b..69fd6cabf8 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -13,7 +13,6 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Threading; -#nullable enable namespace Avalonia { @@ -177,13 +176,13 @@ namespace Avalonia /// public IApplicationLifetime? ApplicationLifetime { get; set; } - event Action> IGlobalStyles.GlobalStylesAdded + event Action>? IGlobalStyles.GlobalStylesAdded { add => _stylesAdded += value; remove => _stylesAdded -= value; } - event Action> IGlobalStyles.GlobalStylesRemoved + event Action>? IGlobalStyles.GlobalStylesRemoved { add => _stylesRemoved += value; remove => _stylesRemoved -= value; diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index edddf31d45..76e2d3a161 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -15,26 +15,26 @@ namespace Avalonia.Controls.ApplicationLifetimes public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable { private int _exitCode; - private CancellationTokenSource _cts; + private CancellationTokenSource? _cts; private bool _isShuttingDown; private HashSet _windows = new HashSet(); - private static ClassicDesktopStyleApplicationLifetime _activeLifetime; + private static ClassicDesktopStyleApplicationLifetime? _activeLifetime; static ClassicDesktopStyleApplicationLifetime() { Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened); Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent); } - private static void WindowClosedEvent(object sender, RoutedEventArgs e) + private static void WindowClosedEvent(object? sender, RoutedEventArgs e) { - _activeLifetime?._windows.Remove((Window)sender); - _activeLifetime?.HandleWindowClosed((Window)sender); + _activeLifetime?._windows.Remove((Window)sender!); + _activeLifetime?.HandleWindowClosed((Window)sender!); } - private static void OnWindowOpened(object sender, RoutedEventArgs e) + private static void OnWindowOpened(object? sender, RoutedEventArgs e) { - _activeLifetime?._windows.Add((Window)sender); + _activeLifetime?._windows.Add((Window)sender!); } public ClassicDesktopStyleApplicationLifetime() @@ -46,24 +46,24 @@ namespace Avalonia.Controls.ApplicationLifetimes } /// - public event EventHandler Startup; + public event EventHandler? Startup; /// - public event EventHandler ShutdownRequested; + public event EventHandler? ShutdownRequested; /// - public event EventHandler Exit; + public event EventHandler? Exit; /// /// Gets the arguments passed to the AppBuilder Start method. /// - public string[] Args { get; set; } + public string[]? Args { get; set; } /// public ShutdownMode ShutdownMode { get; set; } /// - public Window MainWindow { get; set; } + public Window? MainWindow { get; set; } public IReadOnlyList Windows => _windows.ToList(); @@ -183,7 +183,7 @@ namespace Avalonia.Controls.ApplicationLifetimes return true; } - private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) => DoShutdown(e); + private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => DoShutdown(e); } public class ClassicDesktopStyleApplicationLifetimeOptions diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index a83229b732..2bd5c1238d 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls.ApplicationLifetimes /// /// method. /// - string[] Args { get; } + string[]? Args { get; } /// /// Gets or sets the . This property indicates whether the application is shutdown explicitly or implicitly. @@ -38,7 +38,7 @@ namespace Avalonia.Controls.ApplicationLifetimes /// /// The main window. /// - Window MainWindow { get; set; } + Window? MainWindow { get; set; } IReadOnlyList Windows { get; } @@ -58,6 +58,6 @@ namespace Avalonia.Controls.ApplicationLifetimes /// will try to close each non-owned open window, invoking the event on each and allowing /// each window to cancel the shutdown of the application. Windows cannot however prevent OS shutdown. /// - event EventHandler ShutdownRequested; + event EventHandler? ShutdownRequested; } } diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs index eb451f51af..e25815602e 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs @@ -2,6 +2,6 @@ namespace Avalonia.Controls.ApplicationLifetimes { public interface ISingleViewApplicationLifetime : IApplicationLifetime { - Control MainView { get; set; } + Control? MainView { get; set; } } } diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 0e946126ea..930e250334 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -69,7 +69,7 @@ namespace Avalonia.Controls /// /// The text that is used to determine which items to display in /// the . - public string Parameter { get; private set; } + public string? Parameter { get; private set; } /// /// Initializes a new instance of the @@ -79,7 +79,7 @@ namespace Avalonia.Controls /// /// property, which is used to filter items for the /// control. - public PopulatingEventArgs(string parameter) + public PopulatingEventArgs(string? parameter) { Parameter = parameter; } @@ -98,7 +98,7 @@ namespace Avalonia.Controls /// The type used for filtering the /// . This type can /// be either a string or an object. - public delegate bool AutoCompleteFilterPredicate(string search, T item); + public delegate bool AutoCompleteFilterPredicate(string? search, T item); /// /// Specifies how text in the text box portion of the @@ -245,7 +245,7 @@ namespace Avalonia.Controls /// . /// This type can be either a string or an object. /// - public delegate string AutoCompleteSelector(string search, T item); + public delegate string AutoCompleteSelector(string? search, T item); /// /// Represents a control that provides a text box for user input and a @@ -275,19 +275,19 @@ namespace Avalonia.Controls /// private const string ElementTextBox = "PART_TextBox"; - private IEnumerable _itemsEnumerable; + private IEnumerable? _itemsEnumerable; /// /// Gets or sets a local cached copy of the items data. /// - private List _items; + private List? _items; /// /// Gets or sets the observable collection that contains references to /// all of the items in the generated view of data that is provided to /// the selection-style control adapter. /// - private AvaloniaList _view; + private AvaloniaList? _view; /// /// Gets or sets a value to ignore a number of pending change handlers. @@ -338,7 +338,7 @@ namespace Avalonia.Controls /// Gets or sets the DispatcherTimer used for the MinimumPopulateDelay /// condition for auto completion. /// - private DispatcherTimer _delayTimer; + private DispatcherTimer? _delayTimer; /// /// Gets or sets a value indicating whether a read-only dependency @@ -351,47 +351,47 @@ namespace Avalonia.Controls /// /// The TextBox template part. /// - private TextBox _textBox; - private IDisposable _textBoxSubscriptions; + private TextBox? _textBox; + private IDisposable? _textBoxSubscriptions; /// /// The SelectionAdapter. /// - private ISelectionAdapter _adapter; + private ISelectionAdapter? _adapter; /// /// A control that can provide updated string values from a binding. /// - private BindingEvaluator _valueBindingEvaluator; + private BindingEvaluator? _valueBindingEvaluator; /// /// A weak subscription for the collection changed event. /// - private IDisposable _collectionChangeSubscription; + private IDisposable? _collectionChangeSubscription; - private Func>> _asyncPopulator; - private CancellationTokenSource _populationCancellationTokenSource; + private Func>>? _asyncPopulator; + private CancellationTokenSource? _populationCancellationTokenSource; private bool _itemTemplateIsFromValueMemberBinding = true; private bool _settingItemTemplateFromValueMemberBinding; - private object _selectedItem; + private object? _selectedItem; private bool _isDropDownOpen; private bool _isFocused = false; - private string _text = string.Empty; - private string _searchText = string.Empty; + private string? _text = string.Empty; + private string? _searchText = string.Empty; - private AutoCompleteFilterPredicate _itemFilter; - private AutoCompleteFilterPredicate _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith); + private AutoCompleteFilterPredicate? _itemFilter; + private AutoCompleteFilterPredicate? _textFilter = AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith); - private AutoCompleteSelector _itemSelector; - private AutoCompleteSelector _textSelector; + private AutoCompleteSelector? _itemSelector; + private AutoCompleteSelector? _textSelector; public static readonly RoutedEvent SelectionChangedEvent = RoutedEvent.Register(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); - public static readonly StyledProperty WatermarkProperty = + public static readonly StyledProperty WatermarkProperty = TextBox.WatermarkProperty.AddOwner(); /// @@ -479,8 +479,8 @@ namespace Avalonia.Controls /// The identifier the /// /// dependency property. - public static readonly DirectProperty SelectedItemProperty = - AvaloniaProperty.RegisterDirect( + public static readonly DirectProperty SelectedItemProperty = + AvaloniaProperty.RegisterDirect( nameof(SelectedItem), o => o.SelectedItem, (o, v) => o.SelectedItem = v, @@ -495,7 +495,7 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty TextProperty = + public static readonly DirectProperty TextProperty = TextBlock.TextProperty.AddOwnerWithDataValidation( o => o.Text, (o, v) => o.Text = v, @@ -510,8 +510,8 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty SearchTextProperty = - AvaloniaProperty.RegisterDirect( + public static readonly DirectProperty SearchTextProperty = + AvaloniaProperty.RegisterDirect( nameof(SearchText), o => o.SearchText, unsetValue: string.Empty); @@ -535,8 +535,8 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty> ItemFilterProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty?> ItemFilterProperty = + AvaloniaProperty.RegisterDirect?>( nameof(ItemFilter), o => o.ItemFilter, (o, v) => o.ItemFilter = v); @@ -549,8 +549,8 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty> TextFilterProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty?> TextFilterProperty = + AvaloniaProperty.RegisterDirect?>( nameof(TextFilter), o => o.TextFilter, (o, v) => o.TextFilter = v, @@ -564,8 +564,8 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty> ItemSelectorProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty?> ItemSelectorProperty = + AvaloniaProperty.RegisterDirect?>( nameof(ItemSelector), o => o.ItemSelector, (o, v) => o.ItemSelector = v); @@ -578,8 +578,8 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty> TextSelectorProperty = - AvaloniaProperty.RegisterDirect>( + public static readonly DirectProperty?> TextSelectorProperty = + AvaloniaProperty.RegisterDirect?>( nameof(TextSelector), o => o.TextSelector, (o, v) => o.TextSelector = v); @@ -592,14 +592,14 @@ namespace Avalonia.Controls /// The identifier for the /// /// dependency property. - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( + public static readonly DirectProperty ItemsProperty = + AvaloniaProperty.RegisterDirect( nameof(Items), o => o.Items, (o, v) => o.Items = v); - public static readonly DirectProperty>>> AsyncPopulatorProperty = - AvaloniaProperty.RegisterDirect>>>( + public static readonly DirectProperty>>?> AsyncPopulatorProperty = + AvaloniaProperty.RegisterDirect>>?>( nameof(AsyncPopulator), o => o.AsyncPopulator, (o, v) => o.AsyncPopulator = v); @@ -640,7 +640,7 @@ namespace Avalonia.Controls /// The event data. private void OnControlIsEnabledChanged(AvaloniaPropertyChangedEventArgs e) { - bool isEnabled = (bool)e.NewValue; + bool isEnabled = (bool)e.NewValue!; if (!isEnabled) { IsDropDownOpen = false; @@ -655,7 +655,7 @@ namespace Avalonia.Controls /// Event arguments. private void OnMinimumPopulateDelayChanged(AvaloniaPropertyChangedEventArgs e) { - var newValue = (TimeSpan)e.NewValue; + var newValue = (TimeSpan)e.NewValue!; // Stop any existing timer if (_delayTimer != null) @@ -695,8 +695,8 @@ namespace Avalonia.Controls return; } - bool oldValue = (bool)e.OldValue; - bool newValue = (bool)e.NewValue; + bool oldValue = (bool)e.OldValue!; + bool newValue = (bool)e.NewValue!; if (newValue) { @@ -750,7 +750,7 @@ namespace Avalonia.Controls /// Event arguments. private void OnTextPropertyChanged(AvaloniaPropertyChangedEventArgs e) { - TextUpdated((string)e.NewValue, false); + TextUpdated((string?)e.NewValue, false); } private void OnSearchTextPropertyChanged(AvaloniaPropertyChangedEventArgs e) @@ -778,7 +778,7 @@ namespace Avalonia.Controls /// Event arguments. private void OnFilterModePropertyChanged(AvaloniaPropertyChangedEventArgs e) { - AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue; + AutoCompleteFilterMode mode = (AutoCompleteFilterMode)e.NewValue!; // Sets the filter predicate for the new value TextFilter = AutoCompleteSearch.GetFilter(mode); @@ -790,7 +790,7 @@ namespace Avalonia.Controls /// Event arguments. private void OnItemFilterPropertyChanged(AvaloniaPropertyChangedEventArgs e) { - AutoCompleteFilterPredicate value = e.NewValue as AutoCompleteFilterPredicate; + var value = e.NewValue as AutoCompleteFilterPredicate; // If null, revert to the "None" predicate if (value == null) @@ -810,7 +810,7 @@ namespace Avalonia.Controls /// Event arguments. private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) { - OnItemsChanged((IEnumerable)e.NewValue); + OnItemsChanged((IEnumerable?)e.NewValue); } private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e) @@ -818,7 +818,7 @@ namespace Avalonia.Controls if (!_settingItemTemplateFromValueMemberBinding) _itemTemplateIsFromValueMemberBinding = false; } - private void OnValueMemberBindingChanged(IBinding value) + private void OnValueMemberBindingChanged(IBinding? value) { if(_itemTemplateIsFromValueMemberBinding) { @@ -828,7 +828,8 @@ namespace Avalonia.Controls (o, _) => { var control = new ContentControl(); - control.Bind(ContentControl.ContentProperty, value); + if (value is not null) + control.Bind(ContentControl.ContentProperty, value); return control; }); @@ -975,7 +976,7 @@ namespace Avalonia.Controls /// The object used /// when binding to a collection property. [AssignBinding] - public IBinding ValueMemberBinding + public IBinding? ValueMemberBinding { get { return _valueBindingEvaluator?.ValueBinding; } set @@ -998,7 +999,7 @@ namespace Avalonia.Controls /// then displayed in the text box, the SelectedItem property will be /// a null reference. /// - public object SelectedItem + public object? SelectedItem { get { return _selectedItem; } set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); } @@ -1010,7 +1011,7 @@ namespace Avalonia.Controls /// /// The text in the text box portion of the /// control. - public string Text + public string? Text { get { return _text; } set { SetAndRaise(TextProperty, ref _text, value); } @@ -1029,7 +1030,7 @@ namespace Avalonia.Controls /// Text property, but is set after the TextChanged event occurs /// and before the Populating event. /// - public string SearchText + public string? SearchText { get { return _searchText; } private set @@ -1071,7 +1072,7 @@ namespace Avalonia.Controls set { SetValue(FilterModeProperty, value); } } - public string Watermark + public string? Watermark { get { return GetValue(WatermarkProperty); } set { SetValue(WatermarkProperty, value); } @@ -1091,7 +1092,7 @@ namespace Avalonia.Controls /// The filter mode is automatically set to Custom if you set the /// ItemFilter property. /// - public AutoCompleteFilterPredicate ItemFilter + public AutoCompleteFilterPredicate? ItemFilter { get { return _itemFilter; } set { SetAndRaise(ItemFilterProperty, ref _itemFilter, value); } @@ -1111,7 +1112,7 @@ namespace Avalonia.Controls /// The search mode is automatically set to Custom if you set the /// TextFilter property. /// - public AutoCompleteFilterPredicate TextFilter + public AutoCompleteFilterPredicate? TextFilter { get { return _textFilter; } set { SetAndRaise(TextFilterProperty, ref _textFilter, value); } @@ -1127,7 +1128,7 @@ namespace Avalonia.Controls /// text and one of the items specified by the /// . /// - public AutoCompleteSelector ItemSelector + public AutoCompleteSelector? ItemSelector { get { return _itemSelector; } set { SetAndRaise(ItemSelectorProperty, ref _itemSelector, value); } @@ -1145,13 +1146,13 @@ namespace Avalonia.Controls /// /// in a text-based way. /// - public AutoCompleteSelector TextSelector + public AutoCompleteSelector? TextSelector { get { return _textSelector; } set { SetAndRaise(TextSelectorProperty, ref _textSelector, value); } } - public Func>> AsyncPopulator + public Func>>? AsyncPopulator { get { return _asyncPopulator; } set { SetAndRaise(AsyncPopulatorProperty, ref _asyncPopulator, value); } @@ -1165,7 +1166,7 @@ namespace Avalonia.Controls /// The collection that is used to generate the items of the /// drop-down portion of the /// control. - public IEnumerable Items + public IEnumerable? Items { get { return _itemsEnumerable; } set { SetAndRaise(ItemsProperty, ref _itemsEnumerable, value); } @@ -1174,12 +1175,12 @@ namespace Avalonia.Controls /// /// Gets or sets the drop down popup control. /// - private Popup DropDownPopup { get; set; } + private Popup? DropDownPopup { get; set; } /// /// Gets or sets the Text template part. /// - private TextBox TextBox + private TextBox? TextBox { get { return _textBox; } set @@ -1243,7 +1244,7 @@ namespace Avalonia.Controls /// use with AutoCompleteBox or deriving from AutoCompleteBox to /// create a custom control. /// - protected ISelectionAdapter SelectionAdapter + protected ISelectionAdapter? SelectionAdapter { get { return _adapter; } set @@ -1279,10 +1280,10 @@ namespace Avalonia.Controls /// A object, /// if possible. Otherwise, null. /// - protected virtual ISelectionAdapter GetSelectionAdapterPart(INameScope nameScope) + protected virtual ISelectionAdapter? GetSelectionAdapterPart(INameScope nameScope) { - ISelectionAdapter adapter = null; - SelectingItemsControl selector = nameScope.Find(ElementSelector); + ISelectionAdapter? adapter = null; + SelectingItemsControl? selector = nameScope.Find(ElementSelector); if (selector != null) { // Check if it is already an IItemsSelector @@ -1316,7 +1317,7 @@ namespace Avalonia.Controls // Set the template parts. Individual part setters remove and add // any event handlers. - Popup popup = e.NameScope.Find(ElementPopup); + Popup? popup = e.NameScope.Find(ElementPopup); if (popup != null) { DropDownPopup = popup; @@ -1358,7 +1359,7 @@ namespace Avalonia.Controls /// that contains the event data. protected override void OnKeyDown(KeyEventArgs e) { - Contract.Requires(e != null); + _ = e ?? throw new ArgumentNullException(nameof(e)); base.OnKeyDown(e); @@ -1453,7 +1454,7 @@ namespace Avalonia.Controls /// otherwise, false. protected bool HasFocus() { - IVisual focused = FocusManager.Instance.Current; + IVisual? focused = FocusManager.Instance?.Current; while (focused != null) { @@ -1464,11 +1465,11 @@ namespace Avalonia.Controls // This helps deal with popups that may not be in the same // visual tree - IVisual parent = focused.GetVisualParent(); + IVisual? parent = focused.GetVisualParent(); if (parent == null) { // Try the logical parent. - IControl element = focused as IControl; + IControl? element = focused as IControl; if (element != null) { parent = element.Parent; @@ -1519,7 +1520,7 @@ namespace Avalonia.Controls /// Occurs when the text in the text box portion of the /// changes. /// - public event EventHandler TextChanged; + public event EventHandler? TextChanged; /// /// Occurs when the @@ -1535,7 +1536,7 @@ namespace Avalonia.Controls /// In this case, if you want possible matches to appear, you must /// provide the logic for populating the selection adapter. /// - public event EventHandler Populating; + public event EventHandler? Populating; /// /// Occurs when the @@ -1544,35 +1545,35 @@ namespace Avalonia.Controls /// /// property. /// - public event EventHandler Populated; + public event EventHandler? Populated; /// /// Occurs when the value of the /// /// property is changing from false to true. /// - public event EventHandler DropDownOpening; + public event EventHandler? DropDownOpening; /// /// Occurs when the value of the /// /// property has changed from false to true and the drop-down is open. /// - public event EventHandler DropDownOpened; + public event EventHandler? DropDownOpened; /// /// Occurs when the /// /// property is changing from true to false. /// - public event EventHandler DropDownClosing; + public event EventHandler? DropDownClosing; /// /// Occurs when the /// /// property was changed from true to false and the drop-down is open. /// - public event EventHandler DropDownClosed; + public event EventHandler? DropDownClosed; /// /// Occurs when the selected item in the drop-down portion of the @@ -1740,7 +1741,7 @@ namespace Avalonia.Controls /// /// The source object. /// The event data. - private void DropDownPopup_Closed(object sender, EventArgs e) + private void DropDownPopup_Closed(object? sender, EventArgs e) { // Force the drop down dependency property to be false. if (IsDropDownOpen) @@ -1760,7 +1761,7 @@ namespace Avalonia.Controls /// /// The source object. /// The event arguments. - private void PopulateDropDown(object sender, EventArgs e) + private void PopulateDropDown(object? sender, EventArgs e) { if (_delayTimer != null) { @@ -1786,7 +1787,7 @@ namespace Avalonia.Controls PopulateComplete(); } } - private bool TryPopulateAsync(string searchText) + private bool TryPopulateAsync(string? searchText) { _populationCancellationTokenSource?.Cancel(false); _populationCancellationTokenSource?.Dispose(); @@ -1804,12 +1805,12 @@ namespace Avalonia.Controls return true; } - private async Task PopulateAsync(string searchText, CancellationToken cancellationToken) + private async Task PopulateAsync(string? searchText, CancellationToken cancellationToken) { try { - IEnumerable result = await _asyncPopulator.Invoke(searchText, cancellationToken); + IEnumerable result = await _asyncPopulator!.Invoke(searchText, cancellationToken); var resultList = result.ToList(); if (cancellationToken.IsCancellationRequested) @@ -1878,9 +1879,9 @@ namespace Avalonia.Controls /// A value indicating whether to clear /// the data context after the lookup is performed. /// Formatted Value. - private string FormatValue(object value, bool clearDataContext) + private string? FormatValue(object? value, bool clearDataContext) { - string result = FormatValue(value); + string? result = FormatValue(value); if(clearDataContext && _valueBindingEvaluator != null) { _valueBindingEvaluator.ClearDataContext(); @@ -1902,7 +1903,7 @@ namespace Avalonia.Controls /// /// Override this method to provide a custom string conversion. /// - protected virtual string FormatValue(object value) + protected virtual string? FormatValue(object? value) { if (_valueBindingEvaluator != null) { @@ -1923,7 +1924,7 @@ namespace Avalonia.Controls Dispatcher.UIThread.Post(() => { // Call the central updated text method as a user-initiated action - TextUpdated(_textBox.Text, true); + TextUpdated(_textBox!.Text, true); }); } @@ -1933,7 +1934,7 @@ namespace Avalonia.Controls /// text changed events when there is a change. /// /// The new string value. - private void UpdateTextValue(string value) + private void UpdateTextValue(string? value) { UpdateTextValue(value, null); } @@ -1949,7 +1950,7 @@ namespace Avalonia.Controls /// underlying text dependency property is updated. In a non-user /// interaction, the text box value is updated. When user initiated is /// null, all values are updated. - private void UpdateTextValue(string value, bool? userInitiated) + private void UpdateTextValue(string? value, bool? userInitiated) { bool callTextChanged = false; // Update the Text dependency property @@ -1987,7 +1988,7 @@ namespace Avalonia.Controls /// A value indicating whether the update /// is a user-initiated action. This should be a True value when the /// TextUpdated method is called from a TextBox event handler. - private void TextUpdated(string newText, bool userInitiated) + private void TextUpdated(string? newText, bool userInitiated) { // Only process this event if it is coming from someone outside // setting the Text dependency property directly. @@ -2087,7 +2088,7 @@ namespace Avalonia.Controls bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null; int view_index = 0; - int view_count = _view.Count; + int view_count = _view!.Count; List items = _items; foreach (object item in items) { @@ -2096,7 +2097,7 @@ namespace Avalonia.Controls { if (stringFiltering) { - inResults = TextFilter(text, FormatValue(item)); + inResults = TextFilter!(text, FormatValue(item)); } else { @@ -2166,7 +2167,7 @@ namespace Avalonia.Controls /// adapter's ItemsSource to the view if appropriate. /// /// The new enumerable reference. - private void OnItemsChanged(IEnumerable newValue) + private void OnItemsChanged(IEnumerable? newValue) { // Remove handler for oldValue.CollectionChanged (if present) _collectionChangeSubscription?.Dispose(); @@ -2198,28 +2199,28 @@ namespace Avalonia.Controls /// /// The object that raised the event. /// The event data. - private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { // Update the cache if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null) { for (int index = 0; index < e.OldItems.Count; index++) { - _items.RemoveAt(e.OldStartingIndex); + _items!.RemoveAt(e.OldStartingIndex); } } - if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null && _items.Count >= e.NewStartingIndex) + if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null && _items!.Count >= e.NewStartingIndex) { for (int index = 0; index < e.NewItems.Count; index++) { - _items.Insert(e.NewStartingIndex + index, e.NewItems[index]); + _items.Insert(e.NewStartingIndex + index, e.NewItems[index]!); } } if (e.Action == NotifyCollectionChangedAction.Replace && e.NewItems != null && e.OldItems != null) { for (int index = 0; index < e.NewItems.Count; index++) { - _items[e.NewStartingIndex] = e.NewItems[index]; + _items![e.NewStartingIndex] = e.NewItems[index]!; } } @@ -2228,7 +2229,7 @@ namespace Avalonia.Controls { for (int index = 0; index < e.OldItems.Count; index++) { - _view.Remove(e.OldItems[index]); + _view!.Remove(e.OldItems[index]!); } } @@ -2270,7 +2271,7 @@ namespace Avalonia.Controls RefreshView(); // Fire the Populated event containing the read-only view data. - PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view)); + PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view!)); OnPopulated(populated); if (SelectionAdapter != null && SelectionAdapter.Items != _view) @@ -2278,7 +2279,7 @@ namespace Avalonia.Controls SelectionAdapter.Items = _view; } - bool isDropDownOpen = _userCalledPopulate && (_view.Count > 0); + bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0); if (isDropDownOpen != IsDropDownOpen) { _ignorePropertyChange = true; @@ -2306,20 +2307,20 @@ namespace Avalonia.Controls private void UpdateTextCompletion(bool userInitiated) { // By default this method will clear the selected value - object newSelectedItem = null; - string text = Text; + object? newSelectedItem = null; + string? text = Text; // Text search is StartsWith explicit and only when enabled, in // line with WPF's ComboBox lookup. When in use it will associate // a Value with the Text if it is found in ItemsSource. This is // only valid when there is data and the user initiated the action. - if (_view.Count > 0) + if (_view!.Count > 0) { if (IsTextCompletionEnabled && TextBox != null && userInitiated) { int currentLength = TextBox.Text?.Length ?? 0; int selectionStart = TextBoxSelectionStart; - if (selectionStart == text.Length && selectionStart > _textSelectionStart) + if (selectionStart == text?.Length && selectionStart > _textSelectionStart) { // When the FilterMode dependency property is set to // either StartsWith or StartsWithCaseSensitive, the @@ -2327,7 +2328,7 @@ namespace Avalonia.Controls // performance on the lookup. It assumes that the // FilterMode the user has selected is an acceptable // case sensitive matching function for their scenario. - object top = FilterMode == AutoCompleteFilterMode.StartsWith || FilterMode == AutoCompleteFilterMode.StartsWithCaseSensitive + object? top = FilterMode == AutoCompleteFilterMode.StartsWith || FilterMode == AutoCompleteFilterMode.StartsWithCaseSensitive ? _view[0] : TryGetMatch(text, _view, AutoCompleteSearch.GetFilter(AutoCompleteFilterMode.StartsWith)); @@ -2335,18 +2336,18 @@ namespace Avalonia.Controls if (top != null) { newSelectedItem = top; - string topString = FormatValue(top, true); + string? topString = FormatValue(top, true); // Only replace partially when the two words being the same - int minLength = Math.Min(topString.Length, Text.Length); - if (AutoCompleteSearch.Equals(Text.Substring(0, minLength), topString.Substring(0, minLength))) + int minLength = Math.Min(topString?.Length ?? 0, Text?.Length ?? 0); + if (AutoCompleteSearch.Equals(Text?.Substring(0, minLength), topString?.Substring(0, minLength))) { // Update the text UpdateTextValue(topString); // Select the text past the user's caret TextBox.SelectionStart = currentLength; - TextBox.SelectionEnd = topString.Length; + TextBox.SelectionEnd = topString?.Length ?? 0; } } } @@ -2392,8 +2393,11 @@ namespace Avalonia.Controls /// The predicate to use for the partial or /// exact match. /// Returns the object or null. - private object TryGetMatch(string searchText, AvaloniaList view, AutoCompleteFilterPredicate predicate) + private object? TryGetMatch(string? searchText, AvaloniaList view, AutoCompleteFilterPredicate? predicate) { + if (predicate is null) + return null; + if (view != null && view.Count > 0) { foreach (object o in view) @@ -2428,9 +2432,9 @@ namespace Avalonia.Controls /// that is displayed in the text box part. /// /// The new item. - private void OnSelectedItemChanged(object newItem) + private void OnSelectedItemChanged(object? newItem) { - string text; + string? text; if (newItem == null) { @@ -2461,9 +2465,9 @@ namespace Avalonia.Controls /// /// The source object. /// The selection changed event data. - private void OnAdapterSelectionChanged(object sender, SelectionChangedEventArgs e) + private void OnAdapterSelectionChanged(object? sender, SelectionChangedEventArgs e) { - SelectedItem = _adapter.SelectedItem; + SelectedItem = _adapter!.SelectedItem; } //TODO Check UpdateTextCompletion @@ -2472,7 +2476,7 @@ namespace Avalonia.Controls /// /// The source object. /// The event data. - private void OnAdapterSelectionComplete(object sender, RoutedEventArgs e) + private void OnAdapterSelectionComplete(object? sender, RoutedEventArgs e) { IsDropDownOpen = false; @@ -2482,7 +2486,7 @@ namespace Avalonia.Controls // Text should not be selected ClearTextBoxSelection(); - TextBox.Focus(); + TextBox!.Focus(); } /// @@ -2490,7 +2494,7 @@ namespace Avalonia.Controls /// /// The source object. /// The event data. - private void OnAdapterSelectionCanceled(object sender, RoutedEventArgs e) + private void OnAdapterSelectionCanceled(object? sender, RoutedEventArgs e) { UpdateTextValue(SearchText); @@ -2510,7 +2514,7 @@ namespace Avalonia.Controls /// /// The built-in search mode. /// Returns the string-based comparison function. - public static AutoCompleteFilterPredicate GetFilter(AutoCompleteFilterMode FilterMode) + public static AutoCompleteFilterPredicate? GetFilter(AutoCompleteFilterMode FilterMode) { switch (FilterMode) { @@ -2566,9 +2570,11 @@ namespace Avalonia.Controls /// The string value to search for. /// The string comparison type. /// Returns true when the substring is found. - private static bool Contains(string s, string value, StringComparison comparison) + private static bool Contains(string? s, string? value, StringComparison comparison) { - return s.IndexOf(value, comparison) >= 0; + if (s is not null && value is not null) + return s.IndexOf(value, comparison) >= 0; + return false; } /// @@ -2577,9 +2583,11 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool StartsWith(string text, string value) + public static bool StartsWith(string? text, string? value) { - return value.StartsWith(text, StringComparison.CurrentCultureIgnoreCase); + if (value is not null && text is not null) + return value.StartsWith(text, StringComparison.CurrentCultureIgnoreCase); + return false; } /// @@ -2588,9 +2596,11 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool StartsWithCaseSensitive(string text, string value) + public static bool StartsWithCaseSensitive(string? text, string? value) { - return value.StartsWith(text, StringComparison.CurrentCulture); + if (value is not null && text is not null) + return value.StartsWith(text, StringComparison.CurrentCulture); + return false; } /// @@ -2599,9 +2609,11 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool StartsWithOrdinal(string text, string value) + public static bool StartsWithOrdinal(string? text, string? value) { - return value.StartsWith(text, StringComparison.OrdinalIgnoreCase); + if (value is not null && text is not null) + return value.StartsWith(text, StringComparison.OrdinalIgnoreCase); + return false; } /// @@ -2610,9 +2622,11 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool StartsWithOrdinalCaseSensitive(string text, string value) + public static bool StartsWithOrdinalCaseSensitive(string? text, string? value) { - return value.StartsWith(text, StringComparison.Ordinal); + if (value is not null && text is not null) + return value.StartsWith(text, StringComparison.Ordinal); + return false; } /// @@ -2622,7 +2636,7 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool Contains(string text, string value) + public static bool Contains(string? text, string? value) { return Contains(value, text, StringComparison.CurrentCultureIgnoreCase); } @@ -2633,7 +2647,7 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool ContainsCaseSensitive(string text, string value) + public static bool ContainsCaseSensitive(string? text, string? value) { return Contains(value, text, StringComparison.CurrentCulture); } @@ -2644,7 +2658,7 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool ContainsOrdinal(string text, string value) + public static bool ContainsOrdinal(string? text, string? value) { return Contains(value, text, StringComparison.OrdinalIgnoreCase); } @@ -2655,7 +2669,7 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool ContainsOrdinalCaseSensitive(string text, string value) + public static bool ContainsOrdinalCaseSensitive(string? text, string? value) { return Contains(value, text, StringComparison.Ordinal); } @@ -2666,9 +2680,9 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool Equals(string text, string value) + public static bool Equals(string? text, string? value) { - return value.Equals(text, StringComparison.CurrentCultureIgnoreCase); + return string.Equals(value, text, StringComparison.CurrentCultureIgnoreCase); } /// @@ -2677,9 +2691,9 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool EqualsCaseSensitive(string text, string value) + public static bool EqualsCaseSensitive(string? text, string? value) { - return value.Equals(text, StringComparison.CurrentCulture); + return string.Equals(value, text, StringComparison.CurrentCulture); } /// @@ -2688,9 +2702,9 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool EqualsOrdinal(string text, string value) + public static bool EqualsOrdinal(string? text, string? value) { - return value.Equals(text, StringComparison.OrdinalIgnoreCase); + return string.Equals(value, text, StringComparison.OrdinalIgnoreCase); } /// @@ -2699,9 +2713,9 @@ namespace Avalonia.Controls /// The AutoCompleteBox prefix text. /// The item's string value. /// Returns true if the condition is met. - public static bool EqualsOrdinalCaseSensitive(string text, string value) + public static bool EqualsOrdinalCaseSensitive(string? text, string? value) { - return value.Equals(text, StringComparison.Ordinal); + return string.Equals(value, text, StringComparison.Ordinal); } } @@ -2715,7 +2729,7 @@ namespace Avalonia.Controls /// /// Gets or sets the string value binding used by the control. /// - private IBinding _binding; + private IBinding? _binding; #region public T Value @@ -2739,13 +2753,14 @@ namespace Avalonia.Controls /// /// Gets or sets the value binding. /// - public IBinding ValueBinding + public IBinding? ValueBinding { get { return _binding; } set { _binding = value; - AvaloniaObjectExtensions.Bind(this, ValueProperty, value); + if (value is not null) + AvaloniaObjectExtensions.Bind(this, ValueProperty, value); } } @@ -2760,7 +2775,7 @@ namespace Avalonia.Controls /// setting the initial binding to the provided parameter. /// /// The initial string value binding. - public BindingEvaluator(IBinding binding) + public BindingEvaluator(IBinding? binding) : this() { ValueBinding = binding; @@ -2802,7 +2817,7 @@ namespace Avalonia.Controls /// The object to use as the data context. /// Returns the evaluated T value of the bound dependency /// property. - public T GetDynamicValue(object o) + public T GetDynamicValue(object? o) { DataContext = o; return Value; diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index e2c6a714aa..543a513d57 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -18,4 +18,5 @@ + 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 ce41b90bb0..a7a4759182 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -42,21 +42,21 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = - AvaloniaProperty.RegisterDirect(nameof(Command), + public static readonly DirectProperty CommandProperty = + AvaloniaProperty.RegisterDirect(nameof(Command), button => button.Command, (button, command) => button.Command = command, enableDataValidation: true); /// /// Defines the property. /// - public static readonly StyledProperty HotKeyProperty = + public static readonly StyledProperty HotKeyProperty = HotKeyManager.HotKeyProperty.AddOwner