diff --git a/Avalonia.sln b/Avalonia.sln index b21df07628..f33b782479 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -244,13 +244,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo", "samples\SafeAreaDemo\SafeAreaDemo.csproj", "{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Android", "samples\SafeAreaDemo.Android\SafeAreaDemo.Android.csproj", "{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Desktop", "samples\SafeAreaDemo.Desktop\SafeAreaDemo.Desktop.csproj", "{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -595,6 +603,26 @@ Global {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.Build.0 = Release|Any CPU + {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.Build.0 = Release|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Build.0 = Release|Any CPU + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.Build.0 = Release|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -661,10 +689,14 @@ Global {75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098} {C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {6B60A970-D5D2-49C2-8BAB-F9C7973B74B6} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs index ae7e43f511..81d379ce05 100644 --- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs @@ -114,7 +114,7 @@ namespace ControlCatalog.Pages foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox")) { - box.Items = States; + box.ItemsSource = States; } var converter = new FuncMultiValueConverter(parts => @@ -132,7 +132,7 @@ namespace ControlCatalog.Pages asyncBox.AsyncPopulator = PopulateAsync; var customAutocompleteBox = this.Get("CustomAutocompleteBox"); - customAutocompleteBox.Items = Sentences.SelectMany(x => x); + customAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x); customAutocompleteBox.TextFilter = LastWordContains; customAutocompleteBox.TextSelector = AppendWord; } diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml index cb294442d2..56f8547740 100644 --- a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml +++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml @@ -67,7 +67,7 @@ - + diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index c39e9f0a81..fb097da71b 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -94,7 +94,7 @@ + ItemsSource="{Binding DataGrid3Source}"> diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs index b0c3e3a553..617b0db5ed 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs @@ -36,7 +36,7 @@ namespace ControlCatalog.Pages collectionView1.SortDescriptions.Add(dataGridSortDescription); } }; - dg1.Items = collectionView1; + dg1.ItemsSource = collectionView1; var dg2 = this.Get("dataGridGrouping"); dg2.IsReadOnly = true; @@ -44,7 +44,7 @@ namespace ControlCatalog.Pages var collectionView2 = new DataGridCollectionView(Countries.All); collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region")); - dg2.Items = collectionView2; + dg2.ItemsSource = collectionView2; var dg3 = this.Get("dataGridEdit"); dg3.IsReadOnly = false; diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 90c717e7ed..980b210aaa 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -45,7 +45,7 @@ - + Desktop Documents @@ -54,7 +54,7 @@ Videos Music - + - diff --git a/samples/SafeAreaDemo.Android/Icon.png b/samples/SafeAreaDemo.Android/Icon.png new file mode 100644 index 0000000000..41a2a618fb Binary files /dev/null and b/samples/SafeAreaDemo.Android/Icon.png differ diff --git a/samples/SafeAreaDemo.Android/MainActivity.cs b/samples/SafeAreaDemo.Android/MainActivity.cs new file mode 100644 index 0000000000..b0f0a6e419 --- /dev/null +++ b/samples/SafeAreaDemo.Android/MainActivity.cs @@ -0,0 +1,11 @@ +using Android.App; +using Android.Content.PM; +using Avalonia.Android; + +namespace SafeAreaDemo.Android +{ + [Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] + public class MainActivity : AvaloniaMainActivity + { + } +} diff --git a/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml b/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml new file mode 100644 index 0000000000..b6a5777e03 --- /dev/null +++ b/samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml b/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml new file mode 100644 index 0000000000..2e920b4b3b --- /dev/null +++ b/samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/samples/SafeAreaDemo.Android/Resources/values/colors.xml b/samples/SafeAreaDemo.Android/Resources/values/colors.xml new file mode 100644 index 0000000000..59279d5d32 --- /dev/null +++ b/samples/SafeAreaDemo.Android/Resources/values/colors.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + diff --git a/samples/SafeAreaDemo.Android/Resources/values/styles.xml b/samples/SafeAreaDemo.Android/Resources/values/styles.xml new file mode 100644 index 0000000000..2759d2904a --- /dev/null +++ b/samples/SafeAreaDemo.Android/Resources/values/styles.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj new file mode 100644 index 0000000000..f5d2af79d0 --- /dev/null +++ b/samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj @@ -0,0 +1,24 @@ + + + Exe + net7.0-android + 21 + enable + com.avalonia.safeareademo + 1 + 1.0 + apk + False + + + + + Resources\drawable\Icon.png + + + + + + + + diff --git a/samples/SafeAreaDemo.Android/SplashActivity.cs b/samples/SafeAreaDemo.Android/SplashActivity.cs new file mode 100644 index 0000000000..621ad1c675 --- /dev/null +++ b/samples/SafeAreaDemo.Android/SplashActivity.cs @@ -0,0 +1,30 @@ +using Android.App; +using Android.Content; +using Android.OS; +using Avalonia; +using Avalonia.Android; +using Application = Android.App.Application; + +namespace SafeAreaDemo.Android +{ + [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] + public class SplashActivity : AvaloniaSplashActivity + { + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder); + } + + protected override void OnCreate(Bundle? savedInstanceState) + { + base.OnCreate(savedInstanceState); + } + + protected override void OnResume() + { + base.OnResume(); + + StartActivity(new Intent(Application.Context, typeof(MainActivity))); + } + } +} diff --git a/samples/SafeAreaDemo.Desktop/Program.cs b/samples/SafeAreaDemo.Desktop/Program.cs new file mode 100644 index 0000000000..b07682e8c8 --- /dev/null +++ b/samples/SafeAreaDemo.Desktop/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace SafeAreaDemo.Desktop +{ + internal class Program + { + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .LogToTrace(); + } +} diff --git a/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj new file mode 100644 index 0000000000..619209892d --- /dev/null +++ b/samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj @@ -0,0 +1,24 @@ + + + WinExe + + net7.0 + enable + true + + + + app.manifest + + + + + + + + + + + + diff --git a/samples/SafeAreaDemo.Desktop/app.manifest b/samples/SafeAreaDemo.Desktop/app.manifest new file mode 100644 index 0000000000..f0a4b00175 --- /dev/null +++ b/samples/SafeAreaDemo.Desktop/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/samples/SafeAreaDemo.iOS/AppDelegate.cs b/samples/SafeAreaDemo.iOS/AppDelegate.cs new file mode 100644 index 0000000000..6990435d78 --- /dev/null +++ b/samples/SafeAreaDemo.iOS/AppDelegate.cs @@ -0,0 +1,17 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.iOS; +using Avalonia.Media; +using Foundation; +using UIKit; + +namespace SafeAreaDemo.iOS +{ + // The UIApplicationDelegate for the application. This class is responsible for launching the + // User Interface of the application, as well as listening (and optionally responding) to + // application events from iOS. + [Register("AppDelegate")] + public partial class AppDelegate : AvaloniaAppDelegate + { + } +} diff --git a/samples/SafeAreaDemo.iOS/Entitlements.plist b/samples/SafeAreaDemo.iOS/Entitlements.plist new file mode 100644 index 0000000000..0c67376eba --- /dev/null +++ b/samples/SafeAreaDemo.iOS/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/SafeAreaDemo.iOS/Info.plist b/samples/SafeAreaDemo.iOS/Info.plist new file mode 100644 index 0000000000..ec04bd5a87 --- /dev/null +++ b/samples/SafeAreaDemo.iOS/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDisplayName + SafeAreaDemo + CFBundleIdentifier + companyName.SafeAreaDemo + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + MinimumOSVersion + 10.0 + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/samples/SafeAreaDemo.iOS/Main.cs b/samples/SafeAreaDemo.iOS/Main.cs new file mode 100644 index 0000000000..1c76dc6bc4 --- /dev/null +++ b/samples/SafeAreaDemo.iOS/Main.cs @@ -0,0 +1,15 @@ +using UIKit; + +namespace SafeAreaDemo.iOS +{ + public class Application + { + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } + } +} \ No newline at end of file diff --git a/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib b/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib new file mode 100644 index 0000000000..c6dd636c46 --- /dev/null +++ b/samples/SafeAreaDemo.iOS/Resources/LaunchScreen.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj b/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj new file mode 100644 index 0000000000..71365fe07d --- /dev/null +++ b/samples/SafeAreaDemo.iOS/SafeAreaDemo.iOS.csproj @@ -0,0 +1,18 @@ + + + Exe + net7.0-ios + 10.0 + manual + enable + iossimulator-x64 + + + + + + + + + + diff --git a/samples/SafeAreaDemo/App.xaml b/samples/SafeAreaDemo/App.xaml new file mode 100644 index 0000000000..f5ffbdb32a --- /dev/null +++ b/samples/SafeAreaDemo/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SafeAreaDemo/App.xaml.cs b/samples/SafeAreaDemo/App.xaml.cs new file mode 100644 index 0000000000..e23cb0e04a --- /dev/null +++ b/samples/SafeAreaDemo/App.xaml.cs @@ -0,0 +1,36 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using SafeAreaDemo.ViewModels; +using SafeAreaDemo.Views; + +namespace SafeAreaDemo +{ + public partial class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow + { + DataContext = new MainViewModel() + }; + } + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) + { + singleViewPlatform.MainView = new MainView + { + DataContext = new MainViewModel() + }; + } + + base.OnFrameworkInitializationCompleted(); + } + } +} \ No newline at end of file diff --git a/samples/SafeAreaDemo/Assets/avalonia-logo.ico b/samples/SafeAreaDemo/Assets/avalonia-logo.ico new file mode 100644 index 0000000000..da8d49ff9b Binary files /dev/null and b/samples/SafeAreaDemo/Assets/avalonia-logo.ico differ diff --git a/samples/SafeAreaDemo/SafeAreaDemo.csproj b/samples/SafeAreaDemo/SafeAreaDemo.csproj new file mode 100644 index 0000000000..20bc5ec8fe --- /dev/null +++ b/samples/SafeAreaDemo/SafeAreaDemo.csproj @@ -0,0 +1,27 @@ + + + net7.0 + enable + latest + true + + + + + + %(Filename) + + + Designer + + + + + + + + + + + + diff --git a/samples/SafeAreaDemo/ViewLocator.cs b/samples/SafeAreaDemo/ViewLocator.cs new file mode 100644 index 0000000000..4f71fdbe9c --- /dev/null +++ b/samples/SafeAreaDemo/ViewLocator.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using MiniMvvm; + +namespace SafeAreaDemo +{ + public class ViewLocator : IDataTemplate + { + public Control? Build(object? data) + { + if (data is null) + return null; + + var name = data.GetType().FullName!.Replace("ViewModel", "View"); + var type = Type.GetType(name); + + if (type != null) + { + return (Control)Activator.CreateInstance(type)!; + } + + return new TextBlock { Text = name }; + } + + public bool Match(object? data) + { + return data is ViewModelBase; + } + } +} diff --git a/samples/SafeAreaDemo/ViewModels/MainViewModel.cs b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs new file mode 100644 index 0000000000..fe58567171 --- /dev/null +++ b/samples/SafeAreaDemo/ViewModels/MainViewModel.cs @@ -0,0 +1,112 @@ +using Avalonia; +using Avalonia.Controls.Platform; +using MiniMvvm; + +namespace SafeAreaDemo.ViewModels +{ + public class MainViewModel : ViewModelBase + { + private bool _useSafeArea = true; + private bool _fullscreen; + private IInsetsManager? _insetsManager; + private bool _hideSystemBars; + + public Thickness SafeAreaPadding + { + get + { + return _insetsManager?.SafeAreaPadding ?? default; + } + } + + public Thickness ViewPadding + { + get + { + return _useSafeArea ? SafeAreaPadding : default; + } + } + + public bool UseSafeArea + { + get => _useSafeArea; + set + { + _useSafeArea = value; + + this.RaisePropertyChanged(); + + RaiseSafeAreaChanged(); + } + } + + public bool Fullscreen + { + get => _fullscreen; + set + { + _fullscreen = value; + + if (_insetsManager != null) + { + _insetsManager.DisplayEdgeToEdge = value; + } + + this.RaisePropertyChanged(); + + RaiseSafeAreaChanged(); + } + } + + public bool HideSystemBars + { + get => _hideSystemBars; + set + { + _hideSystemBars = value; + + if (_insetsManager != null) + { + _insetsManager.IsSystemBarVisible = !value; + } + + this.RaisePropertyChanged(); + + RaiseSafeAreaChanged(); + } + } + + internal IInsetsManager? InsetsManager + { + get => _insetsManager; + set + { + if (_insetsManager != null) + { + _insetsManager.SafeAreaChanged -= InsetsManager_SafeAreaChanged; + } + + _insetsManager = value; + + if (_insetsManager != null) + { + _insetsManager.SafeAreaChanged += InsetsManager_SafeAreaChanged; + + _insetsManager.DisplayEdgeToEdge = _fullscreen; + _insetsManager.IsSystemBarVisible = !_hideSystemBars; + } + } + } + + private void InsetsManager_SafeAreaChanged(object? sender, SafeAreaChangedArgs e) + { + RaiseSafeAreaChanged(); + } + + private void RaiseSafeAreaChanged() + { + this.RaisePropertyChanged(nameof(SafeAreaPadding)); + this.RaisePropertyChanged(nameof(ViewPadding)); + } + } +} diff --git a/samples/SafeAreaDemo/Views/MainView.xaml b/samples/SafeAreaDemo/Views/MainView.xaml new file mode 100644 index 0000000000..a8f7c2e735 --- /dev/null +++ b/samples/SafeAreaDemo/Views/MainView.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + Fullscreen + Use Safe Area + Hide System Bars + + + + + + + diff --git a/samples/SafeAreaDemo/Views/MainView.xaml.cs b/samples/SafeAreaDemo/Views/MainView.xaml.cs new file mode 100644 index 0000000000..2b651225e7 --- /dev/null +++ b/samples/SafeAreaDemo/Views/MainView.xaml.cs @@ -0,0 +1,25 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using SafeAreaDemo.ViewModels; + +namespace SafeAreaDemo.Views +{ + public partial class MainView : UserControl + { + public MainView() + { + AvaloniaXamlLoader.Load(this); + } + + protected override void OnLoaded() + { + base.OnLoaded(); + + var insetsManager = TopLevel.GetTopLevel(this)?.InsetsManager; + if (insetsManager != null && DataContext is MainViewModel viewModel) + { + viewModel.InsetsManager = insetsManager; + } + } + } +} diff --git a/samples/SafeAreaDemo/Views/MainWindow.xaml b/samples/SafeAreaDemo/Views/MainWindow.xaml new file mode 100644 index 0000000000..ccd3028bb9 --- /dev/null +++ b/samples/SafeAreaDemo/Views/MainWindow.xaml @@ -0,0 +1,12 @@ + + + diff --git a/samples/SafeAreaDemo/Views/MainWindow.xaml.cs b/samples/SafeAreaDemo/Views/MainWindow.xaml.cs new file mode 100644 index 0000000000..de8f2b05ca --- /dev/null +++ b/samples/SafeAreaDemo/Views/MainWindow.xaml.cs @@ -0,0 +1,13 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace SafeAreaDemo.Views +{ + public partial class MainWindow : Window + { + public MainWindow() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/src/Avalonia.Base/Media/Effects/DropShadowEffect.cs b/src/Avalonia.Base/Media/Effects/DropShadowEffect.cs index 67be74fe49..ea931c0a8c 100644 --- a/src/Avalonia.Base/Media/Effects/DropShadowEffect.cs +++ b/src/Avalonia.Base/Media/Effects/DropShadowEffect.cs @@ -18,7 +18,7 @@ public abstract class DropShadowEffectBase : Effect } public static readonly StyledProperty ColorProperty = AvaloniaProperty.Register( - nameof(Color)); + nameof(Color), Colors.Black); public Color Color { @@ -97,8 +97,8 @@ public class DropShadowDirectionEffect : DropShadowEffectBase, IDirectionDropSha set => SetValue(DirectionProperty, value); } - public double OffsetX => Math.Cos(Direction) * ShadowDepth; - public double OffsetY => Math.Sin(Direction) * ShadowDepth; + public double OffsetX => Math.Cos(Direction * Math.PI / 180) * ShadowDepth; + public double OffsetY => Math.Sin(Direction * Math.PI / 180) * ShadowDepth; public IImmutableEffect ToImmutable() => new ImmutableDropShadowDirectionEffect(OffsetX, OffsetY, BlurRadius, Color, Opacity); } \ No newline at end of file diff --git a/src/Avalonia.Base/Media/Effects/IDropShadowEffect.cs b/src/Avalonia.Base/Media/Effects/IDropShadowEffect.cs index 30d787198c..bb97410d7f 100644 --- a/src/Avalonia.Base/Media/Effects/IDropShadowEffect.cs +++ b/src/Avalonia.Base/Media/Effects/IDropShadowEffect.cs @@ -67,8 +67,8 @@ public class ImmutableDropShadowDirectionEffect : IDirectionDropShadowEffect, II Opacity = opacity; } - public double OffsetX => Math.Cos(Direction) * ShadowDepth; - public double OffsetY => Math.Sin(Direction) * ShadowDepth; + public double OffsetX => Math.Cos(Direction * Math.PI / 180) * ShadowDepth; + public double OffsetY => Math.Sin(Direction * Math.PI / 180) * ShadowDepth; public double Direction { get; } public double ShadowDepth { get; } public double BlurRadius { get; } diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 030776ba14..e56e32a5ff 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -152,8 +152,6 @@ namespace Avalonia.Controls private double _verticalOffset; private byte _verticalScrollChangesIgnored; - private IEnumerable _items; - public event EventHandler HorizontalScroll; public event EventHandler VerticalScroll; @@ -652,21 +650,18 @@ namespace Avalonia.Controls } /// - /// Identifies the ItemsSource dependency property. + /// Identifies the ItemsSource property. /// - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( - nameof(Items), - o => o.Items, - (o, v) => o.Items = v); + public static readonly StyledProperty ItemsSourceProperty = + AvaloniaProperty.Register(nameof(ItemsSource)); /// /// Gets or sets a collection that is used to generate the content of the control. /// - public IEnumerable Items + public IEnumerable ItemsSource { - get { return _items; } - set { SetAndRaise(ItemsProperty, ref _items, value); } + get => GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); } public static readonly StyledProperty AreRowDetailsFrozenProperty = @@ -713,7 +708,7 @@ namespace Avalonia.Controls HorizontalScrollBarVisibilityProperty, VerticalScrollBarVisibilityProperty); - ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e)); + ItemsSourceProperty.Changed.AddClassHandler((x, e) => x.OnItemsSourcePropertyChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e)); ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e)); FrozenColumnCountProperty.Changed.AddClassHandler((x, e) => x.OnFrozenColumnCountChanged(e)); @@ -816,10 +811,10 @@ namespace Avalonia.Controls } /// - /// ItemsProperty property changed handler. + /// ItemsSourceProperty property changed handler. /// /// The event arguments. - private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) + private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e) { if (!_areHandlersSuspended) { @@ -830,7 +825,7 @@ namespace Avalonia.Controls if (LoadingOrUnloadingRow) { - SetValueNoCallback(ItemsProperty, oldValue); + SetValueNoCallback(ItemsSourceProperty, oldValue); throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows(); } @@ -1855,7 +1850,7 @@ namespace Avalonia.Controls { get { - if (CurrentSlot == -1 || Items == null || RowGroupHeadersTable.Contains(CurrentSlot)) + if (CurrentSlot == -1 || ItemsSource == null || RowGroupHeadersTable.Contains(CurrentSlot)) { return null; } diff --git a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs index 110590fef2..61a1eb2bf0 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs @@ -25,7 +25,7 @@ namespace Avalonia.Controls /// //TODO Binding [AssignBinding] - [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] + [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))] public virtual IBinding Binding { get diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index ae52e5f970..ee9cc04420 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -122,9 +122,9 @@ namespace Avalonia.Controls // We need to use the raw ItemsSource as opposed to DataSource because DataSource // may be the ItemsSource wrapped in a collection view, in which case we wouldn't // be able to take T to be the type if we're given IEnumerable - if (_dataType == null && _owner.Items != null) + if (_dataType == null && _owner.ItemsSource != null) { - _dataType = _owner.Items.GetItemType(); + _dataType = _owner.ItemsSource.GetItemType(); } return _dataType; } diff --git a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs index 00318e2dd8..0bfb4b6913 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs @@ -24,7 +24,7 @@ namespace Avalonia.Controls (o, v) => o.CellTemplate = v); [Content] - [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] + [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))] public IDataTemplate CellTemplate { get { return _cellTemplate; } @@ -51,7 +51,7 @@ namespace Avalonia.Controls /// /// If this property is the column is read-only. /// - [InheritDataTypeFromItems(nameof(DataGrid.Items), AncestorType = typeof(DataGrid))] + [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))] public IDataTemplate CellEditingTemplate { get => _cellEditingCellTemplate; diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs index 499904deac..5fe9c6fa05 100644 --- a/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs +++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs @@ -36,13 +36,13 @@ namespace Avalonia.Controls ItemsControl.ItemTemplateProperty.AddOwner(); /// - /// Defines the property. + /// Defines the property. /// - public static readonly DirectProperty ItemsProperty = + public static readonly DirectProperty ItemsSourceProperty = AvaloniaProperty.RegisterDirect( - nameof(Items), - o => o.Items, - (o, v) => o.Items = v); + nameof(ItemsSource), + o => o.ItemsSource, + (o, v) => o.ItemsSource = v); /// /// Defines the property. @@ -65,7 +65,7 @@ namespace Avalonia.Controls private readonly ViewManager _viewManager; private readonly ViewportManager _viewportManager; private readonly TargetWeakEventSubscriber _layoutWeakSubscriber; - private IEnumerable? _items; + private IEnumerable? _itemsSource; private RepeaterLayoutContext? _layoutContext; private EventHandler? _childIndexChanged; private bool _isLayoutInProgress; @@ -116,16 +116,16 @@ namespace Avalonia.Controls /// /// Gets or sets an object source used to generate the content of the ItemsRepeater. /// - public IEnumerable? Items + public IEnumerable? ItemsSource { - get => _items; - set => SetAndRaise(ItemsProperty, ref _items, value); + get => _itemsSource; + set => SetAndRaise(ItemsSourceProperty, ref _itemsSource, value); } /// /// Gets or sets the template used to display each item. /// - [InheritDataTypeFromItems(nameof(Items))] + [InheritDataTypeFromItems(nameof(ItemsSource))] public IDataTemplate? ItemTemplate { get => GetValue(ItemTemplateProperty); @@ -415,7 +415,7 @@ namespace Avalonia.Controls /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (change.Property == ItemsProperty) + if (change.Property == ItemsSourceProperty) { var (oldEnumerable, newEnumerable) = change.GetOldAndNewValue(); diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs index b38151854b..47a0c531ba 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs @@ -143,12 +143,12 @@ namespace Avalonia.Controls nameof(TextSelector)); /// - /// Identifies the property. + /// Identifies the property. /// - /// The identifier for the property. - public static readonly StyledProperty ItemsProperty = + /// The identifier for the property. + public static readonly StyledProperty ItemsSourceProperty = AvaloniaProperty.Register( - nameof(Items)); + nameof(ItemsSource)); /// /// Identifies the property. @@ -311,10 +311,10 @@ namespace Avalonia.Controls /// /// Gets the text that is used to filter items in the - /// item collection. + /// item collection. /// /// The text that is used to filter items in the - /// item collection. + /// item collection. /// /// The SearchText value is typically the same as the /// Text property, but is set after the TextChanged event occurs @@ -339,7 +339,7 @@ namespace Avalonia.Controls /// /// Gets or sets how the text in the text box is used to filter items - /// specified by the + /// specified by the /// property for display in the drop-down. /// /// One of the @@ -366,11 +366,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that uses user-entered text to filter - /// the items specified by the + /// the items specified by the /// property for display in the drop-down. /// /// The custom method that uses the user-entered text to filter - /// the items specified by the + /// the items specified by the /// property. The default is null. /// /// The filter mode is automatically set to Custom if you set the @@ -384,11 +384,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that uses the user-entered text to - /// filter items specified by the + /// filter items specified by the /// property in a text-based way for display in the drop-down. /// /// The custom method that uses the user-entered text to filter - /// items specified by the + /// items specified by the /// property in a text-based way for display in the drop-down. /// /// The search mode is automatically set to Custom if you set the @@ -402,11 +402,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that combines the user-entered - /// text and one of the items specified by the . + /// text and one of the items specified by the . /// /// /// The custom method that combines the user-entered - /// text and one of the items specified by the . + /// text and one of the items specified by the . /// public AutoCompleteSelector? ItemSelector { @@ -417,11 +417,11 @@ namespace Avalonia.Controls /// /// Gets or sets the custom method that combines the user-entered /// text and one of the items specified by the - /// in a text-based way. + /// in a text-based way. /// /// /// The custom method that combines the user-entered - /// text and one of the items specified by the + /// text and one of the items specified by the /// in a text-based way. /// public AutoCompleteSelector? TextSelector @@ -442,10 +442,10 @@ 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? ItemsSource { - get => GetValue(ItemsProperty); - set => SetValue(ItemsProperty, value); + get => GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); } } } diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs index 72a23144cf..e10cc1d100 100644 --- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs @@ -419,9 +419,9 @@ namespace Avalonia.Controls /// ItemsSourceProperty property changed handler. /// /// Event arguments. - private void OnItemsPropertyChanged(AvaloniaPropertyChangedEventArgs e) + private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e) { - OnItemsChanged((IEnumerable?)e.NewValue); + OnItemsSourceChanged((IEnumerable?)e.NewValue); } private void OnItemTemplatePropertyChanged(AvaloniaPropertyChangedEventArgs e) @@ -461,7 +461,7 @@ namespace Avalonia.Controls SearchTextProperty.Changed.AddClassHandler((x,e) => x.OnSearchTextPropertyChanged(e)); FilterModeProperty.Changed.AddClassHandler((x,e) => x.OnFilterModePropertyChanged(e)); ItemFilterProperty.Changed.AddClassHandler((x,e) => x.OnItemFilterPropertyChanged(e)); - ItemsProperty.Changed.AddClassHandler((x,e) => x.OnItemsPropertyChanged(e)); + ItemsSourceProperty.Changed.AddClassHandler((x,e) => x.OnItemsSourcePropertyChanged(e)); ItemTemplateProperty.Changed.AddClassHandler((x,e) => x.OnItemTemplatePropertyChanged(e)); IsEnabledProperty.Changed.AddClassHandler((x,e) => x.OnControlIsEnabledChanged(e)); } @@ -559,7 +559,7 @@ namespace Avalonia.Controls _adapter.Commit -= OnAdapterSelectionComplete; _adapter.Cancel -= OnAdapterSelectionCanceled; _adapter.Cancel -= OnAdapterSelectionComplete; - _adapter.Items = null; + _adapter.ItemsSource = null; } _adapter = value; @@ -570,7 +570,7 @@ namespace Avalonia.Controls _adapter.Commit += OnAdapterSelectionComplete; _adapter.Cancel += OnAdapterSelectionCanceled; _adapter.Cancel += OnAdapterSelectionComplete; - _adapter.Items = _view; + _adapter.ItemsSource = _view; } } } @@ -1128,7 +1128,7 @@ namespace Avalonia.Controls { if (!cancellationToken.IsCancellationRequested) { - SetCurrentValue(ItemsProperty, resultList); + SetCurrentValue(ItemsSourceProperty, resultList); PopulateComplete(); } }); @@ -1475,7 +1475,7 @@ namespace Avalonia.Controls /// adapter's ItemsSource to the view if appropriate. /// /// The new enumerable reference. - private void OnItemsChanged(IEnumerable? newValue) + private void OnItemsSourceChanged(IEnumerable? newValue) { // Remove handler for oldValue.CollectionChanged (if present) _collectionChangeSubscription?.Dispose(); @@ -1492,9 +1492,9 @@ namespace Avalonia.Controls // Clear and set the view on the selection adapter ClearView(); - if (SelectionAdapter != null && SelectionAdapter.Items != _view) + if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view) { - SelectionAdapter.Items = _view; + SelectionAdapter.ItemsSource = _view; } if (IsDropDownOpen) { @@ -1545,9 +1545,9 @@ namespace Avalonia.Controls { // Significant changes to the underlying data. ClearView(); - if (Items != null) + if (ItemsSource != null) { - _items = new List(Items.Cast()); + _items = new List(ItemsSource.Cast()); } } @@ -1582,9 +1582,9 @@ namespace Avalonia.Controls PopulatedEventArgs populated = new PopulatedEventArgs(new ReadOnlyCollection(_view!)); OnPopulated(populated); - if (SelectionAdapter != null && SelectionAdapter.Items != _view) + if (SelectionAdapter != null && SelectionAdapter.ItemsSource != _view) { - SelectionAdapter.Items = _view; + SelectionAdapter.ItemsSource = _view; } bool isDropDownOpen = _userCalledPopulate && (_view!.Count > 0); diff --git a/src/Avalonia.Controls/Flyouts/MenuFlyout.cs b/src/Avalonia.Controls/Flyouts/MenuFlyout.cs index 8e1bc214a7..a2ce93ee6d 100644 --- a/src/Avalonia.Controls/Flyouts/MenuFlyout.cs +++ b/src/Avalonia.Controls/Flyouts/MenuFlyout.cs @@ -1,6 +1,5 @@ using System.Collections; using System.ComponentModel; -using Avalonia.Collections; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Metadata; @@ -12,17 +11,15 @@ namespace Avalonia.Controls { public MenuFlyout() { - _items = new AvaloniaList(); + Items = new ItemCollection(); } /// - /// Defines the property + /// Defines the property /// - public static readonly DirectProperty ItemsProperty = - AvaloniaProperty.RegisterDirect( - nameof(Items), - x => x.Items, - (x, v) => x.Items = v); + public static readonly StyledProperty ItemsSourceProperty = + AvaloniaProperty.Register( + nameof(ItemsSource)); /// /// Defines the property @@ -45,14 +42,16 @@ namespace Avalonia.Controls public Classes FlyoutPresenterClasses => _classes ??= new Classes(); + [Content] + public ItemCollection Items { get; } + /// /// Gets or sets the items of the MenuFlyout /// - [Content] - public IEnumerable? Items + public IEnumerable? ItemsSource { - get => _items; - set => SetAndRaise(ItemsProperty, ref _items, value); + get => GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); } /// @@ -83,14 +82,13 @@ namespace Avalonia.Controls } private Classes? _classes; - private IEnumerable? _items; private IDataTemplate? _itemTemplate; protected override Control CreatePresenter() { return new MenuFlyoutPresenter { - [!ItemsControl.ItemsSourceProperty] = this[!ItemsProperty], + ItemsSource = Items, [!ItemsControl.ItemTemplateProperty] = this[!ItemTemplateProperty], [!ItemsControl.ItemContainerThemeProperty] = this[!ItemContainerThemeProperty], }; @@ -113,5 +111,13 @@ namespace Avalonia.Controls base.OnOpening(args); } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ItemsSourceProperty) + Items.SetItemsSource(change.GetNewValue()); + } } } diff --git a/src/Avalonia.Controls/ItemsSourceView.cs b/src/Avalonia.Controls/ItemsSourceView.cs index 614b70d0ba..977d712371 100644 --- a/src/Avalonia.Controls/ItemsSourceView.cs +++ b/src/Avalonia.Controls/ItemsSourceView.cs @@ -262,7 +262,7 @@ namespace Avalonia.Controls _source = source switch { - ItemsSourceView => throw new ArgumentException("Cannot wrap an existing ItemsSourceView.", nameof(source)), + ItemsSourceView isv => isv.Source, IList list => list, INotifyCollectionChanged => throw new ArgumentException( "Collection implements INotifyCollectionChanged but not IList.", diff --git a/src/Avalonia.Controls/RadioButton.cs b/src/Avalonia.Controls/RadioButton.cs index d4528fdb1c..f48ae72312 100644 --- a/src/Avalonia.Controls/RadioButton.cs +++ b/src/Avalonia.Controls/RadioButton.cs @@ -86,7 +86,7 @@ namespace Avalonia.Controls continue; } if (current != radioButton && current.IsChecked.GetValueOrDefault()) - current.IsChecked = false; + current.SetCurrentValue(IsCheckedProperty, false); i++; } if (group.Count == 0) @@ -193,7 +193,7 @@ namespace Avalonia.Controls foreach (var sibling in siblings) { if (sibling.IsChecked.GetValueOrDefault()) - sibling.IsChecked = false; + sibling.SetCurrentValue(IsCheckedProperty, false); } } } diff --git a/src/Avalonia.Controls/Utils/ISelectionAdapter.cs b/src/Avalonia.Controls/Utils/ISelectionAdapter.cs index 3ede518ffa..55739500a1 100644 --- a/src/Avalonia.Controls/Utils/ISelectionAdapter.cs +++ b/src/Avalonia.Controls/Utils/ISelectionAdapter.cs @@ -36,7 +36,7 @@ namespace Avalonia.Controls.Utils /// /// The collection that is used to generate content for the /// selection adapter. - IEnumerable? Items { get; set; } + IEnumerable? ItemsSource { get; set; } /// /// Occurs when a selected item is not cancelled and is committed as the diff --git a/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs b/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs index 5f528e2c72..ca8827eb45 100644 --- a/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs +++ b/src/Avalonia.Controls/Utils/SelectingItemsControlSelectionAdapter.cs @@ -140,7 +140,7 @@ namespace Avalonia.Controls.Utils /// /// The collection used to generate content for the selection /// adapter. - public IEnumerable? Items + public IEnumerable? ItemsSource { get { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml index c2e63da31c..63b002d110 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ControlDetailsView.xaml @@ -51,7 +51,7 @@ @@ -26,12 +26,6 @@ - - - - - - diff --git a/src/Avalonia.Themes.Simple/Controls/TextBox.xaml b/src/Avalonia.Themes.Simple/Controls/TextBox.xaml index 0c7095f2f5..20816c4c0b 100644 --- a/src/Avalonia.Themes.Simple/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Simple/Controls/TextBox.xaml @@ -13,15 +13,6 @@ IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}" /> - - - - - - diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index e5373ce42f..38af1b6d7b 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -24,6 +24,7 @@ using Avalonia.X11.Glx; using Avalonia.X11.NativeDialogs; using static Avalonia.X11.XLib; using Avalonia.Input.Platform; +using System.Runtime.InteropServices; // ReSharper disable IdentifierTypo // ReSharper disable StringLiteralTypo @@ -629,6 +630,8 @@ namespace Avalonia.X11 ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN); ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); + SendNetWMMessage(_x11.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, _x11.LastActivityTimestamp, + IntPtr.Zero); } } } @@ -735,6 +738,10 @@ namespace Avalonia.X11 { if (_inputRoot is null) return; + + if (_disabled && args is RawPointerEventArgs pargs && pargs.Type == RawPointerEventType.Move) + return; + Input?.Invoke(args); if (!args.Handled && args is RawKeyEventArgsWithText text && !string.IsNullOrEmpty(text.Text)) Input?.Invoke(new RawTextInputEventArgs(_keyboard, args.Timestamp, _inputRoot, text.Text)); @@ -1201,6 +1208,32 @@ namespace Avalonia.X11 public void SetEnabled(bool enable) { _disabled = !enable; + + UpdateWMHints(); + } + + private void UpdateWMHints() + { + var wmHintsPtr = XGetWMHints(_x11.Display, _handle); + + XWMHints hints = default; + + if (wmHintsPtr != IntPtr.Zero) + { + hints = Marshal.PtrToStructure(wmHintsPtr); + } + + var flags = hints.flags.ToInt64(); + flags |= (long)XWMHintsFlags.InputHint; + hints.flags = (IntPtr)flags; + hints.input = !_disabled ? 1 : 0; + + XSetWMHints(_x11.Display, _handle, ref hints); + + if (wmHintsPtr != IntPtr.Zero) + { + XFree(wmHintsPtr); + } } public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) @@ -1290,6 +1323,8 @@ namespace Avalonia.X11 public bool NeedsManagedDecorations => false; + public bool IsEnabled => !_disabled; + public class SurfacePlatformHandle : IPlatformNativeSurfaceHandle { private readonly X11Window _owner; diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index f66616f2aa..b066f9d827 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -231,7 +231,7 @@ namespace Avalonia.X11 return; } - if (_multitouch && ev.Emulated) + if (!client.IsEnabled || (_multitouch && ev.Emulated)) return; if (ev.Type == XiEventType.XI_Motion) @@ -370,6 +370,7 @@ namespace Avalonia.X11 internal interface IXI2Client { + bool IsEnabled { get; } IInputRoot InputRoot { get; } void ScheduleXI2Input(RawInputEventArgs args); IMouseDevice MouseDevice { get; } diff --git a/src/Avalonia.X11/XLib.cs b/src/Avalonia.X11/XLib.cs index 7a43cd378b..641adde7e2 100644 --- a/src/Avalonia.X11/XLib.cs +++ b/src/Avalonia.X11/XLib.cs @@ -375,6 +375,9 @@ namespace Avalonia.X11 [DllImport(libX11)] public static extern void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints); + [DllImport(libX11)] + public static extern IntPtr XGetWMHints(IntPtr display, IntPtr window); + [DllImport(libX11)] public static extern int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count); diff --git a/tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs b/tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs index 321676abc0..3c4777688d 100644 --- a/tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs +++ b/tests/Avalonia.Controls.ItemsRepeater.UnitTests/ItemsRepeaterTests.cs @@ -9,16 +9,16 @@ namespace Avalonia.Controls.UnitTests public void Can_Reassign_Items() { var target = new ItemsRepeater(); - target.Items = new ObservableCollection(); - target.Items = new ObservableCollection(); + target.ItemsSource = new ObservableCollection(); + target.ItemsSource = new ObservableCollection(); } [Fact] public void Can_Reassign_Items_To_Null() { var target = new ItemsRepeater(); - target.Items = new ObservableCollection(); - target.Items = null; + target.ItemsSource = new ObservableCollection(); + target.ItemsSource = null; } } } diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index 4582659763..acae583b5c 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -89,7 +89,7 @@ namespace Avalonia.Controls.UnitTests bool closeEvent = false; control.DropDownOpened += (s, e) => openEvent = true; control.DropDownClosed += (s, e) => closeEvent = true; - control.Items = CreateSimpleStringArray(); + control.ItemsSource = CreateSimpleStringArray(); textbox.Text = "a"; Dispatcher.UIThread.RunJobs(); @@ -258,7 +258,7 @@ namespace Avalonia.Controls.UnitTests control.FilterMode = AutoCompleteFilterMode.None; control.Populating += (s, e) => { - control.Items = new string[] { custom }; + control.ItemsSource = new string[] { custom }; Assert.Equal(search, e.Parameter); }; control.Populated += (s, e) => @@ -380,7 +380,7 @@ namespace Avalonia.Controls.UnitTests { RunTest((control, textbox) => { - object selectedItem = control.Items.Cast().First(); + object selectedItem = control.ItemsSource.Cast().First(); string input = "42"; control.TextSelector = (text, item) => text + item; @@ -397,7 +397,7 @@ namespace Avalonia.Controls.UnitTests { RunTest((control, textbox) => { - object selectedItem = control.Items.Cast().First(); + object selectedItem = control.ItemsSource.Cast().First(); string input = "42"; control.ItemSelector = (text, item) => text + item; @@ -1053,7 +1053,7 @@ namespace Avalonia.Controls.UnitTests using (UnitTestApplication.Start(Services)) { AutoCompleteBox control = CreateControl(); - control.Items = CreateSimpleStringArray(); + control.ItemsSource = CreateSimpleStringArray(); TextBox textBox = GetTextBox(control); var window = new Window {Content = control}; window.ApplyStyling(); diff --git a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs index a51abdb9af..9fd56dec4a 100644 --- a/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls.UnitTests Text = "1234", ContextFlyout = new MenuFlyout { - Items = new List + Items = { new MenuItem { Header = "Item 1" }, new MenuItem {Header = "Item 2" }, diff --git a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs index bbe46b2b21..bd59183e92 100644 --- a/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs +++ b/tests/Avalonia.Controls.UnitTests/MenuItemTests.cs @@ -232,7 +232,7 @@ namespace Avalonia.Controls.UnitTests return true; }); var target = new MenuItem(); - var flyout = new MenuFlyout { Items = new AvaloniaList { target } }; + var flyout = new MenuFlyout { Items = { target } }; var button = new Button { Flyout = flyout }; var window = new Window { Content = button }; window.ApplyStyling(); diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 0f0fd8f6c4..d71abe5a67 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -93,7 +93,7 @@ namespace Avalonia.Controls.UnitTests Text = "1234", ContextFlyout = new MenuFlyout { - Items = new List + Items = { new MenuItem { Header = "Item 1" }, new MenuItem {Header = "Item 2" }, diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 1320d34f39..55825561af 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -51,7 +51,7 @@ namespace Avalonia.LeakTests { Content = new DataGrid { - Items = _observableCollection + ItemsSource = _observableCollection } }; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs index 20a4543b72..2c20b7a0b7 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs @@ -424,7 +424,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml }; var list = window.FindControl("list"); - list.Items = collection; + list.ItemsSource = collection; window.Show();