diff --git a/Avalonia.sln b/Avalonia.sln index 3ee107707f..c2b8243688 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -121,10 +121,13 @@ 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\Base.props = build\Base.props build\Binding.props = build\Binding.props - build\BuildTargets.targets = build\BuildTargets.targets + build\CoreLibraries.props = build\CoreLibraries.props + build\EmbedXaml.props = build\EmbedXaml.props build\HarfBuzzSharp.props = build\HarfBuzzSharp.props + build\iOSWorkarounds.props = build\iOSWorkarounds.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props @@ -134,17 +137,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\NetCore.props = build\NetCore.props build\NetFX.props = build\NetFX.props build\ReactiveUI.props = build\ReactiveUI.props + build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props build\Rx.props = build\Rx.props build\SampleApp.props = build\SampleApp.props + build\SharedVersion.props = build\SharedVersion.props build\SharpDX.props = build\SharpDX.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceLink.props = build\SourceLink.props + build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Memory.props = build\System.Memory.props + build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props + build\ApiDiff.props = build\ApiDiff.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" ProjectSection(SolutionItems) = preProject build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets + build\BuildTargets.targets = build\BuildTargets.targets + build\LegacyProject.targets = build\LegacyProject.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}" diff --git a/NuGet.Config b/NuGet.Config index 2a1e0af74d..3abd236d42 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,7 +1,9 @@  + + diff --git a/build/ApiDiff.props b/build/ApiDiff.props new file mode 100644 index 0000000000..da82fbcc51 --- /dev/null +++ b/build/ApiDiff.props @@ -0,0 +1,12 @@ + + + 0.10.0-preview3 + $(PackageId) + Avalonia + + + + + + + diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index f827cb9a32..d8e86e917e 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index bd183faab3..d3cebef418 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> Avalonia - 0.9.999 + 0.10.999 Copyright 2020 © The AvaloniaUI Project https://avaloniaui.net https://github.com/AvaloniaUI/Avalonia/ diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index a8d9332c57..d7d04c7971 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,6 +1,6 @@  - - + + diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm index 278daf9a18..455cfa2e41 100644 --- a/native/Avalonia.Native/src/OSX/Screens.mm +++ b/native/Avalonia.Native/src/OSX/Screens.mm @@ -4,6 +4,14 @@ class Screens : public ComSingleObject { public: FORWARD_IUNKNOWN() + + private: + CGFloat PrimaryDisplayHeight() + { + return NSMaxY([[[NSScreen screens] firstObject] frame]); + } + +public: virtual HRESULT GetScreenCount (int* ret) override { @autoreleasepool @@ -25,15 +33,15 @@ class Screens : public ComSingleObject auto screen = [[NSScreen screens] objectAtIndex:index]; - ret->Bounds.X = [screen frame].origin.x; - ret->Bounds.Y = [screen frame].origin.y; ret->Bounds.Height = [screen frame].size.height; ret->Bounds.Width = [screen frame].size.width; + ret->Bounds.X = [screen frame].origin.x; + ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height; - ret->WorkingArea.X = [screen visibleFrame].origin.x; - ret->WorkingArea.Y = [screen visibleFrame].origin.y; ret->WorkingArea.Height = [screen visibleFrame].size.height; ret->WorkingArea.Width = [screen visibleFrame].size.width; + ret->WorkingArea.X = [screen visibleFrame].origin.x; + ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height; ret->PixelDensity = [screen backingScaleFactor]; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 872269bb26..dd241409c7 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -27,6 +27,7 @@ public: AvnPoint lastPositionSet; NSString* _lastTitle; IAvnMenu* _mainMenu; + bool _shown; WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) @@ -230,6 +231,29 @@ public: { @autoreleasepool { + auto maxSize = [Window maxSize]; + auto minSize = [Window minSize]; + + if (x < minSize.width) + { + x = minSize.width; + } + + if (y < minSize.height) + { + y = minSize.height; + } + + if (x > maxSize.width) + { + x = maxSize.width; + } + + if (y > maxSize.height) + { + y = maxSize.height; + } + [Window setContentSize:NSSize{x, y}]; return S_OK; @@ -1291,10 +1315,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _parent->UpdateCursor(); auto fsize = [self convertSizeToBacking: [self frame].size]; - _lastPixelSize.Width = (int)fsize.width; - _lastPixelSize.Height = (int)fsize.height; - [self updateRenderTarget]; - _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); + + if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) + { + _lastPixelSize.Width = (int)fsize.width; + _lastPixelSize.Height = (int)fsize.height; + [self updateRenderTarget]; + + _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); + } } - (void)updateLayer diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index fe877dc49c..fbfbf47e1b 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -128,7 +128,9 @@ partial class Build : NukeBuild { var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; - NpmTasks.NpmInstall(c => c.SetWorkingDirectory(webappDir)); + NpmTasks.NpmInstall(c => c + .SetWorkingDirectory(webappDir) + .SetArgumentConfigurator(a => a.Add("--silent"))); NpmTasks.NpmRun(c => c .SetWorkingDirectory(webappDir) .SetCommand("dist")); diff --git a/readme.md b/readme.md index 19a9a8420d..ccdbbfb967 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
[![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) @@ -16,7 +16,7 @@ To see the status of some of our features, please see our [Roadmap](https://gith ## 🚀 Getting Started -The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](http://avaloniaui.net/docs/quickstart/create-new-project). +The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://avaloniaui.net/docs/quickstart/create-new-project). Avalonia is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/ @@ -47,7 +47,7 @@ We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using ## Documentation -Documentation can be found on our website at http://avaloniaui.net/docs/. We also have a [tutorial](http://avaloniaui.net/docs/tutorial/) over there for newcomers. +Documentation can be found on our website at https://avaloniaui.net/docs/. We also have a [tutorial](https://avaloniaui.net/docs/tutorial/) over there for newcomers. ## Building and Using @@ -68,7 +68,7 @@ Avalonia is licenced under the [MIT licence](licence.md). ## Contributors -This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing)]. +This project exists thanks to all the people who contribute. [[Contribute](https://avaloniaui.net/contributing)]. ### Backers diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml index 14c371efef..b583503327 100644 --- a/samples/BindingDemo/MainWindow.xaml +++ b/samples/BindingDemo/MainWindow.xaml @@ -116,6 +116,7 @@ - + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index 47b4ce7151..edf3d41bf5 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -10,7 +10,12 @@ HorizontalAlignment="Center" Spacing="16"> - + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs index 0f2d4461bf..59c8fc0cbe 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reactive; using Avalonia.Controls; using Avalonia.Markup.Xaml; -using ReactiveUI; +using ControlCatalog.ViewModels; namespace ControlCatalog.Pages { @@ -13,72 +9,12 @@ namespace ControlCatalog.Pages public ListBoxPage() { InitializeComponent(); - DataContext = new PageViewModel(); + DataContext = new ListBoxPageViewModel(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } - - private class PageViewModel : ReactiveObject - { - private int _counter; - private SelectionMode _selectionMode; - - public PageViewModel() - { - Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); - SelectedItems = new ObservableCollection(); - - AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem())); - - RemoveItemCommand = ReactiveCommand.Create(() => - { - while (SelectedItems.Count > 0) - { - Items.Remove(SelectedItems[0]); - } - }); - - SelectRandomItemCommand = ReactiveCommand.Create(() => - { - var random = new Random(); - - SelectedItem = Items[random.Next(Items.Count - 1)]; - }); - } - - public ObservableCollection Items { get; } - - private string _selectedItem; - - public string SelectedItem - { - get { return _selectedItem; } - set { this.RaiseAndSetIfChanged(ref _selectedItem, value); } - } - - - public ObservableCollection SelectedItems { get; } - - public ReactiveCommand AddItemCommand { get; } - - public ReactiveCommand RemoveItemCommand { get; } - - public ReactiveCommand SelectRandomItemCommand { get; } - - public SelectionMode SelectionMode - { - get => _selectionMode; - set - { - SelectedItems.Clear(); - this.RaiseAndSetIfChanged(ref _selectionMode, value); - } - } - - private string GenerateItem() => $"Item {_counter++.ToString()}"; - } } } diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index de9ea34e80..2c09cb9b4d 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -16,11 +16,17 @@ Defined in XAML - + + - + + + + + + @@ -52,6 +58,33 @@ + + + + Mixed + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index d775eb9635..c39f414b44 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -29,7 +29,8 @@ namespace ControlCatalog.Pages var screens = w.Screens.All; var scaling = ((IRenderRoot)w).RenderScaling; - Pen p = new Pen(Brushes.Black); + var drawBrush = Brushes.Green; + Pen p = new Pen(drawBrush); if (screens != null) foreach (Screen screen in screens) { @@ -53,19 +54,19 @@ namespace ControlCatalog.Pages }; text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; - context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height), text); + context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text); text.Text = $"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"; - context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text); + context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text); text.Text = $"Scaling: {screen.PixelDensity * 100}%"; - context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text); + context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text); text.Text = $"Primary: {screen.Primary}"; - context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text); + 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(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); + context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); } context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10)); diff --git a/samples/ControlCatalog/Pages/TextBlockPage.xaml b/samples/ControlCatalog/Pages/TextBlockPage.xaml index 4b8edcf98c..4a1c196917 100644 --- a/samples/ControlCatalog/Pages/TextBlockPage.xaml +++ b/samples/ControlCatalog/Pages/TextBlockPage.xaml @@ -12,7 +12,7 @@ @@ -49,7 +49,7 @@ diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 64118a00b4..481a459159 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -20,6 +20,7 @@ @@ -37,6 +38,8 @@ Text="Multiline TextBox with TextWrapping. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." /> + + resm fonts diff --git a/samples/ControlCatalog/Pages/TreeViewPage.xaml b/samples/ControlCatalog/Pages/TreeViewPage.xaml index 789b45e62c..6d99132680 100644 --- a/samples/ControlCatalog/Pages/TreeViewPage.xaml +++ b/samples/ControlCatalog/Pages/TreeViewPage.xaml @@ -10,7 +10,7 @@ HorizontalAlignment="Center" Spacing="16"> - + diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs new file mode 100644 index 0000000000..d088576998 --- /dev/null +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using Avalonia.Controls; +using Avalonia.Controls.Selection; +using ReactiveUI; + +namespace ControlCatalog.ViewModels +{ + public class ListBoxPageViewModel : ReactiveObject + { + private int _counter; + private SelectionMode _selectionMode; + + public ListBoxPageViewModel() + { + Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); + Selection = new SelectionModel(); + Selection.Select(1); + + AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem())); + + RemoveItemCommand = ReactiveCommand.Create(() => + { + while (Selection.Count > 0) + { + Items.Remove(Selection.SelectedItems.First()); + } + }); + + SelectRandomItemCommand = ReactiveCommand.Create(() => + { + var random = new Random(); + + using (Selection.BatchUpdate()) + { + Selection.Clear(); + Selection.Select(random.Next(Items.Count - 1)); + } + }); + } + + public ObservableCollection Items { get; } + + public SelectionModel Selection { get; } + + public ReactiveCommand AddItemCommand { get; } + + public ReactiveCommand RemoveItemCommand { get; } + + public ReactiveCommand SelectRandomItemCommand { get; } + + public SelectionMode SelectionMode + { + get => _selectionMode; + set + { + Selection.Clear(); + this.RaiseAndSetIfChanged(ref _selectionMode, value); + } + } + + private string GenerateItem() => $"Item {_counter++.ToString()}"; + } +} diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs index dc9c4a8f49..9e7ae8b716 100644 --- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs @@ -17,6 +17,23 @@ namespace ControlCatalog.ViewModels SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false)); OpenRecentCommand = ReactiveCommand.Create(OpenRecent); + var recentItems = new[] + { + new MenuItemViewModel + { + Header = "File1.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File1.txt" + }, + new MenuItemViewModel + { + Header = "File2.txt", + Command = OpenRecentCommand, + CommandParameter = @"c:\foo\File2.txt" + }, + }; + + RecentItems = recentItems; MenuItems = new[] { new MenuItemViewModel @@ -24,27 +41,13 @@ namespace ControlCatalog.ViewModels Header = "_File", Items = new[] { - new MenuItemViewModel { Header = "_Open...", Command = OpenCommand }, + new MenuItemViewModel { Header = "O_pen...", Command = OpenCommand }, new MenuItemViewModel { Header = "Save", Command = SaveCommand }, new MenuItemViewModel { Header = "-" }, new MenuItemViewModel { Header = "Recent", - Items = new[] - { - new MenuItemViewModel - { - Header = "File1.txt", - Command = OpenRecentCommand, - CommandParameter = @"c:\foo\File1.txt" - }, - new MenuItemViewModel - { - Header = "File2.txt", - Command = OpenRecentCommand, - CommandParameter = @"c:\foo\File2.txt" - }, - } + Items = recentItems }, } }, @@ -61,6 +64,7 @@ namespace ControlCatalog.ViewModels } public IReadOnlyList MenuItems { get; set; } + public IReadOnlyList RecentItems { get; set; } public ReactiveCommand OpenCommand { get; } public ReactiveCommand SaveCommand { get; } public ReactiveCommand OpenRecentCommand { get; } diff --git a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs index 5bc23e2fe5..210e281ed6 100644 --- a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reactive; @@ -18,8 +17,7 @@ namespace ControlCatalog.ViewModels _root = new Node(); Items = _root.Children; - Selection = new SelectionModel(); - Selection.SelectionChanged += SelectionChanged; + SelectedItems = new ObservableCollection(); AddItemCommand = ReactiveCommand.Create(AddItem); RemoveItemCommand = ReactiveCommand.Create(RemoveItem); @@ -27,7 +25,7 @@ namespace ControlCatalog.ViewModels } public ObservableCollection Items { get; } - public SelectionModel Selection { get; } + public ObservableCollection SelectedItems { get; } public ReactiveCommand AddItemCommand { get; } public ReactiveCommand RemoveItemCommand { get; } public ReactiveCommand SelectRandomItemCommand { get; } @@ -37,24 +35,24 @@ namespace ControlCatalog.ViewModels get => _selectionMode; set { - Selection.ClearSelection(); + SelectedItems.Clear(); this.RaiseAndSetIfChanged(ref _selectionMode, value); } } private void AddItem() { - var parentItem = Selection.SelectedItems.Count > 0 ? (Node)Selection.SelectedItems[0] : _root; + var parentItem = SelectedItems.Count > 0 ? (Node)SelectedItems[0] : _root; parentItem.AddItem(); } private void RemoveItem() { - while (Selection.SelectedItems.Count > 0) + while (SelectedItems.Count > 0) { - Node lastItem = (Node)Selection.SelectedItems[0]; + Node lastItem = (Node)SelectedItems[0]; RecursiveRemove(Items, lastItem); - Selection.DeselectAt(Selection.SelectedIndices[0]); + SelectedItems.RemoveAt(0); } bool RecursiveRemove(ObservableCollection items, Node selectedItem) @@ -80,16 +78,16 @@ namespace ControlCatalog.ViewModels { var random = new Random(); var depth = random.Next(4); - var indexes = Enumerable.Range(0, 4).Select(x => random.Next(10)); - var path = new IndexPath(indexes); - Selection.SelectedIndex = path; - } + var indexes = Enumerable.Range(0, depth).Select(x => random.Next(10)); + var node = _root; - private void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) - { - var selected = string.Join(",", e.SelectedIndices); - var deselected = string.Join(",", e.DeselectedIndices); - System.Diagnostics.Debug.WriteLine($"Selected '{selected}', Deselected '{deselected}'"); + foreach (var i in indexes) + { + node = node.Children[i]; + } + + SelectedItems.Clear(); + SelectedItems.Add(node); } public class Node diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 14ccc82043..770960d7c4 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -44,6 +44,9 @@ + + + diff --git a/samples/RenderDemo/Pages/WriteableBitmapPage.cs b/samples/RenderDemo/Pages/WriteableBitmapPage.cs new file mode 100644 index 0000000000..850e398a93 --- /dev/null +++ b/samples/RenderDemo/Pages/WriteableBitmapPage.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.LogicalTree; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Media.Immutable; +using Avalonia.Platform; +using Avalonia.Threading; + +namespace RenderDemo.Pages +{ + public class WriteableBitmapPage : Control + { + private WriteableBitmap _unpremulBitmap; + private WriteableBitmap _premulBitmap; + private readonly Stopwatch _st = Stopwatch.StartNew(); + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + _unpremulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul); + _premulBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul); + + base.OnAttachedToLogicalTree(e); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnDetachedFromLogicalTree(e); + + _unpremulBitmap?.Dispose(); + _unpremulBitmap = null; + + _premulBitmap?.Dispose(); + _unpremulBitmap = null; + } + + public override void Render(DrawingContext context) + { + void FillPixels(WriteableBitmap bitmap, byte fillAlpha, bool premul) + { + using (var fb = bitmap.Lock()) + { + var data = new int[fb.Size.Width * fb.Size.Height]; + + for (int y = 0; y < fb.Size.Height; y++) + { + for (int x = 0; x < fb.Size.Width; x++) + { + var color = new Color(fillAlpha, 0, 255, 0); + + if (premul) + { + byte r = (byte) (color.R * color.A / 255); + byte g = (byte) (color.G * color.A / 255); + byte b = (byte) (color.B * color.A / 255); + + color = new Color(fillAlpha, r, g, b); + } + + data[y * fb.Size.Width + x] = (int) color.ToUint32(); + } + } + + Marshal.Copy(data, 0, fb.Address, fb.Size.Width * fb.Size.Height); + } + } + + base.Render(context); + + byte alpha = (byte)((_st.ElapsedMilliseconds / 10) % 256); + + FillPixels(_unpremulBitmap, alpha, false); + FillPixels(_premulBitmap, alpha, true); + + context.FillRectangle(Brushes.Red, new Rect(0, 0, 256 * 3, 256)); + + context.DrawImage(_unpremulBitmap, + new Rect(0, 0, 256, 256), + new Rect(0, 0, 256, 256)); + + context.DrawImage(_premulBitmap, + new Rect(0, 0, 256, 256), + new Rect(256, 0, 256, 256)); + + context.FillRectangle(new ImmutableSolidColorBrush(Colors.Lime, alpha / 255d), new Rect(512, 0, 256, 256)); + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + } + } +} diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs index 3a6ed88fcd..852c01399f 100644 --- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs @@ -7,6 +7,7 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using ReactiveUI; using Avalonia.Layout; +using Avalonia.Controls.Selection; namespace VirtualizationDemo.ViewModels { @@ -48,7 +49,7 @@ namespace VirtualizationDemo.ViewModels set { this.RaiseAndSetIfChanged(ref _itemCount, value); } } - public SelectionModel Selection { get; } = new SelectionModel(); + public SelectionModel Selection { get; } = new SelectionModel(); public AvaloniaList Items { @@ -137,9 +138,9 @@ namespace VirtualizationDemo.ViewModels { var index = Items.Count; - if (Selection.SelectedIndices.Count > 0) + if (Selection.SelectedItems.Count > 0) { - index = Selection.SelectedIndex.GetAt(0); + index = Selection.SelectedIndex; } Items.Insert(index, new ItemViewModel(_newItemIndex++, NewItemString)); @@ -149,7 +150,7 @@ namespace VirtualizationDemo.ViewModels { if (Selection.SelectedItems.Count > 0) { - Items.RemoveAll(Selection.SelectedItems.Cast().ToList()); + Items.RemoveAll(Selection.SelectedItems.ToList()); } } @@ -163,7 +164,7 @@ namespace VirtualizationDemo.ViewModels private void SelectItem(int index) { - Selection.SelectedIndex = new IndexPath(index); + Selection.SelectedIndex = index; } } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 69fd2a9f13..71dce93ce7 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -126,7 +126,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _view.Visibility = ViewStates.Visible; } - public double Scaling => 1; + public double RenderScaling => 1; void Draw() { diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index 73c619942e..ee91acf45e 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -1,9 +1,10 @@  - netstandard2.0 + netstandard2.0 + diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index cbcf7a7386..9bfd387ca9 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -3,7 +3,7 @@ netstandard2.0 Avalonia.Base Avalonia - True + True
@@ -13,4 +13,5 @@ + diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index d18f0b3f94..65233f9230 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -806,7 +806,9 @@ namespace Avalonia break; } - if (p.IsDataValidationEnabled) + var metadata = p.GetMetadata(GetType()); + + if (metadata.EnableDataValidation == true) { UpdateDataValidation(property, value); } diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index a873d5fd42..39391490b0 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -369,14 +369,14 @@ namespace Avalonia var metadata = new DirectPropertyMetadata( unsetValue: unsetValue, - defaultBindingMode: defaultBindingMode); + defaultBindingMode: defaultBindingMode, + enableDataValidation: enableDataValidation); var result = new DirectProperty( name, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result); return result; } diff --git a/src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs b/src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs deleted file mode 100644 index 7f4c83772d..0000000000 --- a/src/Avalonia.Base/Data/Converters/AlwaysEnabledDelegateCommand.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Globalization; -using System.Reflection; -using System.Windows.Input; -using Avalonia.Utilities; - -namespace Avalonia.Data.Converters -{ - class AlwaysEnabledDelegateCommand : ICommand - { - private readonly Delegate action; - - private ParameterInfo parameterInfo; - - public AlwaysEnabledDelegateCommand(Delegate action) - { - this.action = action; - var parameters = action.Method.GetParameters(); - parameterInfo = parameters.Length == 0 ? null : parameters[0]; - } - -#pragma warning disable 0067 - public event EventHandler CanExecuteChanged; -#pragma warning restore 0067 - - public bool CanExecute(object parameter) => true; - - public void Execute(object parameter) - { - if (parameterInfo == null) - { - action.DynamicInvoke(); - } - else - { - TypeUtilities.TryConvert(parameterInfo.ParameterType, parameter, CultureInfo.CurrentCulture, out object convertedParameter); - action.DynamicInvoke(convertedParameter); - } - } - } -} diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index 5e80a15059..83f7e02c16 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -33,7 +33,7 @@ namespace Avalonia.Data.Converters if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1) { - return new AlwaysEnabledDelegateCommand(d); + return new MethodToCommandConverter(d); } if (TypeUtilities.TryConvert(targetType, value, culture, out object result)) diff --git a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs new file mode 100644 index 0000000000..7e7417d2f5 --- /dev/null +++ b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs @@ -0,0 +1,230 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Windows.Input; +using Avalonia.Utilities; + +namespace Avalonia.Data.Converters +{ + class MethodToCommandConverter : ICommand + { + readonly static Func AlwaysEnabled = (_) => true; + readonly static MethodInfo tryConvert = typeof(TypeUtilities) + .GetMethod(nameof(TypeUtilities.TryConvert), BindingFlags.Public | BindingFlags.Static); + readonly static PropertyInfo currentCulture = typeof(CultureInfo) + .GetProperty(nameof(CultureInfo.CurrentCulture), BindingFlags.Public | BindingFlags.Static); + readonly Func canExecute; + readonly Action execute; + readonly WeakPropertyChangedProxy weakPropertyChanged; + readonly PropertyChangedEventHandler propertyChangedEventHandler; + readonly string[] dependencyProperties; + + public MethodToCommandConverter(Delegate action) + { + var target = action.Target; + var canExecuteMethodName = "Can" + action.Method.Name; + var parameters = action.Method.GetParameters(); + var parameterInfo = parameters.Length == 0 ? null : parameters[0].ParameterType; + + if (parameterInfo == null) + { + execute = CreateExecute(target, action.Method); + } + else + { + execute = CreateExecute(target, action.Method, parameterInfo); + } + + var canExecuteMethod = action.Method.DeclaringType.GetRuntimeMethods() + .FirstOrDefault(m => m.Name == canExecuteMethodName + && m.GetParameters().Length == 1 + && m.GetParameters()[0].ParameterType == typeof(object)); + if (canExecuteMethod == null) + { + canExecute = AlwaysEnabled; + } + else + { + canExecute = CreateCanExecute(target, canExecuteMethod); + dependencyProperties = canExecuteMethod + .GetCustomAttributes(typeof(Metadata.DependsOnAttribute), true) + .OfType() + .Select(x => x.Name) + .ToArray(); + if (dependencyProperties.Any() + && target is INotifyPropertyChanged inpc) + { + propertyChangedEventHandler = OnPropertyChanged; + weakPropertyChanged = new WeakPropertyChangedProxy(inpc, propertyChangedEventHandler); + } + } + } + + void OnPropertyChanged(object sender,PropertyChangedEventArgs args) + { + if (string.IsNullOrWhiteSpace(args.PropertyName) + || dependencyProperties?.Contains(args.PropertyName) == true) + { + Threading.Dispatcher.UIThread.Post(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty) + , Threading.DispatcherPriority.Input); + } + } + +#pragma warning disable 0067 + public event EventHandler CanExecuteChanged; +#pragma warning restore 0067 + + public bool CanExecute(object parameter) => canExecute(parameter); + + public void Execute(object parameter) => execute(parameter); + + + static Action CreateExecute(object target + , System.Reflection.MethodInfo method) + { + + var parameter = Expression.Parameter(typeof(object), "parameter"); + + var instance = Expression.Convert + ( + Expression.Constant(target), + method.DeclaringType + ); + + + var call = Expression.Call + ( + instance, + method + ); + + + return Expression + .Lambda>(call, parameter) + .Compile(); + } + + static Action CreateExecute(object target + , System.Reflection.MethodInfo method + , Type parameterType) + { + + var parameter = Expression.Parameter(typeof(object), "parameter"); + + var instance = Expression.Convert + ( + Expression.Constant(target), + method.DeclaringType + ); + + Expression body; + + if (parameterType == typeof(object)) + { + body = Expression.Call(instance, + method, + parameter + ); + } + else + { + var arg0 = Expression.Variable(typeof(object), "argX"); + var convertCall = Expression.Call(tryConvert, + Expression.Constant(parameterType), + parameter, + Expression.Property(null, currentCulture), + arg0 + ); + + var call = Expression.Call(instance, + method, + Expression.Convert(arg0, parameterType) + ); + body = Expression.Block(new[] { arg0 }, + convertCall, + call + ); + + } + Action action = null; + try + { + action = Expression + .Lambda>(body, parameter) + .Compile(); + } + catch (Exception ex) + { + throw ex; + } + return action; + } + + static Func CreateCanExecute(object target + , System.Reflection.MethodInfo method) + { + var parameter = Expression.Parameter(typeof(object), "parameter"); + var instance = Expression.Convert + ( + Expression.Constant(target), + method.DeclaringType + ); + var call = Expression.Call + ( + instance, + method, + parameter + ); + return Expression + .Lambda>(call, parameter) + .Compile(); + } + + + internal class WeakPropertyChangedProxy + { + readonly WeakReference _listener = new WeakReference(null); + readonly PropertyChangedEventHandler _handler; + internal WeakReference Source { get; } = new WeakReference(null); + + public WeakPropertyChangedProxy() + { + _handler = new PropertyChangedEventHandler(OnPropertyChanged); + } + + public WeakPropertyChangedProxy(INotifyPropertyChanged source, PropertyChangedEventHandler listener) : this() + { + SubscribeTo(source, listener); + } + + public void SubscribeTo(INotifyPropertyChanged source, PropertyChangedEventHandler listener) + { + source.PropertyChanged += _handler; + + Source.SetTarget(source); + _listener.SetTarget(listener); + } + + public void Unsubscribe() + { + if (Source.TryGetTarget(out INotifyPropertyChanged source) && source != null) + source.PropertyChanged -= _handler; + + Source.SetTarget(null); + _listener.SetTarget(null); + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (_listener.TryGetTarget(out var handler) && handler != null) + handler(sender, e); + else + Unsubscribe(); + } + + } + } +} diff --git a/src/Avalonia.Base/DirectProperty.cs b/src/Avalonia.Base/DirectProperty.cs index d21969502a..a8120fbd4f 100644 --- a/src/Avalonia.Base/DirectProperty.cs +++ b/src/Avalonia.Base/DirectProperty.cs @@ -23,16 +23,12 @@ namespace Avalonia /// Gets the current value of the property. /// Sets the value of the property. May be null. /// The property metadata. - /// - /// Whether the property is interested in data validation. - /// public DirectProperty( string name, Func getter, Action setter, - DirectPropertyMetadata metadata, - bool enableDataValidation) - : base(name, typeof(TOwner), metadata, enableDataValidation) + DirectPropertyMetadata metadata) + : base(name, typeof(TOwner), metadata) { Contract.Requires(getter != null); @@ -47,16 +43,12 @@ namespace Avalonia /// Gets the current value of the property. /// Sets the value of the property. May be null. /// Optional overridden metadata. - /// - /// Whether the property is interested in data validation. - /// private DirectProperty( DirectPropertyBase source, Func getter, Action setter, - DirectPropertyMetadata metadata, - bool enableDataValidation) - : base(source, typeof(TOwner), metadata, enableDataValidation) + DirectPropertyMetadata metadata) + : base(source, typeof(TOwner), metadata) { Contract.Requires(getter != null); @@ -107,7 +99,8 @@ namespace Avalonia { var metadata = new DirectPropertyMetadata( unsetValue: unsetValue, - defaultBindingMode: defaultBindingMode); + defaultBindingMode: defaultBindingMode, + enableDataValidation: enableDataValidation); metadata.Merge(GetMetadata(), this); @@ -115,8 +108,7 @@ namespace Avalonia (DirectPropertyBase)this, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result); return result; @@ -155,8 +147,7 @@ namespace Avalonia this, getter, setter, - metadata, - enableDataValidation); + metadata); AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result); return result; diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index d42c030245..dbc2625b86 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -23,17 +23,12 @@ namespace Avalonia /// The name of the property. /// The type of the class that registers the property. /// The property metadata. - /// - /// Whether the property is interested in data validation. - /// protected DirectPropertyBase( string name, Type ownerType, - PropertyMetadata metadata, - bool enableDataValidation) + PropertyMetadata metadata) : base(name, ownerType, metadata) { - IsDataValidationEnabled = enableDataValidation; } /// @@ -42,17 +37,12 @@ namespace Avalonia /// The property to copy. /// The new owner type. /// Optional overridden metadata. - /// - /// Whether the property is interested in data validation. - /// protected DirectPropertyBase( AvaloniaProperty source, Type ownerType, - PropertyMetadata metadata, - bool enableDataValidation) + PropertyMetadata metadata) : base(source, ownerType, metadata) { - IsDataValidationEnabled = enableDataValidation; } /// @@ -60,11 +50,6 @@ namespace Avalonia /// public abstract Type Owner { get; } - /// - /// Gets a value that indicates whether data validation is enabled for the property. - /// - public bool IsDataValidationEnabled { get; } - /// /// Gets the value of the property on the instance. /// @@ -102,6 +87,26 @@ namespace Avalonia return (DirectPropertyMetadata)base.GetMetadata(type); } + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(DirectPropertyMetadata metadata) where T : IAvaloniaObject + { + base.OverrideMetadata(typeof(T), metadata); + } + + /// + /// Overrides the metadata for the property on the specified type. + /// + /// The type. + /// The metadata. + public void OverrideMetadata(Type type, DirectPropertyMetadata metadata) + { + base.OverrideMetadata(type, metadata); + } + /// public override void Accept(IAvaloniaPropertyVisitor vistor, ref TData data) { diff --git a/src/Avalonia.Base/Metadata/DependsOnAttribute.cs b/src/Avalonia.Base/Metadata/DependsOnAttribute.cs index 92c6a58170..caee71ebfd 100644 --- a/src/Avalonia.Base/Metadata/DependsOnAttribute.cs +++ b/src/Avalonia.Base/Metadata/DependsOnAttribute.cs @@ -5,7 +5,7 @@ namespace Avalonia.Metadata /// /// Indicates that the property depends on the value of another property in markup. /// - [AttributeUsage(AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class DependsOnAttribute : Attribute { /// diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 692982cdc6..13b247de51 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -9,3 +9,4 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.UnitTests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid")] +[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests")] diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index 0bfc713ba0..397826df53 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -9,6 +9,16 @@ namespace Avalonia.Threading /// public class AvaloniaScheduler : LocalScheduler { + /// + /// Users can schedule actions on the dispatcher thread while being on the correct thread already. + /// We are optimizing this case by invoking user callback immediately which can lead to stack overflows in certain cases. + /// To prevent this we are limiting amount of reentrant calls to before we will + /// schedule on a dispatcher anyway. + /// + private const int MaxReentrantSchedules = 32; + + private int _reentrancyGuard; + /// /// The instance of the . /// @@ -24,31 +34,58 @@ namespace Avalonia.Threading /// public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) { - var composite = new CompositeDisposable(2); + IDisposable PostOnDispatcher() + { + var composite = new CompositeDisposable(2); + + var cancellation = new CancellationDisposable(); + + Dispatcher.UIThread.Post(() => + { + if (!cancellation.Token.IsCancellationRequested) + { + composite.Add(action(this, state)); + } + }, DispatcherPriority.DataBind); + + composite.Add(cancellation); + + return composite; + } + if (dueTime == TimeSpan.Zero) { if (!Dispatcher.UIThread.CheckAccess()) { - var cancellation = new CancellationDisposable(); - Dispatcher.UIThread.Post(() => - { - if (!cancellation.Token.IsCancellationRequested) - { - composite.Add(action(this, state)); - } - }, DispatcherPriority.DataBind); - composite.Add(cancellation); + return PostOnDispatcher(); } else { - return action(this, state); + if (_reentrancyGuard >= MaxReentrantSchedules) + { + return PostOnDispatcher(); + } + + try + { + _reentrancyGuard++; + + return action(this, state); + } + finally + { + _reentrancyGuard--; + } } } else { + var composite = new CompositeDisposable(2); + composite.Add(DispatcherTimer.RunOnce(() => composite.Add(action(this, state)), dueTime)); + + return composite; } - return composite; } } } diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index c1cc0e7bf0..c9c9c562bd 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -128,7 +128,7 @@ namespace Avalonia.Build.Tasks asm.MainModule.Types.Add(typeDef); var builder = typeSystem.CreateTypeBuilder(typeDef); - foreach (var res in group.Resources.Where(CheckXamlName)) + foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant())) { try { diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 27853a1540..6975642db2 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -22,4 +22,5 @@ + diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index fe9656a00b..f7903086ab 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -39,6 +39,7 @@ namespace Avalonia.Controls private const string DATAGRID_elementRowHeadersPresenterName = "PART_RowHeadersPresenter"; private const string DATAGRID_elementTopLeftCornerHeaderName = "PART_TopLeftCornerHeader"; private const string DATAGRID_elementTopRightCornerHeaderName = "PART_TopRightCornerHeader"; + private const string DATAGRID_elementBottomRightCornerHeaderName = "PART_BottomRightCorner"; private const string DATAGRID_elementValidationSummary = "PART_ValidationSummary"; private const string DATAGRID_elementVerticalScrollbarName = "PART_VerticalScrollbar"; @@ -79,6 +80,7 @@ namespace Avalonia.Controls private INotifyCollectionChanged _topLevelGroup; private ContentControl _clipboardContentControl; + private IVisual _bottomRightCorner; private DataGridColumnHeadersPresenter _columnHeadersPresenter; private DataGridRowsPresenter _rowsPresenter; private ScrollBar _vScrollBar; @@ -1825,6 +1827,22 @@ namespace Avalonia.Controls } } + private bool IsHorizontalScrollBarOverCells + { + get + { + return _columnHeadersPresenter != null && Grid.GetColumnSpan(_columnHeadersPresenter) == 2; + } + } + + private bool IsVerticalScrollBarOverCells + { + get + { + return _rowsPresenter != null && Grid.GetRowSpan(_rowsPresenter) == 2; + } + } + private int NoSelectionChangeCount { get @@ -2323,6 +2341,7 @@ namespace Avalonia.Controls _topLeftCornerHeader = e.NameScope.Find(DATAGRID_elementTopLeftCornerHeaderName); EnsureTopLeftCornerHeader(); // EnsureTopLeftCornerHeader checks for a null _topLeftCornerHeader; _topRightCornerHeader = e.NameScope.Find(DATAGRID_elementTopRightCornerHeaderName); + _bottomRightCorner = e.NameScope.Find(DATAGRID_elementBottomRightCornerHeaderName); } /// @@ -2753,7 +2772,7 @@ namespace Avalonia.Controls //We don't need to refresh the state of AutoGenerated column headers because they're up-to-date if (!column.IsAutoGenerated && column.HasHeaderCell) { - column.HeaderCell.ApplyState(); + column.HeaderCell.UpdatePseudoClasses(); } } @@ -3293,6 +3312,10 @@ namespace Avalonia.Controls // } + + bool isHorizontalScrollBarOverCells = IsHorizontalScrollBarOverCells; + bool isVerticalScrollBarOverCells = IsVerticalScrollBarOverCells; + double cellsWidth = CellsWidth; double cellsHeight = CellsHeight; @@ -3308,10 +3331,17 @@ namespace Avalonia.Controls // Compensate if the horizontal scrollbar is already taking up space if (!forceHorizScrollbar && _hScrollBar.IsVisible) { - cellsHeight += _hScrollBar.DesiredSize.Height; + if (!isHorizontalScrollBarOverCells) + { + cellsHeight += _hScrollBar.DesiredSize.Height; + } + } + if (!isHorizontalScrollBarOverCells) + { + horizScrollBarHeight = _hScrollBar.Height + _hScrollBar.Margin.Top + _hScrollBar.Margin.Bottom; } - horizScrollBarHeight = _hScrollBar.Height + _hScrollBar.Margin.Top + _hScrollBar.Margin.Bottom; } + bool allowVertScrollbar = false; bool forceVertScrollbar = false; double vertScrollBarWidth = 0; @@ -3324,9 +3354,15 @@ namespace Avalonia.Controls // Compensate if the vertical scrollbar is already taking up space if (!forceVertScrollbar && _vScrollBar.IsVisible) { - cellsWidth += _vScrollBar.DesiredSize.Width; + if (!isVerticalScrollBarOverCells) + { + cellsWidth += _vScrollBar.DesiredSize.Width; + } + } + if (!isVerticalScrollBarOverCells) + { + vertScrollBarWidth = _vScrollBar.Width + _vScrollBar.Margin.Left + _vScrollBar.Margin.Right; } - vertScrollBarWidth = _vScrollBar.Width + _vScrollBar.Margin.Left + _vScrollBar.Margin.Right; } // Now cellsWidth is the width potentially available for displaying data cells. @@ -3354,7 +3390,9 @@ namespace Avalonia.Controls cellsHeight -= horizScrollBarHeight; Debug.Assert(cellsHeight >= 0); needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true; - if (allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) || + + if (vertScrollBarWidth > 0 && + allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) || MathUtilities.LessThanOrClose(cellsWidth - totalVisibleFrozenWidth, vertScrollBarWidth))) { // Would we still need a horizontal scrollbar without the vertical one? @@ -3372,7 +3410,12 @@ namespace Avalonia.Controls } } - UpdateDisplayedRows(DisplayData.FirstScrollingSlot, cellsHeight); + // Store the current FirstScrollingSlot because removing the horizontal scrollbar could scroll + // the DataGrid up; however, if we realize later that we need to keep the horizontal scrollbar + // then we should use the first slot stored here which is not scrolled. + int firstScrollingSlot = DisplayData.FirstScrollingSlot; + + UpdateDisplayedRows(firstScrollingSlot, cellsHeight); if (allowVertScrollbar && MathUtilities.GreaterThan(cellsHeight, 0) && MathUtilities.LessThanOrClose(vertScrollBarWidth, cellsWidth) && @@ -3384,10 +3427,12 @@ namespace Avalonia.Controls } DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn(); + // we compute the number of visible columns only after we set up the vertical scroll bar. ComputeDisplayedColumns(); - if (allowHorizScrollbar && + if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) && + allowHorizScrollbar && needVertScrollbar && !needHorizScrollbar && MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) && MathUtilities.LessThan(totalVisibleFrozenWidth, cellsWidth) && @@ -3398,7 +3443,7 @@ namespace Avalonia.Controls Debug.Assert(cellsHeight >= 0); needVertScrollbar = false; - UpdateDisplayedRows(DisplayData.FirstScrollingSlot, cellsHeight); + UpdateDisplayedRows(firstScrollingSlot, cellsHeight); if (cellsHeight > 0 && vertScrollBarWidth <= cellsWidth && DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount) @@ -3479,6 +3524,15 @@ namespace Avalonia.Controls _topRightCornerHeader.IsVisible = false; } } + + if (_bottomRightCorner != null) + { + // Show the BottomRightCorner when both scrollbars are visible. + _bottomRightCorner.IsVisible = + _hScrollBar != null && _hScrollBar.IsVisible && + _vScrollBar != null && _vScrollBar.IsVisible; + } + DisplayData.FullyRecycleElements(); } @@ -3922,6 +3976,12 @@ namespace Avalonia.Controls dataGridCell: editingCell); EditingRow.InvalidateDesiredHeight(); + var column = editingCell.OwningColumn; + if (column.Width.IsSizeToCells || column.Width.IsAuto) + {// Invalidate desired width and force recalculation + column.SetWidthDesiredValue(0); + EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width); + } } // We're done, so raise the CellEditEnded event @@ -5411,7 +5471,7 @@ namespace Avalonia.Controls } else if (displayedElement is DataGridRowGroupHeader groupHeader) { - groupHeader.ApplyState(useTransitions: true); + groupHeader.UpdatePseudoClasses(); if (AreRowHeadersVisible) { groupHeader.ApplyHeaderStatus(); diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs index 3973f1e86f..e5fbfa1a81 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs @@ -19,7 +19,7 @@ namespace Avalonia.Controls private Rectangle _rightGridLine; private DataGridColumn _owningColumn; - bool _isValid; + bool _isValid = true; public static readonly DirectProperty IsValidProperty = AvaloniaProperty.RegisterDirect( @@ -180,7 +180,18 @@ namespace Avalonia.Controls internal void UpdatePseudoClasses() { + if (OwningGrid == null || OwningColumn == null || OwningRow == null || !OwningRow.IsVisible || OwningRow.Slot == -1) + { + return; + } + + PseudoClasses.Set(":selected", OwningRow.IsSelected); + + PseudoClasses.Set(":current", IsCurrent); + + PseudoClasses.Set(":edited", IsEdited); + PseudoClasses.Set(":invalid", !IsValid); } // Makes sure the right gridline has the proper stroke and visibility. If lastVisibleColumn is specified, the diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index e6cc7e5e40..017718bc92 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -13,6 +13,7 @@ using System.Diagnostics; using Avalonia.Utilities; using System; using Avalonia.Controls.Utils; +using Avalonia.Controls.Mixins; namespace Avalonia.Controls { @@ -39,7 +40,7 @@ namespace Avalonia.Controls private static Cursor _originalCursor; private static double _originalHorizontalOffset; private static double _originalWidth; - private bool _desiredSeparatorVisibility; + private bool _desiredSeparatorVisibility = true; private static Point? _dragStart; private static DataGridColumn _dragColumn; private static double _frozenColumnsWidth; @@ -68,6 +69,7 @@ namespace Avalonia.Controls static DataGridColumnHeader() { AreSeparatorsVisibleProperty.Changed.AddClassHandler((x, e) => x.OnAreSeparatorsVisibleChanged(e)); + PressedMixin.Attach(); } /// @@ -149,8 +151,7 @@ namespace Avalonia.Controls } } - //TODO Implement - internal void ApplyState() + internal void UpdatePseudoClasses() { CurrentSortingState = null; if (OwningGrid != null @@ -441,7 +442,7 @@ namespace Avalonia.Controls Point mousePosition = e.GetPosition(this); OnMouseEnter(mousePosition); - ApplyState(); + UpdatePseudoClasses(); } private void DataGridColumnHeader_PointerLeave(object sender, PointerEventArgs e) @@ -452,12 +453,12 @@ namespace Avalonia.Controls } OnMouseLeave(); - ApplyState(); + UpdatePseudoClasses(); } private void DataGridColumnHeader_PointerPressed(object sender, PointerPressedEventArgs e) { - if (OwningColumn == null || e.Handled || !IsEnabled || e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (OwningColumn == null || e.Handled || !IsEnabled || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { return; } @@ -467,7 +468,7 @@ namespace Avalonia.Controls OnMouseLeftButtonDown(ref handled, e, mousePosition); e.Handled = handled; - ApplyState(); + UpdatePseudoClasses(); } private void DataGridColumnHeader_PointerReleased(object sender, PointerReleasedEventArgs e) @@ -483,7 +484,7 @@ namespace Avalonia.Controls OnMouseLeftButtonUp(ref handled, e, mousePosition, mousePositionHeaders); e.Handled = handled; - ApplyState(); + UpdatePseudoClasses(); } private void DataGridColumnHeader_PointerMove(object sender, PointerEventArgs e) diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index e21ee3be37..19539bf032 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -610,7 +610,7 @@ namespace Avalonia.Controls // refresh sort description foreach (DataGridColumn column in _owner.ColumnsItemsInternal) { - column.HeaderCell.ApplyState(); + column.HeaderCell.UpdatePseudoClasses(); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index d5115c983a..d5ce8dba75 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -624,17 +624,17 @@ namespace Avalonia.Controls { if (_headerElement != null && OwningGrid.AreRowHeadersVisible) { - _headerElement.ApplyOwnerStatus(); + _headerElement.UpdatePseudoClasses(); } } - //TODO Implement internal void UpdatePseudoClasses() { - PseudoClasses.Set(":selected", IsSelected); - PseudoClasses.Set(":editing", IsEditing); if (RootElement != null && OwningGrid != null && IsVisible) { + PseudoClasses.Set(":selected", IsSelected); + PseudoClasses.Set(":editing", IsEditing); + PseudoClasses.Set(":invalid", !IsValid); ApplyHeaderStatus(); } } @@ -789,7 +789,7 @@ namespace Avalonia.Controls private void DataGridRow_PointerPressed(PointerPressedEventArgs e) { - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { return; } @@ -917,23 +917,23 @@ namespace Avalonia.Controls //TODO Cleanup double? _previousDetailsHeight = null; - + //TODO Animation - private void DetailsContent_SizeChanged(Rect newValue) + private void DetailsContent_HeightChanged(double newValue) { if (_previousDetailsHeight.HasValue) { var oldValue = _previousDetailsHeight.Value; - _previousDetailsHeight = newValue.Height; - if (newValue.Height != oldValue && newValue.Height != _detailsDesiredHeight) + _previousDetailsHeight = newValue; + if (newValue != oldValue && newValue != _detailsDesiredHeight) { if (AreDetailsVisible && _appliedDetailsTemplate != null) { // Update the new desired height for RowDetails - _detailsDesiredHeight = newValue.Height; + _detailsDesiredHeight = newValue; - _detailsElement.ContentHeight = newValue.Height; + _detailsElement.ContentHeight = newValue; // Calling this when details are not visible invalidates during layout when we have no work // to do. In certain scenarios, this could cause a layout cycle @@ -943,19 +943,29 @@ namespace Avalonia.Controls } else { - _previousDetailsHeight = newValue.Height; + _previousDetailsHeight = newValue; } } - private void DetailsContent_BoundsChanged(Rect newValue) + + private void DetailsContent_SizeChanged(Rect newValue) { - if(_detailsContent != null) - DetailsContent_SizeChanged(newValue.Inflate(_detailsContent.Margin)); + DetailsContent_HeightChanged(newValue.Height); } private void DetailsContent_MarginChanged(Thickness newValue) { if (_detailsContent != null) DetailsContent_SizeChanged(_detailsContent.Bounds.Inflate(newValue)); } + private void DetailsContent_LayoutUpdated(object sender, EventArgs e) + { + if (_detailsContent != null) + { + var margin = _detailsContent.Margin; + var height = _detailsContent.DesiredSize.Height + margin.Top + margin.Bottom; + + DetailsContent_HeightChanged(height); + } + } //TODO Animation // Sets AreDetailsVisible on the row and animates if necessary @@ -1035,12 +1045,26 @@ namespace Avalonia.Controls if (_detailsContent != null) { - _detailsContentSizeSubscription = - System.Reactive.Disposables.StableCompositeDisposable.Create( - _detailsContent.GetObservable(BoundsProperty) - .Subscribe(DetailsContent_BoundsChanged), + if (_detailsContent is Layout.Layoutable layoutableContent) + { + layoutableContent.LayoutUpdated += DetailsContent_LayoutUpdated; + + _detailsContentSizeSubscription = + System.Reactive.Disposables.StableCompositeDisposable.Create( + System.Reactive.Disposables.Disposable.Create(() => layoutableContent.LayoutUpdated -= DetailsContent_LayoutUpdated), + _detailsContent.GetObservable(MarginProperty) + .Subscribe(DetailsContent_MarginChanged)); + + + } + else + { + _detailsContentSizeSubscription = _detailsContent.GetObservable(MarginProperty) - .Subscribe(DetailsContent_MarginChanged)); + .Subscribe(DetailsContent_MarginChanged); + + } + _detailsElement.Children.Add(_detailsContent); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs index 0790fcf5d5..0833247439 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs @@ -3,6 +3,7 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Media; @@ -96,6 +97,7 @@ namespace Avalonia.Controls static DataGridRowGroupHeader() { SublevelIndentProperty.Changed.AddClassHandler((x,e) => x.OnSublevelIndentChanged(e)); + PressedMixin.Attach(); } /// @@ -205,14 +207,18 @@ namespace Avalonia.Controls { if (_headerElement != null && OwningGrid.AreRowHeadersVisible) { - _headerElement.ApplyOwnerStatus(); + _headerElement.UpdatePseudoClasses(); } } - //TODO Implement - internal void ApplyState(bool useTransitions) + internal void UpdatePseudoClasses() { + PseudoClasses.Set(":current", IsCurrent); + if (RowGroupInfo?.CollectionViewGroup != null) + { + PseudoClasses.Set(":expanded", RowGroupInfo.IsVisible && RowGroupInfo.CollectionViewGroup.ItemCount > 0); + } } protected override Size ArrangeOverride(Size finalSize) @@ -328,7 +334,7 @@ namespace Avalonia.Controls { if (_headerElement != null && OwningGrid != null) { - _headerElement.IsVisible = OwningGrid.AreColumnHeadersVisible; + _headerElement.IsVisible = OwningGrid.AreRowHeadersVisible; } } @@ -344,7 +350,7 @@ namespace Avalonia.Controls { EnsureExpanderButtonIsChecked(); EnsureHeaderVisibility(); - ApplyState(useTransitions: false); + UpdatePseudoClasses(); ApplyHeaderStatus(); } @@ -353,7 +359,7 @@ namespace Avalonia.Controls if (IsEnabled) { IsMouseOver = true; - ApplyState(useTransitions: true); + UpdatePseudoClasses(); } base.OnPointerEnter(e); @@ -364,7 +370,7 @@ namespace Avalonia.Controls if (IsEnabled) { IsMouseOver = false; - ApplyState(useTransitions: true); + UpdatePseudoClasses(); } base.OnPointerLeave(e); @@ -402,7 +408,7 @@ namespace Avalonia.Controls EnsureExpanderButtonIsChecked(); - ApplyState(true /*useTransitions*/); + UpdatePseudoClasses(); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs index 324227d749..8f8b1742ba 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls.Primitives /// public class DataGridRowHeader : ContentControl { - private const string DATAGRIDROWHEADER_elementRootName = "Root"; + private const string DATAGRIDROWHEADER_elementRootName = "PART_Root"; private const double DATAGRIDROWHEADER_separatorThickness = 1; private Control _rootElement; @@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives _rootElement = e.NameScope.Find(DATAGRIDROWHEADER_elementRootName); if (_rootElement != null) { - ApplyOwnerStatus(); + UpdatePseudoClasses(); } } @@ -131,12 +131,27 @@ namespace Avalonia.Controls.Primitives return measuredSize; } - //TODO Implement - internal void ApplyOwnerStatus() + internal void UpdatePseudoClasses() { if (_rootElement != null && Owner != null && Owner.IsVisible) { + if (OwningRow != null) + { + PseudoClasses.Set(":invalid", !OwningRow.IsValid); + + PseudoClasses.Set(":selected", OwningRow.IsSelected); + + PseudoClasses.Set(":editing", OwningRow.IsEditing); + if (OwningGrid != null) + { + PseudoClasses.Set(":current", OwningRow.Slot == OwningGrid.CurrentSlot); + } + } + else if (OwningRowGroupHeader != null && OwningGrid != null) + { + PseudoClasses.Set(":current", OwningRowGroupHeader.RowGroupInfo.Slot == OwningGrid.CurrentSlot); + } } } @@ -162,7 +177,7 @@ namespace Avalonia.Controls.Primitives //TODO TabStop private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEventArgs e) { - if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) { return; } diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 924156f5f4..a69b8eafe1 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -1912,7 +1912,7 @@ namespace Avalonia.Controls { // Assume it's a RowGroupHeader DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader; - groupHeader.ApplyState(useTransitions: true); + groupHeader.UpdatePseudoClasses(); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs index 589cdc253c..d31204b9e6 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs @@ -9,6 +9,7 @@ using Avalonia.Media; using System; using System.ComponentModel; using Avalonia.Layout; +using Avalonia.Markup.Xaml.MarkupExtensions; namespace Avalonia.Controls { @@ -17,6 +18,7 @@ namespace Avalonia.Controls /// public class DataGridTextColumn : DataGridBoundColumn { + private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin"; private double? _fontSize; private FontStyle? _fontStyle; @@ -186,7 +188,7 @@ namespace Avalonia.Controls { TextBlock textBlockElement = new TextBlock { - Margin = new Thickness(4), + [!Layoutable.MarginProperty] = new DynamicResourceExtension(DATAGRID_TextColumnCellTextBlockMarginKey), VerticalAlignment = VerticalAlignment.Center }; diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 738732f671..cf062e0920 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -1,5 +1,11 @@ - + + + + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt new file mode 100644 index 0000000000..11708b360f --- /dev/null +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -0,0 +1,18 @@ +Compat issues with assembly Avalonia.Controls: +TypesMustExist : Type 'Avalonia.Controls.IndexPath' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.ISelectedItemInfo' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.ISelectionModel' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.ListBox.SelectionProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.ListBox.Selection.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TreeView.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. +Total Issues: 16 diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index d69052ad3d..f616a42cac 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -88,6 +88,23 @@ namespace Avalonia.Controls }; } + /// + /// Begin configuring an . + /// + /// Factory function for . + /// The subclass of to configure. + /// is useful for passing of dependencies to . + /// An instance. + public static TAppBuilder Configure(Func appFactory) + where TApp : Application + { + return new TAppBuilder() + { + ApplicationType = typeof(TApp), + _appFactory = appFactory + }; + } + protected TAppBuilder Self => (TAppBuilder)this; public TAppBuilder AfterSetup(Action callback) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 31101dc0f1..c164f282e8 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -1647,7 +1647,7 @@ namespace Avalonia.Controls /// /// The source object. /// The event data. - private void DropDownPopup_Closed(object sender, PopupClosedEventArgs e) + private void DropDownPopup_Closed(object sender, EventArgs e) { // Force the drop down dependency property to be false. if (IsDropDownOpen) @@ -1655,11 +1655,6 @@ namespace Avalonia.Controls IsDropDownOpen = false; } - if (e.CloseEvent is PointerEventArgs pointerEvent) - { - pointerEvent.Handled = true; - } - // Fire the DropDownClosed event if (_popupHasOpened) { diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 32331d29ab..7f1f4bc8f3 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,7 +1,10 @@  - netstandard2.0 + netstandard2.0 + + + @@ -14,4 +17,5 @@ + diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index b987f065be..046b55d49a 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -889,17 +889,12 @@ namespace Avalonia.Controls _ignoreButtonClick = false; } } - private void PopUp_Closed(object sender, PopupClosedEventArgs e) + private void PopUp_Closed(object sender, EventArgs e) { IsDropDownOpen = false; if(!_isPopupClosing) { - if (e.CloseEvent is PointerEventArgs pointerEvent) - { - pointerEvent.Handled = true; - } - _isPopupClosing = true; Threading.Dispatcher.UIThread.InvokeAsync(() => _isPopupClosing = false); } diff --git a/src/Avalonia.Controls/Chrome/CaptionButtons.cs b/src/Avalonia.Controls/Chrome/CaptionButtons.cs index 75d6c366b8..a86cbc271b 100644 --- a/src/Avalonia.Controls/Chrome/CaptionButtons.cs +++ b/src/Avalonia.Controls/Chrome/CaptionButtons.cs @@ -38,12 +38,10 @@ namespace Avalonia.Controls.Chrome { if (_disposables != null) { - var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow); - - layer?.Children.Remove(this); - _disposables.Dispose(); _disposables = null; + + _hostWindow = null; } } diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs index 78b49d2a03..c0c8076dd8 100644 --- a/src/Avalonia.Controls/Chrome/TitleBar.cs +++ b/src/Avalonia.Controls/Chrome/TitleBar.cs @@ -12,106 +12,86 @@ namespace Avalonia.Controls.Chrome public class TitleBar : TemplatedControl { private CompositeDisposable? _disposables; - private readonly Window? _hostWindow; private CaptionButtons? _captionButtons; - public TitleBar(Window hostWindow) + private void UpdateSize(Window window) { - _hostWindow = hostWindow; - } - - public TitleBar() - { - - } - - public void Attach() - { - if (_disposables == null) - { - var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow); - - layer?.Children.Add(this); - - if (_hostWindow != null) - { - _disposables = new CompositeDisposable - { - _hostWindow.GetObservable(Window.WindowDecorationMarginProperty) - .Subscribe(x => UpdateSize()), - - _hostWindow.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty) - .Subscribe(x => UpdateSize()), - - _hostWindow.GetObservable(Window.OffScreenMarginProperty) - .Subscribe(x => UpdateSize()), - - _hostWindow.GetObservable(Window.WindowStateProperty) - .Subscribe(x => - { - PseudoClasses.Set(":minimized", x == WindowState.Minimized); - PseudoClasses.Set(":normal", x == WindowState.Normal); - PseudoClasses.Set(":maximized", x == WindowState.Maximized); - PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen); - }) - }; - - _captionButtons?.Attach(_hostWindow); - } - - UpdateSize(); - } - } - - private void UpdateSize() - { - if (_hostWindow != null) + if (window != null) { Margin = new Thickness( - _hostWindow.OffScreenMargin.Left, - _hostWindow.OffScreenMargin.Top, - _hostWindow.OffScreenMargin.Right, - _hostWindow.OffScreenMargin.Bottom); + window.OffScreenMargin.Left, + window.OffScreenMargin.Top, + window.OffScreenMargin.Right, + window.OffScreenMargin.Bottom); - if (_hostWindow.WindowState != WindowState.FullScreen) + if (window.WindowState != WindowState.FullScreen) { - Height = _hostWindow.WindowDecorationMargin.Top; + Height = window.WindowDecorationMargin.Top; if (_captionButtons != null) { _captionButtons.Height = Height; } } + + IsVisible = window.PlatformImpl.NeedsManagedDecorations; } } - public void Detach() + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { - if (_disposables != null) - { - var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow); - - layer?.Children.Remove(this); + base.OnApplyTemplate(e); - _disposables.Dispose(); - _disposables = null; + _captionButtons?.Detach(); + + _captionButtons = e.NameScope.Get("PART_CaptionButtons"); - _captionButtons?.Detach(); + if (VisualRoot is Window window) + { + _captionButtons?.Attach(window); + + UpdateSize(window); } } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - base.OnApplyTemplate(e); - - _captionButtons = e.NameScope.Get("PART_CaptionButtons"); + base.OnAttachedToVisualTree(e); - if (_hostWindow != null) + if (VisualRoot is Window window) { - _captionButtons.Attach(_hostWindow); + _disposables = new CompositeDisposable + { + window.GetObservable(Window.WindowDecorationMarginProperty) + .Subscribe(x => UpdateSize(window)), + window.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty) + .Subscribe(x => UpdateSize(window)), + window.GetObservable(Window.OffScreenMarginProperty) + .Subscribe(x => UpdateSize(window)), + window.GetObservable(Window.ExtendClientAreaChromeHintsProperty) + .Subscribe(x => UpdateSize(window)), + window.GetObservable(Window.WindowStateProperty) + .Subscribe(x => + { + PseudoClasses.Set(":minimized", x == WindowState.Minimized); + PseudoClasses.Set(":normal", x == WindowState.Normal); + PseudoClasses.Set(":maximized", x == WindowState.Maximized); + PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen); + }), + window.GetObservable(Window.IsExtendedIntoWindowDecorationsProperty) + .Subscribe(x => UpdateSize(window)) + }; } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); - UpdateSize(); + _disposables?.Dispose(); + + _captionButtons?.Detach(); + _captionButtons = null; } } } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 75a0e41e7b..27313b0b4c 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -290,24 +290,6 @@ namespace Avalonia.Controls _popup = e.NameScope.Get("PART_Popup"); _popup.Opened += PopupOpened; - _popup.Closed += PopupClosed; - } - - /// - /// Called when the ComboBox popup is closed, with the - /// that caused the popup to close. - /// - /// The event args. - /// - /// This method can be overridden to control whether the event that caused the popup to close - /// is swallowed or passed through. - /// - protected virtual void PopupClosedOverride(PopupClosedEventArgs e) - { - if (e.CloseEvent is PointerEventArgs pointerEvent) - { - pointerEvent.Handled = true; - } } internal void ItemFocused(ComboBoxItem dropDownItem) @@ -318,13 +300,11 @@ namespace Avalonia.Controls } } - private void PopupClosed(object sender, PopupClosedEventArgs e) + private void PopupClosed(object sender, EventArgs e) { _subscriptionsOnOpen?.Dispose(); _subscriptionsOnOpen = null; - PopupClosedOverride(e); - if (CanFocus(this)) { Focus(); diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 5929dd39d4..c4df5c1815 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -265,7 +265,8 @@ namespace Avalonia.Controls PlacementMode = PlacementMode, PlacementRect = PlacementRect, PlacementTarget = PlacementTarget ?? control, - StaysOpen = false + IsLightDismissEnabled = true, + OverlayDismissEventPassThrough = true, }; _popup.Opened += PopupOpened; @@ -278,10 +279,14 @@ namespace Avalonia.Controls ((ISetLogicalParent)_popup).SetParent(control); } - _popup.Child = this; - _popup.IsOpen = true; + if (PlacementTarget is null && _popup.PlacementTarget != control) + { + _popup.PlacementTarget = control; + } + _popup.Child = this; IsOpen = true; + _popup.IsOpen = true; RaiseEvent(new RoutedEventArgs { diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs index 5d3311e8c6..a41c159980 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs @@ -88,7 +88,7 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(SelectedDate), x => x.SelectedDate, (x, v) => x.SelectedDate = v); - //Template Items + // Template Items private Button _flyoutButton; private TextBlock _dayText; private TextBlock _monthText; @@ -359,10 +359,14 @@ namespace Avalonia.Controls } } - Grid.SetColumn(_spacer1, 1); - Grid.SetColumn(_spacer2, 3); - _spacer1.IsVisible = columnIndex > 1; - _spacer2.IsVisible = columnIndex > 2; + var isSpacer1Visible = columnIndex > 1; + var isSpacer2Visible = columnIndex > 2; + // ternary conditional operator is used to make sure grid cells will be validated + Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0); + Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0); + + _spacer1.IsVisible = isSpacer1Visible; + _spacer2.IsVisible = isSpacer2Visible; } private void SetSelectedDateText() @@ -398,7 +402,7 @@ namespace Avalonia.Controls var deltaY = _presenter.GetOffsetForPopup(); - //The extra 5 px I think is related to default popup placement behavior + // The extra 5 px I think is related to default popup placement behavior _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5), Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom, Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY); diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs index 8b86e46e88..eec4615736 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs @@ -77,7 +77,7 @@ namespace Avalonia.Controls DatePicker.YearVisibleProperty.AddOwner(x => x.YearVisible, (x, v) => x.YearVisible = v); - //Template Items + // Template Items private Grid _pickerContainer; private Button _acceptButton; private Button _dismissButton; @@ -107,7 +107,7 @@ namespace Avalonia.Controls private bool _yearVisible = true; private DateTimeOffset _syncDate; - private GregorianCalendar _calendar; + private readonly GregorianCalendar _calendar; private bool _suppressUpdateSelection; public DatePickerPresenter() @@ -234,7 +234,7 @@ namespace Avalonia.Controls protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - //These are requirements, so throw if not found + // These are requirements, so throw if not found _pickerContainer = e.NameScope.Get("PickerContainer"); _monthHost = e.NameScope.Get("MonthHost"); _dayHost = e.NameScope.Get("DayHost"); @@ -326,7 +326,7 @@ namespace Avalonia.Controls /// private void InitPicker() { - //OnApplyTemplate must've been called before we can init here... + // OnApplyTemplate must've been called before we can init here... if (_pickerContainer == null) return; @@ -344,12 +344,11 @@ namespace Avalonia.Controls SetGrid(); - //Date should've been set when we reach this point + // Date should've been set when we reach this point var dt = Date; if (DayVisible) { - GregorianCalendar gc = new GregorianCalendar(); - var maxDays = gc.GetDaysInMonth(dt.Year, dt.Month); + var maxDays = _calendar.GetDaysInMonth(dt.Year, dt.Month); _daySelector.MaximumValue = maxDays; _daySelector.MinimumValue = 1; _daySelector.SelectedValue = dt.Day; @@ -357,10 +356,17 @@ namespace Avalonia.Controls } if (MonthVisible) + { _monthSelector.SelectedValue = dt.Month; - + _monthSelector.FormatDate = dt.Date; + } + if (YearVisible) + { _yearSelector.SelectedValue = dt.Year; + _yearSelector.FormatDate = dt.Date; + } + _suppressUpdateSelection = false; SetInitialFocus(); @@ -407,10 +413,14 @@ namespace Avalonia.Controls } } - Grid.SetColumn(_spacer1, 1); - Grid.SetColumn(_spacer2, 3); - _spacer1.IsVisible = columnIndex > 1; - _spacer2.IsVisible = columnIndex > 2; + var isSpacer1Visible = columnIndex > 1; + var isSpacer2Visible = columnIndex > 2; + // ternary conditional operator is used to make sure grid cells will be validated + Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0); + Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0); + + _spacer1.IsVisible = isSpacer1Visible; + _spacer2.IsVisible = isSpacer2Visible; } private void SetInitialFocus() @@ -433,12 +443,12 @@ namespace Avalonia.Controls } } - private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnDismissButtonClicked(object sender, RoutedEventArgs e) { OnDismiss(); } - private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnAcceptButtonClicked(object sender, RoutedEventArgs e) { Date = _syncDate; OnConfirmed(); @@ -471,7 +481,7 @@ namespace Avalonia.Controls _syncDate = newDate; - //We don't need to update the days if not displaying day, not february + // We don't need to update the days if not displaying day, not february if (!DayVisible || _syncDate.Month != 2) return; diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index 3fa426b1d3..e54da1fb3a 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly DirectProperty MinuteIncrementProperty = - AvaloniaProperty.RegisterDirect(nameof(MinuteIncrement), + AvaloniaProperty.RegisterDirect(nameof(MinuteIncrement), x => x.MinuteIncrement, (x, v) => x.MinuteIncrement = v); /// @@ -34,17 +34,17 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly DirectProperty ClockIdentifierProperty = - AvaloniaProperty.RegisterDirect(nameof(ClockIdentifier), + AvaloniaProperty.RegisterDirect(nameof(ClockIdentifier), x => x.ClockIdentifier, (x, v) => x.ClockIdentifier = v); /// /// Defines the property /// public static readonly DirectProperty SelectedTimeProperty = - AvaloniaProperty.RegisterDirect(nameof(SelectedTime), + AvaloniaProperty.RegisterDirect(nameof(SelectedTime), x => x.SelectedTime, (x, v) => x.SelectedTime = v); - //Template Items + // Template Items private TimePickerPresenter _presenter; private Button _flyoutButton; private Border _firstPickerHost; @@ -52,7 +52,7 @@ namespace Avalonia.Controls private Border _thirdPickerHost; private TextBlock _hourText; private TextBlock _minuteText; - public TextBlock _periodText; + private TextBlock _periodText; private Rectangle _firstSplitter; private Rectangle _secondSplitter; private Grid _contentGrid; @@ -145,7 +145,7 @@ namespace Avalonia.Controls if (_flyoutButton != null) _flyoutButton.Click -= OnFlyoutButtonClicked; - if(_presenter != null) + if (_presenter != null) { _presenter.Confirmed -= OnConfirmed; _presenter.Dismissed -= OnDismissPicker; @@ -170,7 +170,6 @@ namespace Avalonia.Controls _popup = e.NameScope.Find("Popup"); _presenter = e.NameScope.Find("PickerPresenter"); - if (_flyoutButton != null) _flyoutButton.Click += OnFlyoutButtonClicked; @@ -185,7 +184,6 @@ namespace Avalonia.Controls _presenter[!TimePickerPresenter.MinuteIncrementProperty] = this[!MinuteIncrementProperty]; _presenter[!TimePickerPresenter.ClockIdentifierProperty] = this[!ClockIdentifierProperty]; } - } private void SetGrid() @@ -195,30 +193,19 @@ namespace Avalonia.Controls bool use24HourClock = ClockIdentifier == "24HourClock"; - if (!use24HourClock) - { - _contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*"); - _thirdPickerHost.IsVisible = true; - _secondSplitter.IsVisible = true; + var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *"; + _contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD); - Grid.SetColumn(_firstPickerHost, 0); - Grid.SetColumn(_secondPickerHost, 2); - Grid.SetColumn(_thirdPickerHost, 4); + _thirdPickerHost.IsVisible = !use24HourClock; + _secondSplitter.IsVisible = !use24HourClock; - Grid.SetColumn(_firstSplitter, 1); - Grid.SetColumn(_secondSplitter, 3); - } - else - { - _contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*"); - _thirdPickerHost.IsVisible = false; - _secondSplitter.IsVisible = false; + Grid.SetColumn(_firstPickerHost, 0); + Grid.SetColumn(_secondPickerHost, 2); - Grid.SetColumn(_firstPickerHost, 0); - Grid.SetColumn(_secondPickerHost, 2); + Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4); - Grid.SetColumn(_firstSplitter, 1); - } + Grid.SetColumn(_firstSplitter, 1); + Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3); } private void SetSelectedTimeText() @@ -237,14 +224,13 @@ namespace Avalonia.Controls hr = hr > 12 ? hr - 12 : hr == 0 ? 12 : hr; newTime = new TimeSpan(hr, newTime.Minutes, 0); } - _hourText.Text = newTime.ToString("%h"); + _hourText.Text = newTime.ToString("%h"); _minuteText.Text = newTime.ToString("mm"); PseudoClasses.Set(":hasnotime", false); _periodText.Text = time.Value.Hours >= 12 ? CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator : CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator; - } else { @@ -262,7 +248,7 @@ namespace Avalonia.Controls SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime)); } - private void OnFlyoutButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e) { _presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay; @@ -270,7 +256,7 @@ namespace Avalonia.Controls var deltaY = _presenter.GetOffsetForPopup(); - //The extra 5 px I think is related to default popup placement behavior + // The extra 5 px I think is related to default popup placement behavior _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5), Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom, Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY); @@ -287,6 +273,5 @@ namespace Avalonia.Controls _popup.Close(); SelectedTime = _presenter.Time; } - } } diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs index 5ab5b92e16..920eeeb406 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs @@ -39,7 +39,7 @@ namespace Avalonia.Controls KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle); } - //TemplateItems + // TemplateItems private Grid _pickerContainer; private Button _acceptButton; private Button _dismissButton; @@ -55,8 +55,8 @@ namespace Avalonia.Controls private Button _minuteDownButton; private Button _periodDownButton; - //Backing Fields - private TimeSpan _Time; + // Backing Fields + private TimeSpan _time; private int _minuteIncrement = 1; private string _clockIdentifier = "12HourClock"; @@ -83,7 +83,7 @@ namespace Avalonia.Controls get => _clockIdentifier; set { - if (string.IsNullOrEmpty(value) || value == "" || !(value == "12HourClock" || value == "24HourClock")) + if (string.IsNullOrEmpty(value) || !(value == "12HourClock" || value == "24HourClock")) throw new ArgumentException("Invalid ClockIdentifier"); SetAndRaise(ClockIdentifierProperty, ref _clockIdentifier, value); InitPicker(); @@ -95,10 +95,10 @@ namespace Avalonia.Controls /// public TimeSpan Time { - get => _Time; + get => _time; set { - SetAndRaise(TimeProperty, ref _Time, value); + SetAndRaise(TimeProperty, ref _time, value); InitPicker(); } } @@ -213,26 +213,24 @@ namespace Avalonia.Controls private void SetGrid() { - if (ClockIdentifier == "12HourClock") - { - _pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*"); - _spacer2.IsVisible = true; - _periodHost.IsVisible = true; - } - else - { - _pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*"); - _spacer2.IsVisible = false; - _periodHost.IsVisible = false; - } + bool use24HourClock = ClockIdentifier == "24HourClock"; + + var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *"; + _pickerContainer.ColumnDefinitions = new ColumnDefinitions(columnsD); + + _spacer2.IsVisible = !use24HourClock; + _periodHost.IsVisible = !use24HourClock; + + Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3); + Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4); } - private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnDismissButtonClicked(object sender, RoutedEventArgs e) { OnDismiss(); } - private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void OnAcceptButtonClicked(object sender, RoutedEventArgs e) { OnConfirmed(); } diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index f8bd2878d9..522103c7bd 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -35,7 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen } } - public double Scaling + public double RenderScaling { get { return _scaling; } set diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs index cd1ce3deae..9e65ef5f81 100644 --- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs @@ -142,7 +142,6 @@ namespace Avalonia.Controls.Generators private readonly IDataTemplate _inner; public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner; public IControl Build(object param) => _inner.Build(param); - public bool SupportsRecycling => _inner.SupportsRecycling; public bool Match(object data) => _inner.Match(data); public InstancedBinding ItemsSelector(object item) => null; } diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs index ee9d0fd6b6..426f265084 100644 --- a/src/Avalonia.Controls/IMenuElement.cs +++ b/src/Avalonia.Controls/IMenuElement.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using Avalonia.Input; +#nullable enable + namespace Avalonia.Controls { /// @@ -11,7 +13,7 @@ namespace Avalonia.Controls /// /// Gets or sets the currently selected submenu item. /// - IMenuItem SelectedItem { get; set; } + IMenuItem? SelectedItem { get; set; } /// /// Gets the submenu items. diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 132d565cb7..94d761f725 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Controls +#nullable enable + +namespace Avalonia.Controls { /// /// Represents a . @@ -29,7 +31,7 @@ /// /// Gets the parent . /// - new IMenuElement Parent { get; } + new IMenuElement? Parent { get; } /// /// Raises a click event on the menu item. diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs deleted file mode 100644 index 6570921c03..0000000000 --- a/src/Avalonia.Controls/ISelectionModel.cs +++ /dev/null @@ -1,249 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections.Generic; -using System.ComponentModel; - -namespace Avalonia.Controls -{ - /// - /// Holds the selected items for a control. - /// - public interface ISelectionModel : INotifyPropertyChanged - { - /// - /// Gets or sets the anchor index. - /// - IndexPath AnchorIndex { get; set; } - - /// - /// Gets or set the index of the first selected item. - /// - IndexPath SelectedIndex { get; set; } - - /// - /// Gets or set the indexes of the selected items. - /// - IReadOnlyList SelectedIndices { get; } - - /// - /// Gets the first selected item. - /// - object SelectedItem { get; } - - /// - /// Gets the selected items. - /// - IReadOnlyList SelectedItems { get; } - - /// - /// Gets a value indicating whether the model represents a single or multiple selection. - /// - bool SingleSelect { get; set; } - - /// - /// Gets a value indicating whether to always keep an item selected where possible. - /// - bool AutoSelect { get; set; } - - /// - /// Gets or sets the collection that contains the items that can be selected. - /// - object Source { get; set; } - - /// - /// Raised when the children of a selection are required. - /// - event EventHandler ChildrenRequested; - - /// - /// Raised when the selection has changed. - /// - event EventHandler SelectionChanged; - - /// - /// Clears the selection. - /// - void ClearSelection(); - - /// - /// Deselects an item. - /// - /// The index of the item. - void Deselect(int index); - - /// - /// Deselects an item. - /// - /// The index of the item group. - /// The index of the item in the group. - void Deselect(int groupIndex, int itemIndex); - - /// - /// Deselects an item. - /// - /// The index of the item. - void DeselectAt(IndexPath index); - - /// - /// Deselects a range of items. - /// - /// The start index of the range. - /// The end index of the range. - void DeselectRange(IndexPath start, IndexPath end); - - /// - /// Deselects a range of items, starting at . - /// - /// The end index of the range. - void DeselectRangeFromAnchor(int index); - - /// - /// Deselects a range of items, starting at . - /// - /// - /// The index of the item group that represents the end of the selection. - /// - /// - /// The index of the item in the group that represents the end of the selection. - /// - void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex); - - /// - /// Deselects a range of items, starting at . - /// - /// The end index of the range. - void DeselectRangeFromAnchorTo(IndexPath index); - - /// - /// Disposes the object and clears the selection. - /// - void Dispose(); - - /// - /// Checks whether an item is selected. - /// - /// The index of the item - bool IsSelected(int index); - - /// - /// Checks whether an item is selected. - /// - /// The index of the item group. - /// The index of the item in the group. - bool IsSelected(int groupIndex, int itemIndex); - - /// - /// Checks whether an item is selected. - /// - /// The index of the item - public bool IsSelectedAt(IndexPath index); - - /// - /// Checks whether an item or its descendents are selected. - /// - /// The index of the item - /// - /// True if the item and all its descendents are selected, false if the item and all its - /// descendents are deselected, or null if a combination of selected and deselected. - /// - bool? IsSelectedWithPartial(int index); - - /// - /// Checks whether an item or its descendents are selected. - /// - /// The index of the item group. - /// The index of the item in the group. - /// - /// True if the item and all its descendents are selected, false if the item and all its - /// descendents are deselected, or null if a combination of selected and deselected. - /// - bool? IsSelectedWithPartial(int groupIndex, int itemIndex); - - /// - /// Checks whether an item or its descendents are selected. - /// - /// The index of the item - /// - /// True if the item and all its descendents are selected, false if the item and all its - /// descendents are deselected, or null if a combination of selected and deselected. - /// - bool? IsSelectedWithPartialAt(IndexPath index); - - /// - /// Selects an item. - /// - /// The index of the item - void Select(int index); - - /// - /// Selects an item. - /// - /// The index of the item group. - /// The index of the item in the group. - void Select(int groupIndex, int itemIndex); - - /// - /// Selects an item. - /// - /// The index of the item - void SelectAt(IndexPath index); - - /// - /// Selects all items. - /// - void SelectAll(); - - /// - /// Selects a range of items. - /// - /// The start index of the range. - /// The end index of the range. - void SelectRange(IndexPath start, IndexPath end); - - /// - /// Selects a range of items, starting at . - /// - /// The end index of the range. - void SelectRangeFromAnchor(int index); - - /// - /// Selects a range of items, starting at . - /// - /// - /// The index of the item group that represents the end of the selection. - /// - /// - /// The index of the item in the group that represents the end of the selection. - /// - void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex); - - /// - /// Selects a range of items, starting at . - /// - /// The end index of the range. - void SelectRangeFromAnchorTo(IndexPath index); - - /// - /// Sets the . - /// - /// The anchor index. - void SetAnchorIndex(int index); - - /// - /// Sets the . - /// - /// The index of the item group. - /// The index of the item in the group. - void SetAnchorIndex(int groupIndex, int index); - - /// - /// Begins a batch update of the selection. - /// - /// An that finishes the batch update. - IDisposable Update(); - } -} diff --git a/src/Avalonia.Controls/IndexPath.cs b/src/Avalonia.Controls/IndexPath.cs deleted file mode 100644 index 73b75bc23d..0000000000 --- a/src/Avalonia.Controls/IndexPath.cs +++ /dev/null @@ -1,200 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections.Generic; -using System.Linq; - -#nullable enable - -namespace Avalonia.Controls -{ - public readonly struct IndexPath : IComparable, IEquatable - { - public static readonly IndexPath Unselected = default; - - private readonly int _index; - private readonly int[]? _path; - - public IndexPath(int index) - { - _index = index + 1; - _path = null; - } - - public IndexPath(int groupIndex, int itemIndex) - { - _index = 0; - _path = new[] { groupIndex, itemIndex }; - } - - public IndexPath(IEnumerable? indices) - { - if (indices != null) - { - _index = 0; - _path = indices.ToArray(); - } - else - { - _index = 0; - _path = null; - } - } - - private IndexPath(int[] basePath, int index) - { - basePath = basePath ?? throw new ArgumentNullException(nameof(basePath)); - - _index = 0; - _path = new int[basePath.Length + 1]; - Array.Copy(basePath, _path, basePath.Length); - _path[basePath.Length] = index; - } - - public int GetSize() => _path?.Length ?? (_index == 0 ? 0 : 1); - - public int GetAt(int index) - { - if (index >= GetSize()) - { - throw new IndexOutOfRangeException(); - } - - return _path?[index] ?? (_index - 1); - } - - public int CompareTo(IndexPath other) - { - var rhsPath = other; - int compareResult = 0; - int lhsCount = GetSize(); - int rhsCount = rhsPath.GetSize(); - - if (lhsCount == 0 || rhsCount == 0) - { - // one of the paths are empty, compare based on size - compareResult = (lhsCount - rhsCount); - } - else - { - // both paths are non-empty, but can be of different size - for (int i = 0; i < Math.Min(lhsCount, rhsCount); i++) - { - if (GetAt(i) < rhsPath.GetAt(i)) - { - compareResult = -1; - break; - } - else if (GetAt(i) > rhsPath.GetAt(i)) - { - compareResult = 1; - break; - } - } - - // if both match upto min(lhsCount, rhsCount), compare based on size - compareResult = compareResult == 0 ? (lhsCount - rhsCount) : compareResult; - } - - if (compareResult != 0) - { - compareResult = compareResult > 0 ? 1 : -1; - } - - return compareResult; - } - - public IndexPath CloneWithChildIndex(int childIndex) - { - if (_path != null) - { - return new IndexPath(_path, childIndex); - } - else if (_index != 0) - { - return new IndexPath(_index - 1, childIndex); - } - else - { - return new IndexPath(childIndex); - } - } - - public bool IsAncestorOf(in IndexPath other) - { - if (other.GetSize() <= GetSize()) - { - return false; - } - - var size = GetSize(); - - for (int i = 0; i < size; i++) - { - if (GetAt(i) != other.GetAt(i)) - { - return false; - } - } - - return true; - } - - public override string ToString() - { - if (_path != null) - { - return "R" + string.Join(".", _path); - } - else if (_index != 0) - { - return "R" + (_index - 1); - } - else - { - return "R"; - } - } - - public static IndexPath CreateFrom(int index) => new IndexPath(index); - - public static IndexPath CreateFrom(int groupIndex, int itemIndex) => new IndexPath(groupIndex, itemIndex); - - public static IndexPath CreateFromIndices(IList indices) => new IndexPath(indices); - - public override bool Equals(object obj) => obj is IndexPath other && Equals(other); - - public bool Equals(IndexPath other) => CompareTo(other) == 0; - - public override int GetHashCode() - { - var hashCode = -504981047; - - if (_path != null) - { - foreach (var i in _path) - { - hashCode = hashCode * -1521134295 + i.GetHashCode(); - } - } - else - { - hashCode = hashCode * -1521134295 + _index.GetHashCode(); - } - - return hashCode; - } - - public static bool operator <(IndexPath x, IndexPath y) { return x.CompareTo(y) < 0; } - public static bool operator >(IndexPath x, IndexPath y) { return x.CompareTo(y) > 0; } - public static bool operator <=(IndexPath x, IndexPath y) { return x.CompareTo(y) <= 0; } - public static bool operator >=(IndexPath x, IndexPath y) { return x.CompareTo(y) >= 0; } - public static bool operator ==(IndexPath x, IndexPath y) { return x.CompareTo(y) == 0; } - public static bool operator !=(IndexPath x, IndexPath y) { return x.CompareTo(y) != 0; } - public static bool operator ==(IndexPath? x, IndexPath? y) { return (x ?? default).CompareTo(y ?? default) == 0; } - public static bool operator !=(IndexPath? x, IndexPath? y) { return (x ?? default).CompareTo(y ?? default) != 0; } - } -} diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 6e0ad66699..a3dfe33641 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls /// /// Displays a collection of items. /// - public class ItemsControl : TemplatedControl, IItemsPresenterHost + public class ItemsControl : TemplatedControl, IItemsPresenterHost, ICollectionChangedListener { /// /// The default value for the property. @@ -53,7 +53,6 @@ namespace Avalonia.Controls private IEnumerable _items = new AvaloniaList(); private int _itemCount; private IItemContainerGenerator _itemContainerGenerator; - private IDisposable _itemsCollectionChangedSubscription; /// /// Initializes static members of the class. @@ -150,6 +149,19 @@ namespace Avalonia.Controls ItemContainerGenerator.Clear(); } + void ICollectionChangedListener.PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + } + + void ICollectionChangedListener.Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + } + + void ICollectionChangedListener.PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + ItemsCollectionChanged(sender, e); + } + /// /// Gets the item at the specified index in a collection. /// @@ -295,7 +307,7 @@ namespace Avalonia.Controls if (next != null) { - focus.Focus(next, NavigationMethod.Directional); + focus.Focus(next, NavigationMethod.Directional, e.KeyModifiers); e.Handled = true; } @@ -315,12 +327,14 @@ namespace Avalonia.Controls /// The event args. protected virtual void ItemsChanged(AvaloniaPropertyChangedEventArgs e) { - _itemsCollectionChangedSubscription?.Dispose(); - _itemsCollectionChangedSubscription = null; - var oldValue = e.OldValue as IEnumerable; var newValue = e.NewValue as IEnumerable; + if (oldValue is INotifyCollectionChanged incc) + { + CollectionChangedEventManager.Instance.RemoveListener(incc, this); + } + UpdateItemCount(); RemoveControlItemsFromLogicalChildren(oldValue); AddControlItemsToLogicalChildren(newValue); @@ -418,11 +432,9 @@ namespace Avalonia.Controls PseudoClasses.Set(":empty", items == null || items.Count() == 0); PseudoClasses.Set(":singleitem", items != null && items.Count() == 1); - var incc = items as INotifyCollectionChanged; - - if (incc != null) + if (items is INotifyCollectionChanged incc) { - _itemsCollectionChangedSubscription = incc.WeakSubscribe(ItemsCollectionChanged); + CollectionChangedEventManager.Instance.AddListener(incc, this); } } diff --git a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs b/src/Avalonia.Controls/ItemsSourceView.cs similarity index 52% rename from src/Avalonia.Controls/Repeater/ItemsSourceView.cs rename to src/Avalonia.Controls/ItemsSourceView.cs index ecf8abc13f..b2663f3213 100644 --- a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs +++ b/src/Avalonia.Controls/ItemsSourceView.cs @@ -7,7 +7,11 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using Avalonia.Controls.Utils; + +#nullable enable namespace Avalonia.Controls { @@ -23,9 +27,13 @@ namespace Avalonia.Controls /// public class ItemsSourceView : INotifyCollectionChanged, IDisposable { - private readonly IList _inner; - private INotifyCollectionChanged _notifyCollectionChanged; - private int _cachedSize = -1; + /// + /// Gets an empty + /// + public static ItemsSourceView Empty { get; } = new ItemsSourceView(Array.Empty()); + + private protected readonly IList _inner; + private INotifyCollectionChanged? _notifyCollectionChanged; /// /// Initializes a new instance of the ItemsSourceView class for the specified data source. @@ -33,7 +41,7 @@ namespace Avalonia.Controls /// The data source. public ItemsSourceView(IEnumerable source) { - Contract.Requires(source != null); + source = source ?? throw new ArgumentNullException(nameof(source)); if (source is IList list) { @@ -54,18 +62,7 @@ namespace Avalonia.Controls /// /// Gets the number of items in the collection. /// - public int Count - { - get - { - if (_cachedSize == -1) - { - _cachedSize = _inner.Count; - } - - return _cachedSize; - } - } + public int Count => _inner.Count; /// /// Gets a value that indicates whether the items source can provide a unique key for each item. @@ -75,10 +72,17 @@ namespace Avalonia.Controls /// public bool HasKeyIndexMapping => false; + /// + /// Retrieves the item at the specified index. + /// + /// The index. + /// The item. + public object? this[int index] => GetAt(index); + /// /// Occurs when the collection has changed to indicate the reason for the change and which items changed. /// - public event NotifyCollectionChangedEventHandler CollectionChanged; + public event NotifyCollectionChangedEventHandler? CollectionChanged; /// public void Dispose() @@ -93,10 +97,26 @@ namespace Avalonia.Controls /// Retrieves the item at the specified index. /// /// The index. - /// the item. - public object GetAt(int index) => _inner[index]; + /// The item. + public object? GetAt(int index) => _inner[index]; - public int IndexOf(object item) => _inner.IndexOf(item); + public int IndexOf(object? item) => _inner.IndexOf(item); + + public static ItemsSourceView GetOrCreate(IEnumerable? items) + { + if (items is ItemsSourceView isv) + { + return isv; + } + else if (items is null) + { + return Empty; + } + else + { + return new ItemsSourceView(items); + } + } /// /// Retrieves the index of the item that has the specified unique identifier (key). @@ -124,9 +144,24 @@ namespace Avalonia.Controls throw new NotImplementedException(); } + internal void AddListener(ICollectionChangedListener listener) + { + if (_inner is INotifyCollectionChanged incc) + { + CollectionChangedEventManager.Instance.AddListener(incc, listener); + } + } + + internal void RemoveListener(ICollectionChangedListener listener) + { + if (_inner is INotifyCollectionChanged incc) + { + CollectionChangedEventManager.Instance.RemoveListener(incc, listener); + } + } + protected void OnItemsSourceChanged(NotifyCollectionChangedEventArgs args) { - _cachedSize = _inner.Count; CollectionChanged?.Invoke(this, args); } @@ -144,4 +179,62 @@ namespace Avalonia.Controls OnItemsSourceChanged(e); } } + + public class ItemsSourceView : ItemsSourceView, IReadOnlyList + { + /// + /// Gets an empty + /// + public new static ItemsSourceView Empty { get; } = new ItemsSourceView(Array.Empty()); + + /// + /// Initializes a new instance of the ItemsSourceView class for the specified data source. + /// + /// The data source. + public ItemsSourceView(IEnumerable source) + : base(source) + { + } + + private ItemsSourceView(IEnumerable source) + : base(source) + { + } + + /// + /// Retrieves the item at the specified index. + /// + /// The index. + /// The item. +#pragma warning disable CS8603 + public new T this[int index] => GetAt(index); +#pragma warning restore CS8603 + + /// + /// Retrieves the item at the specified index. + /// + /// The index. + /// The item. + [return: MaybeNull] + public new T GetAt(int index) => (T)_inner[index]; + + public IEnumerator GetEnumerator() => _inner.Cast().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator(); + + public static new ItemsSourceView GetOrCreate(IEnumerable? items) + { + if (items is ItemsSourceView isv) + { + return isv; + } + else if (items is null) + { + return Empty; + } + else + { + return new ItemsSourceView(items); + } + } + } } diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 3c21cd2c38..f7e86d697a 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -2,6 +2,7 @@ using System.Collections; using Avalonia.Controls.Generators; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; +using Avalonia.Controls.Selection; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.VisualTree; @@ -76,9 +77,7 @@ namespace Avalonia.Controls set => base.SelectedItems = value; } - /// - /// Gets or sets a model holding the current selection. - /// + /// public new ISelectionModel Selection { get => base.Selection; @@ -115,7 +114,7 @@ namespace Avalonia.Controls /// /// Deselects all items in the . /// - public void UnselectAll() => Selection.ClearSelection(); + public void UnselectAll() => Selection.Clear(); /// protected override IItemContainerGenerator CreateItemContainerGenerator() @@ -136,7 +135,8 @@ namespace Avalonia.Controls e.Handled = UpdateSelectionFromEventSource( e.Source, true, - (e.KeyModifiers & KeyModifiers.Shift) != 0); + (e.KeyModifiers & KeyModifiers.Shift) != 0, + (e.KeyModifiers & KeyModifiers.Control) != 0); } } diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs index 7205af0e75..4da044fec1 100644 --- a/src/Avalonia.Controls/Menu.cs +++ b/src/Avalonia.Controls/Menu.cs @@ -1,9 +1,12 @@ using Avalonia.Controls.Platform; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; +#nullable enable + namespace Avalonia.Controls { /// @@ -14,6 +17,8 @@ namespace Avalonia.Controls private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel { Orientation = Orientation.Horizontal }); + private LightDismissOverlayLayer? _overlay; + /// /// Initializes a new instance of the class. /// diff --git a/src/Avalonia.Controls/MenuBase.cs b/src/Avalonia.Controls/MenuBase.cs index 4554cb2bcf..0434928280 100644 --- a/src/Avalonia.Controls/MenuBase.cs +++ b/src/Avalonia.Controls/MenuBase.cs @@ -8,6 +8,8 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +#nullable enable + namespace Avalonia.Controls { /// @@ -51,9 +53,7 @@ namespace Avalonia.Controls /// The menu interaction handler. public MenuBase(IMenuInteractionHandler interactionHandler) { - Contract.Requires(interactionHandler != null); - - InteractionHandler = interactionHandler; + InteractionHandler = interactionHandler ?? throw new ArgumentNullException(nameof(interactionHandler)); } /// @@ -77,7 +77,7 @@ namespace Avalonia.Controls IMenuInteractionHandler IMenu.InteractionHandler => InteractionHandler; /// - IMenuItem IMenuElement.SelectedItem + IMenuItem? IMenuElement.SelectedItem { get { diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 912abc6de3..b4d3272471 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Linq; using System.Windows.Input; using Avalonia.Controls.Generators; using Avalonia.Controls.Mixins; @@ -12,6 +13,8 @@ using Avalonia.Interactivity; using Avalonia.LogicalTree; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls { /// @@ -22,7 +25,7 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = + public static readonly DirectProperty CommandProperty = Button.CommandProperty.AddOwner( menuItem => menuItem.Command, (menuItem, command) => menuItem.Command = command, @@ -94,10 +97,9 @@ namespace Avalonia.Controls private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel()); - private ICommand _command; + private ICommand? _command; private bool _commandCanExecute = true; private Popup _popup; - private IDisposable _gridHack; /// /// Initializes static members of the class. @@ -119,6 +121,32 @@ namespace Avalonia.Controls public MenuItem() { + // HACK: This nasty but it's all WPF's fault. Grid uses an inherited attached + // property to store SharedSizeGroup state, except property inheritance is done + // down the logical tree. In this case, the control which is setting + // Grid.IsSharedSizeScope="True" is not in the logical tree. Instead of fixing + // the way Grid stores shared size state, the developers of WPF just created a + // binding of the internal state of the visual parent to the menu item. We don't + // have much choice but to do the same for now unless we want to refactor Grid, + // which I honestly am not brave enough to do right now. Here's the same hack in + // the WPF codebase: + // + // https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141 + // + // In addition to the hack from WPF, we also make sure to return null when we have + // no parent. If we don't do this, inheritance falls back to the logical tree, + // causing the shared size scope in the parent MenuItem to be used, breaking + // menu layout. + + var parentSharedSizeScope = this.GetObservable(VisualParentProperty) + .SelectMany(x => + { + var parent = x as Control; + return parent?.GetObservable(DefinitionBase.PrivateSharedSizeScopeProperty) ?? + Observable.Return(null); + }); + + this.Bind(DefinitionBase.PrivateSharedSizeScopeProperty, parentSharedSizeScope); } /// @@ -166,7 +194,7 @@ namespace Avalonia.Controls /// /// Gets or sets the command associated with the menu item. /// - public ICommand Command + public ICommand? Command { get { return _command; } set { SetAndRaise(CommandProperty, ref _command, value); } @@ -246,7 +274,7 @@ namespace Avalonia.Controls bool IMenuItem.IsPointerOverSubMenu => _popup?.IsPointerOverPopup ?? false; /// - IMenuElement IMenuItem.Parent => Parent as IMenuElement; + IMenuElement? IMenuItem.Parent => Parent as IMenuElement; protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute; @@ -254,7 +282,7 @@ namespace Avalonia.Controls bool IMenuElement.MoveSelection(NavigationDirection direction, bool wrap) => MoveSelection(direction, wrap); /// - IMenuItem IMenuElement.SelectedItem + IMenuItem? IMenuElement.SelectedItem { get { @@ -323,32 +351,6 @@ namespace Avalonia.Controls { Command.CanExecuteChanged -= CanExecuteChanged; } - - _gridHack?.Dispose(); - _gridHack = null; - } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - - if (this.GetVisualParent() is IControl parent) - { - // HACK: This nasty but it's all WPF's fault. Grid uses an inherited attached - // property to store SharedSizeGroup state, except property inheritance is done - // down the logical tree. In this case, the control which is setting - // Grid.IsSharedSizeScope="True" is not in the logical tree. Instead of fixing - // the way Grid stores shared size state, the developers of WPF just created a - // binding of the internal state of the visual parent to the menu item. We don't - // have much choice but to do the same for now unless we want to refactor Grid, - // which I honestly am not brave enough to do right now. Here's the same hack in - // the WPF codebase: - // - // https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141 - _gridHack = Bind( - DefinitionBase.PrivateSharedSizeScopeProperty, - parent.GetBindingObservable(DefinitionBase.PrivateSharedSizeScopeProperty)); - } } /// @@ -551,7 +553,7 @@ namespace Avalonia.Controls /// The property change event. private void IsSelectedChanged(AvaloniaPropertyChangedEventArgs e) { - if ((bool)e.NewValue) + if ((bool)e.NewValue!) { Focus(); } @@ -563,7 +565,7 @@ namespace Avalonia.Controls /// The property change event. private void SubMenuOpenChanged(AvaloniaPropertyChangedEventArgs e) { - var value = (bool)e.NewValue; + var value = (bool)e.NewValue!; if (value) { diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 2a7ea12d79..6d6398bcda 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -3,7 +3,6 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.LogicalTree; -using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; using Avalonia.VisualTree; @@ -235,7 +234,9 @@ namespace Avalonia.Controls.Platform // If the the parent is an IMenu which successfully moved its selection, // and the current menu is open then close the current menu and open the // new menu. - if (item.IsSubMenuOpen && item.Parent is IMenu) + if (item.IsSubMenuOpen && + item.Parent is IMenu && + item.Parent.SelectedItem is object) { item.Close(); Open(item.Parent.SelectedItem, true); @@ -363,6 +364,11 @@ namespace Avalonia.Controls.Platform } else { + if (item.IsTopLevel && item.Parent is IMainMenu mainMenu) + { + mainMenu.Open(); + } + Open(item, false); } @@ -385,7 +391,7 @@ namespace Avalonia.Controls.Platform { if (e.Source == Menu) { - Menu.MoveSelection(NavigationDirection.First, true); + Menu?.MoveSelection(NavigationDirection.First, true); } } diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 0d77cbf802..7514f214aa 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -23,10 +23,10 @@ namespace Avalonia.Platform Size ClientSize { get; } /// - /// Gets the scaling factor for the toplevel. + /// Gets the scaling factor for the toplevel. This is used for rendering. /// - double Scaling { get; } - + double RenderScaling { get; } + /// /// The list of native platform's surfaces that can be consumed by rendering subsystems. /// diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index b190c4f2e7..ecaf87d1ed 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -13,6 +13,11 @@ namespace Avalonia.Platform /// Hides the window. /// void Hide(); + + /// + /// Gets the scaling factor for Window positioning and sizing. + /// + double DesktopScaling { get; } /// /// Gets the position of the window in device pixels. diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index c4571505ba..8837901816 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters private IControl _child; private bool _createdChild; - private IDataTemplate _dataTemplate; + private IRecyclingDataTemplate _recyclingDataTemplate; private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper(); /// @@ -281,7 +281,7 @@ namespace Avalonia.Controls.Presenters protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); - _dataTemplate = null; + _recyclingDataTemplate = null; _createdChild = false; InvalidateMeasure(); } @@ -307,22 +307,21 @@ namespace Avalonia.Controls.Presenters { var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; - // We have content and it isn't a control, so if the new data template is the same - // as the old data template, try to recycle the existing child control to display - // the new data. - if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling) + if (dataTemplate is IRecyclingDataTemplate rdt) { - newChild = oldChild; + var toRecycle = rdt == _recyclingDataTemplate ? oldChild : null; + newChild = rdt.Build(content, toRecycle); + _recyclingDataTemplate = rdt; } else { - _dataTemplate = dataTemplate; - newChild = _dataTemplate.Build(content); + newChild = dataTemplate.Build(content); + _recyclingDataTemplate = null; } } else { - _dataTemplate = null; + _recyclingDataTemplate = null; } return newChild; @@ -422,7 +421,7 @@ namespace Avalonia.Controls.Presenters LogicalChildren.Remove(Child); ((ISetInheritanceParent)Child).SetParent(Child.Parent); Child = null; - _dataTemplate = null; + _recyclingDataTemplate = null; } InvalidateMeasure(); diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 327cfb7736..5fcb14c858 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -257,15 +257,7 @@ namespace Avalonia.Controls.Presenters return base.ArrangeOverride(finalSize); } - try - { - _arranging = true; - return ArrangeWithAnchoring(finalSize); - } - finally - { - _arranging = false; - } + return ArrangeWithAnchoring(finalSize); } private Size ArrangeWithAnchoring(Size finalSize) @@ -316,7 +308,17 @@ namespace Avalonia.Controls.Presenters } Extent = newExtent; - Offset = newOffset; + + try + { + _arranging = true; + Offset = newOffset; + } + finally + { + _arranging = false; + } + ArrangeOverrideImpl(size, -Offset); } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 6e534bbb2a..37490c3ef3 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -14,6 +14,9 @@ namespace Avalonia.Controls.Presenters o => o.CaretIndex, (o, v) => o.CaretIndex = v); + public static readonly StyledProperty RevealPasswordProperty = + AvaloniaProperty.Register(nameof(RevealPassword)); + public static readonly StyledProperty PasswordCharProperty = AvaloniaProperty.Register(nameof(PasswordChar)); @@ -75,7 +78,7 @@ namespace Avalonia.Controls.Presenters static TextPresenter() { AffectsRender(SelectionBrushProperty); - AffectsMeasure(TextProperty, PasswordCharProperty, + AffectsMeasure(TextProperty, PasswordCharProperty, RevealPasswordProperty, TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty, TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty); @@ -84,7 +87,7 @@ namespace Avalonia.Controls.Presenters TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed, TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed, SelectionStartProperty.Changed, SelectionEndProperty.Changed, - SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed + SelectionForegroundBrushProperty.Changed, PasswordCharProperty.Changed, RevealPasswordProperty.Changed ).AddClassHandler((x, _) => x.InvalidateFormattedText()); CaretIndexProperty.Changed.AddClassHandler((x, e) => x.CaretIndexChanged((int)e.NewValue)); @@ -210,6 +213,12 @@ namespace Avalonia.Controls.Presenters set => SetValue(PasswordCharProperty, value); } + public bool RevealPassword + { + get => GetValue(RevealPasswordProperty); + set => SetValue(RevealPasswordProperty, value); + } + public IBrush SelectionBrush { get => GetValue(SelectionBrushProperty); @@ -426,7 +435,7 @@ namespace Avalonia.Controls.Presenters var text = Text; - if (PasswordChar != default(char)) + if (PasswordChar != default(char) && !RevealPassword) { result = CreateFormattedTextInternal(_constraint, new string(PasswordChar, text?.Length ?? 0)); } diff --git a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs index ba0fdfd535..7171ecc302 100644 --- a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs +++ b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls.Primitives { public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest { - public static ChromeOverlayLayer? GetOverlayLayer(IVisual visual) + public static Panel? GetOverlayLayer(IVisual visual) { foreach (var v in visual.GetVisualAncestors()) if (v is VisualLayerManager vlm) @@ -24,6 +24,11 @@ namespace Avalonia.Controls.Primitives return null; } + public void Add(Control c) + { + base.Children.Add(c); + } + public bool HitTest(Point point) => Children.HitTestCustom(point); } } diff --git a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs new file mode 100644 index 0000000000..752eedb68a --- /dev/null +++ b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Rendering; +using Avalonia.Styling; +using Avalonia.VisualTree; + +#nullable enable + +namespace Avalonia.Controls.Primitives +{ + /// + /// A layer that is used to dismiss a when the user clicks outside. + /// + public class LightDismissOverlayLayer : Border, ICustomHitTest + { + public IInputElement? InputPassThroughElement { get; set; } + + /// + /// Returns the light dismiss overlay for a specified visual. + /// + /// The visual. + /// The light dismiss overlay, or null if none found. + public static LightDismissOverlayLayer? GetLightDismissOverlayLayer(IVisual visual) + { + visual = visual ?? throw new ArgumentNullException(nameof(visual)); + + VisualLayerManager? manager; + + if (visual is TopLevel topLevel) + { + manager = topLevel.GetTemplateChildren() + .OfType() + .FirstOrDefault(); + } + else + { + manager = visual.FindAncestorOfType(); + } + + return manager?.LightDismissOverlayLayer; + } + + public bool HitTest(Point point) + { + if (InputPassThroughElement is object) + { + var p = point.Transform(this.TransformToVisual(VisualRoot)!.Value); + var hit = VisualRoot.GetVisualAt(p, x => x != this); + + if (hit is object) + { + return !InputPassThroughElement.IsVisualAncestorOf(hit); + } + } + + return true; + } + } +} diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 1fcf8d61bc..a676892384 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -1,12 +1,10 @@ using System; -using System.Diagnostics; using System.Linq; using System.Reactive.Disposables; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.Platform; @@ -86,12 +84,27 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(ObeyScreenEdges), true); #pragma warning restore 618 + public static readonly StyledProperty OverlayDismissEventPassThroughProperty = + AvaloniaProperty.Register(nameof(OverlayDismissEventPassThrough)); + + public static readonly DirectProperty OverlayInputPassThroughElementProperty = + AvaloniaProperty.RegisterDirect( + nameof(OverlayInputPassThroughElement), + o => o.OverlayInputPassThroughElement, + (o, v) => o.OverlayInputPassThroughElement = v); + /// /// Defines the property. /// public static readonly StyledProperty HorizontalOffsetProperty = AvaloniaProperty.Register(nameof(HorizontalOffset)); + /// + /// Defines the property. + /// + public static readonly StyledProperty IsLightDismissEnabledProperty = + AvaloniaProperty.Register(nameof(IsLightDismissEnabled)); + /// /// Defines the property. /// @@ -101,8 +114,13 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly StyledProperty StaysOpenProperty = - AvaloniaProperty.Register(nameof(StaysOpen), true); + [Obsolete("Use IsLightDismissEnabledProperty")] + public static readonly DirectProperty StaysOpenProperty = + AvaloniaProperty.RegisterDirect( + nameof(StaysOpen), + o => o.StaysOpen, + (o, v) => o.StaysOpen = v, + true); /// /// Defines the property. @@ -113,6 +131,7 @@ namespace Avalonia.Controls.Primitives private bool _isOpen; private bool _ignoreIsOpenChanged; private PopupOpenState? _openState; + private IInputElement _overlayInputPassThroughElement; /// /// Initializes static members of the class. @@ -127,7 +146,7 @@ namespace Avalonia.Controls.Primitives /// /// Raised when the popup closes. /// - public event EventHandler? Closed; + public event EventHandler? Closed; /// /// Raised when the popup opens. @@ -165,6 +184,18 @@ namespace Avalonia.Controls.Primitives set; } + /// + /// Gets or sets a value that determines how the can be dismissed. + /// + /// + /// Light dismiss is when the user taps on any area other than the popup. + /// + public bool IsLightDismissEnabled + { + get => GetValue(IsLightDismissEnabledProperty); + set => SetValue(IsLightDismissEnabledProperty, value); + } + /// /// Gets or sets a value indicating whether the popup is currently open. /// @@ -246,6 +277,32 @@ namespace Avalonia.Controls.Primitives set => SetValue(ObeyScreenEdgesProperty, value); } + /// + /// Gets or sets a value indicating whether the event that closes the popup is passed + /// through to the parent window. + /// + /// + /// When is set to true, clicks outside the the popup + /// cause the popup to close. When is set to + /// false, these clicks will be handled by the popup and not be registered by the parent + /// window. When set to true, the events will be passed through to the parent window. + /// + public bool OverlayDismissEventPassThrough + { + get => GetValue(OverlayDismissEventPassThroughProperty); + set => SetValue(OverlayDismissEventPassThroughProperty, value); + } + + /// + /// Gets or sets an element that should receive pointer input events even when underneath + /// the popup's overlay. + /// + public IInputElement OverlayInputPassThroughElement + { + get => _overlayInputPassThroughElement; + set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value); + } + /// /// Gets or sets the Horizontal offset of the popup in relation to the . /// @@ -268,10 +325,11 @@ namespace Avalonia.Controls.Primitives /// Gets or sets a value indicating whether the popup should stay open when the popup is /// pressed or loses focus. /// + [Obsolete("Use IsLightDismissEnabled")] public bool StaysOpen { - get { return GetValue(StaysOpenProperty); } - set { SetValue(StaysOpenProperty, value); } + get => !IsLightDismissEnabled; + set => IsLightDismissEnabled = !value; } /// @@ -363,14 +421,12 @@ namespace Avalonia.Controls.Primitives if (parentPopupRoot?.Parent is Popup popup) { - DeferCleanup(SubscribeToEventHandler>(popup, ParentClosed, + DeferCleanup(SubscribeToEventHandler>(popup, ParentClosed, (x, handler) => x.Closed += handler, (x, handler) => x.Closed -= handler)); } } - DeferCleanup(topLevel.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel)); - DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick)); var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state => @@ -384,6 +440,29 @@ namespace Avalonia.Controls.Primitives state.popupHost.Dispose(); }); + if (IsLightDismissEnabled) + { + var dismissLayer = LightDismissOverlayLayer.GetLightDismissOverlayLayer(placementTarget); + + if (dismissLayer != null) + { + dismissLayer.IsVisible = true; + dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement; + + DeferCleanup(Disposable.Create(() => + { + dismissLayer.IsVisible = false; + dismissLayer.InputPassThroughElement = null; + })); + + DeferCleanup(SubscribeToEventHandler>( + dismissLayer, + PointerPressedDismissOverlay, + (x, handler) => x.PointerPressed += handler, + (x, handler) => x.PointerPressed -= handler)); + } + } + _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup); WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); @@ -401,7 +480,7 @@ namespace Avalonia.Controls.Primitives /// /// Closes the popup. /// - public void Close() => CloseCore(null); + public void Close() => CloseCore(); /// /// Measures the control. @@ -471,7 +550,7 @@ namespace Avalonia.Controls.Primitives } } - private void CloseCore(EventArgs? closeEvent) + private void CloseCore() { if (_openState is null) { @@ -491,24 +570,46 @@ namespace Avalonia.Controls.Primitives IsOpen = false; } - Closed?.Invoke(this, new PopupClosedEventArgs(closeEvent)); + Closed?.Invoke(this, EventArgs.Empty); } private void ListenForNonClientClick(RawInputEventArgs e) { var mouse = e as RawPointerEventArgs; - if (!StaysOpen && mouse?.Type == RawPointerEventType.NonClientLeftButtonDown) + if (IsLightDismissEnabled && mouse?.Type == RawPointerEventType.NonClientLeftButtonDown) { - CloseCore(e); + CloseCore(); } } - private void PointerPressedOutside(object sender, PointerPressedEventArgs e) + private void PointerPressedDismissOverlay(object sender, PointerPressedEventArgs e) { - if (!StaysOpen && e.Source is IVisual v && !IsChildOrThis(v)) + if (IsLightDismissEnabled && e.Source is IVisual v && !IsChildOrThis(v)) { - CloseCore(e); + CloseCore(); + + if (OverlayDismissEventPassThrough) + { + PassThroughEvent(e); + } + } + } + + private void PassThroughEvent(PointerPressedEventArgs e) + { + if (e.Source is LightDismissOverlayLayer layer && + layer.GetVisualRoot() is IInputElement root) + { + var p = e.GetCurrentPoint(root); + var hit = root.InputHitTest(p.Position, x => x != layer); + + if (hit != null) + { + e.Pointer.Capture(hit); + hit.RaiseEvent(e); + e.Handled = true; + } } } @@ -602,7 +703,7 @@ namespace Avalonia.Controls.Primitives private void WindowDeactivated(object sender, EventArgs e) { - if (!StaysOpen) + if (IsLightDismissEnabled) { Close(); } @@ -610,7 +711,7 @@ namespace Avalonia.Controls.Primitives private void ParentClosed(object sender, EventArgs e) { - if (!StaysOpen) + if (IsLightDismissEnabled) { Close(); } @@ -618,7 +719,7 @@ namespace Avalonia.Controls.Primitives private void WindowLostFocus() { - if(!StaysOpen) + if (IsLightDismissEnabled) Close(); } diff --git a/src/Avalonia.Controls/Primitives/PopupClosedEventArgs.cs b/src/Avalonia.Controls/Primitives/PopupClosedEventArgs.cs deleted file mode 100644 index c51543438c..0000000000 --- a/src/Avalonia.Controls/Primitives/PopupClosedEventArgs.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Avalonia.Interactivity; - -#nullable enable - -namespace Avalonia.Controls.Primitives -{ - /// - /// Holds data for the event. - /// - public class PopupClosedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - public PopupClosedEventArgs(EventArgs? closeEvent) - { - CloseEvent = closeEvent; - } - - /// - /// Gets the event that closed the popup, if any. - /// - /// - /// If is false, then this property will hold details of the - /// interaction that caused the popup to close if the close was caused by e.g. a pointer press - /// outside the popup. It can be used to mark the event as handled if the event should not - /// be propagated. - /// - public EventArgs? CloseEvent { get; } - } -} diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs index b0e3d1ab08..91ed5d975d 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs @@ -40,9 +40,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning public void MoveAndResize(Point devicePoint, Size virtualSize) { - _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.Scaling); + _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.RenderScaling); } - public virtual double Scaling => _parent.Scaling; + public virtual double Scaling => _parent.DesktopScaling; } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index c915dc70b6..5f8c5da2f8 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -3,9 +3,9 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using Avalonia.Controls.Generators; +using Avalonia.Controls.Selection; using Avalonia.Controls.Utils; using Avalonia.Data; using Avalonia.Input; @@ -13,6 +13,8 @@ using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls.Primitives { /// @@ -24,8 +26,8 @@ namespace Avalonia.Controls.Primitives /// that maintain a selection (single or multiple). By default only its /// and properties are visible; the /// current multiple and together with the - /// and properties are protected, however a derived class can - /// expose these if it wishes to support multiple selection. + /// properties are protected, however a derived class can expose + /// these if it wishes to support multiple selection. /// /// /// maintains a selection respecting the current @@ -58,8 +60,8 @@ namespace Avalonia.Controls.Primitives /// /// Defines the 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, @@ -77,7 +79,7 @@ namespace Avalonia.Controls.Primitives /// /// Defines the property. /// - public static readonly DirectProperty SelectionProperty = + protected static readonly DirectProperty SelectionProperty = AvaloniaProperty.RegisterDirect( nameof(Selection), o => o.Selection, @@ -109,21 +111,12 @@ namespace Avalonia.Controls.Primitives RoutingStrategies.Bubble); private static readonly IList Empty = Array.Empty(); - private readonly SelectedItemsSync _selectedItems; - private ISelectionModel _selection; - private int _selectedIndex = -1; - private object _selectedItem; + private SelectedItemsSync? _selectedItemsSync; + private ISelectionModel? _selection; + private int _oldSelectedIndex; + private object? _oldSelectedItem; + private int _initializing; private bool _ignoreContainerSelectionChanged; - private int _updateCount; - private int _updateSelectedIndex; - private object _updateSelectedItem; - - public SelectingItemsControl() - { - // Setting Selection to null causes a default SelectionModel to be created. - Selection = null; - _selectedItems = new SelectedItemsSync(Selection); - } /// /// Initializes static members of the class. @@ -156,42 +149,17 @@ namespace Avalonia.Controls.Primitives /// public int SelectedIndex { - get => Selection.SelectedIndex != default ? Selection.SelectedIndex.GetAt(0) : -1; - set - { - if (_updateCount == 0) - { - if (value != SelectedIndex) - { - Selection.SelectedIndex = new IndexPath(value); - } - } - else - { - _updateSelectedIndex = value; - _updateSelectedItem = null; - } - } + get => Selection.SelectedIndex; + set => Selection.SelectedIndex = value; } /// /// Gets or sets the selected item. /// - public object SelectedItem + public object? SelectedItem { get => Selection.SelectedItem; - set - { - if (_updateCount == 0) - { - SelectedIndex = IndexOf(Items, value); - } - else - { - _updateSelectedItem = value; - _updateSelectedIndex = int.MinValue; - } - } + set => Selection.SelectedItem = value; } /// @@ -199,46 +167,40 @@ namespace Avalonia.Controls.Primitives /// protected IList SelectedItems { - get => _selectedItems.GetOrCreateItems(); - set => _selectedItems.SetItems(value); + get => SelectedItemsSync.SelectedItems; + set => SelectedItemsSync.SelectedItems = value; } /// - /// Gets or sets a model holding the current selection. + /// Gets or sets the model that holds the current selection. /// - protected ISelectionModel Selection + protected ISelectionModel Selection { - get => _selection; - set + get { - value ??= new SelectionModel + if (_selection is null) { - SingleSelect = !SelectionMode.HasFlagCustom(SelectionMode.Multiple), - AutoSelect = SelectionMode.HasFlagCustom(SelectionMode.AlwaysSelected), - RetainSelectionOnReset = true, - }; + _selection = CreateDefaultSelectionModel(); + InitializeSelectionModel(_selection); + } + + return _selection; + } + set + { + value ??= CreateDefaultSelectionModel(); if (_selection != value) { - if (value == null) - { - throw new ArgumentNullException(nameof(value), "Cannot set Selection to null."); - } - else if (value.Source != null && value.Source != Items) - { - throw new ArgumentException("Selection has invalid Source."); - } - - List oldSelection = null; - - if (_selection != null) + if (value.Source != null && value.Source != Items) { - oldSelection = Selection.SelectedItems.ToList(); - _selection.PropertyChanged -= OnSelectionModelPropertyChanged; - _selection.SelectionChanged -= OnSelectionModelSelectionChanged; - MarkContainersUnselected(); + throw new ArgumentException( + "The supplied ISelectionModel already has an assigned Source but this " + + "collection is different to the Items on the control."); } + var oldSelection = _selection?.SelectedItems.ToList(); + DeinitializeSelectionModel(_selection); _selection = value; if (oldSelection?.Count > 0) @@ -249,55 +211,7 @@ namespace Avalonia.Controls.Primitives Array.Empty())); } - if (_selection != null) - { - _selection.Source = Items; - _selection.PropertyChanged += OnSelectionModelPropertyChanged; - _selection.SelectionChanged += OnSelectionModelSelectionChanged; - - if (_selection.SingleSelect) - { - SelectionMode &= ~SelectionMode.Multiple; - } - else - { - SelectionMode |= SelectionMode.Multiple; - } - - if (_selection.AutoSelect) - { - SelectionMode |= SelectionMode.AlwaysSelected; - } - else - { - SelectionMode &= ~SelectionMode.AlwaysSelected; - } - - UpdateContainerSelection(); - - var selectedIndex = SelectedIndex; - var selectedItem = SelectedItem; - - if (_selectedIndex != selectedIndex) - { - RaisePropertyChanged(SelectedIndexProperty, _selectedIndex, selectedIndex); - _selectedIndex = selectedIndex; - } - - if (_selectedItem != selectedItem) - { - RaisePropertyChanged(SelectedItemProperty, _selectedItem, selectedItem); - _selectedItem = selectedItem; - } - - if (selectedIndex != -1) - { - RaiseEvent(new SelectionChangedEventArgs( - SelectionChangedEvent, - Array.Empty(), - Selection.SelectedItems.ToList())); - } - } + InitializeSelectionModel(_selection); } } } @@ -320,20 +234,20 @@ namespace Avalonia.Controls.Primitives /// protected bool AlwaysSelected => (SelectionMode & SelectionMode.AlwaysSelected) != 0; + private SelectedItemsSync SelectedItemsSync => _selectedItemsSync ??= new SelectedItemsSync(Selection); + /// public override void BeginInit() { base.BeginInit(); - - InternalBeginInit(); + ++_initializing; } /// public override void EndInit() { - InternalEndInit(); - base.EndInit(); + --_initializing; } /// @@ -353,7 +267,7 @@ namespace Avalonia.Controls.Primitives /// /// The control that raised the event. /// The container or null if the event did not originate in a container. - protected IControl GetContainerFromEventSource(IInteractive eventSource) + protected IControl? GetContainerFromEventSource(IInteractive eventSource) { var parent = (IVisual)eventSource; @@ -371,21 +285,14 @@ namespace Avalonia.Controls.Primitives return null; } - /// - protected override void ItemsChanged(AvaloniaPropertyChangedEventArgs e) - { - if (_updateCount == 0) - { - Selection.Source = e.NewValue; - } - - base.ItemsChanged(e); - } - - /// protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { base.ItemsCollectionChanged(sender, e); + + if (AlwaysSelected && SelectedIndex == -1 && ItemCount > 0) + { + SelectedIndex = 0; + } } /// @@ -400,9 +307,10 @@ namespace Avalonia.Controls.Primitives Selection.Select(container.Index); MarkContainerSelected(container.ContainerControl, true); } - else if (Selection.IsSelected(container.Index) == true) + else { - MarkContainerSelected(container.ContainerControl, true); + var selected = Selection.IsSelected(container.Index); + MarkContainerSelected(container.ContainerControl, selected); } } } @@ -433,7 +341,7 @@ namespace Avalonia.Controls.Primitives { if (i.ContainerControl != null && i.Item != null) { - bool selected = Selection.IsSelected(i.Index) == true; + bool selected = Selection.IsSelected(i.Index); MarkContainerSelected(i.ContainerControl, selected); } } @@ -443,27 +351,39 @@ namespace Avalonia.Controls.Primitives protected override void OnDataContextBeginUpdate() { base.OnDataContextBeginUpdate(); + ++_initializing; - InternalBeginInit(); + if (_selection is object) + { + _selection.Source = null; + } } /// protected override void OnDataContextEndUpdate() { base.OnDataContextEndUpdate(); + --_initializing; + + if (_selection is object && _initializing == 0) + { + _selection.Source = Items; - InternalEndInit(); + if (Items is null) + { + _selection.Clear(); + _selectedItemsSync?.SelectedItems?.Clear(); + } + } } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnInitialized() { - base.OnPropertyChanged(change); + base.OnInitialized(); - if (change.Property == SelectionModeProperty) + if (_selection is object) { - var mode = change.NewValue.GetValueOrDefault(); - Selection.SingleSelect = !mode.HasFlagCustom(SelectionMode.Multiple); - Selection.AutoSelect = mode.HasFlagCustom(SelectionMode.AlwaysSelected); + _selection.Source = Items; } } @@ -487,6 +407,29 @@ namespace Avalonia.Controls.Primitives } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ItemsProperty && + _initializing == 0 && + _selection is object) + { + var newValue = change.NewValue.GetValueOrDefault(); + _selection.Source = newValue; + + if (newValue is null) + { + _selection.Clear(); + } + } + else if (change.Property == SelectionModeProperty && _selection is object) + { + var newValue = change.NewValue.GetValueOrDefault(); + _selection.SingleSelect = !newValue.HasFlagCustom(SelectionMode.Multiple); + } + } + /// /// Moves the selection in the specified direction relative to the current selection. /// @@ -506,7 +449,7 @@ namespace Avalonia.Controls.Primitives /// The direction to move. /// Whether to wrap when the selection reaches the first or last item. /// True if the selection was moved; otherwise false. - protected bool MoveSelection(IControl from, NavigationDirection direction, bool wrap) + protected bool MoveSelection(IControl? from, NavigationDirection direction, bool wrap) { if (Presenter?.Panel is INavigableContainer container && GetNextControl(container, direction, from, wrap) is IControl next) @@ -538,71 +481,62 @@ namespace Avalonia.Controls.Primitives bool toggleModifier = false, bool rightButton = false) { - if (index != -1) + if (index < 0 || index >= ItemCount) { - if (select) - { - var mode = SelectionMode; - var multi = (mode & SelectionMode.Multiple) != 0; - var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0); - var range = multi && rangeModifier; - - if (rightButton) - { - if (Selection.IsSelected(index) == false) - { - SelectedIndex = index; - } - } - else if (range) - { - using var operation = Selection.Update(); - var anchor = Selection.AnchorIndex; - - if (anchor.GetSize() == 0) - { - anchor = new IndexPath(0); - } + return; + } - Selection.ClearSelection(); - Selection.AnchorIndex = anchor; - Selection.SelectRangeFromAnchor(index); - } - else if (multi && toggle) - { - if (Selection.IsSelected(index) == true) - { - Selection.Deselect(index); - } - else - { - Selection.Select(index); - } - } - else if (toggle) - { - SelectedIndex = (SelectedIndex == index) ? -1 : index; - } - else - { - using var operation = Selection.Update(); - Selection.ClearSelection(); - Selection.Select(index); - } + var mode = SelectionMode; + var multi = (mode & SelectionMode.Multiple) != 0; + var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0); + var range = multi && rangeModifier; - if (Presenter?.Panel != null) - { - var container = ItemContainerGenerator.ContainerFromIndex(index); - KeyboardNavigation.SetTabOnceActiveElement( - (InputElement)Presenter.Panel, - container); - } + if (!select) + { + Selection.Deselect(index); + } + else if (rightButton) + { + if (Selection.IsSelected(index) == false) + { + SelectedIndex = index; + } + } + else if (range) + { + using var operation = Selection.BatchUpdate(); + Selection.Clear(); + Selection.SelectRange(Selection.AnchorIndex, index); + } + else if (multi && toggle) + { + if (Selection.IsSelected(index) == true) + { + Selection.Deselect(index); } else { - LostSelection(); + Selection.Select(index); } } + else if (toggle) + { + SelectedIndex = (SelectedIndex == index) ? -1 : index; + } + else + { + using var operation = Selection.BatchUpdate(); + Selection.Clear(); + Selection.Select(index); + } + + if (Presenter?.Panel != null) + { + var container = ItemContainerGenerator.ContainerFromIndex(index); + KeyboardNavigation.SetTabOnceActiveElement( + (InputElement)Presenter.Panel, + container); + } } /// @@ -660,23 +594,35 @@ namespace Avalonia.Controls.Primitives } /// - /// Called when is raised. + /// Called when is raised on + /// . /// /// The sender. /// The event args. private void OnSelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(SelectionModel.AnchorIndex) && AutoScrollToSelectedItem) + if (e.PropertyName == nameof(ISelectionModel.AnchorIndex) && AutoScrollToSelectedItem) { - if (Selection.AnchorIndex.GetSize() > 0) + if (Selection.AnchorIndex > 0) { - ScrollIntoView(Selection.AnchorIndex.GetAt(0)); + ScrollIntoView(Selection.AnchorIndex); } } + else if (e.PropertyName == nameof(ISelectionModel.SelectedIndex)) + { + RaisePropertyChanged(SelectedIndexProperty, _oldSelectedIndex, SelectedIndex); + _oldSelectedIndex = SelectedIndex; + } + else if (e.PropertyName == nameof(ISelectionModel.SelectedItem)) + { + RaisePropertyChanged(SelectedItemProperty, _oldSelectedItem, SelectedItem); + _oldSelectedItem = SelectedItem; + } } /// - /// Called when is raised. + /// Called when event is raised on + /// . /// /// The sender. /// The event args. @@ -692,36 +638,40 @@ namespace Avalonia.Controls.Primitives } } - foreach (var i in e.SelectedIndices) + foreach (var i in e.SelectedIndexes) { - Mark(i.GetAt(0), true); + Mark(i, true); } - foreach (var i in e.DeselectedIndices) + foreach (var i in e.DeselectedIndexes) { - Mark(i.GetAt(0), false); + Mark(i, false); } - var newSelectedIndex = SelectedIndex; - var newSelectedItem = SelectedItem; + var route = BuildEventRoute(SelectionChangedEvent); - if (newSelectedIndex != _selectedIndex) + if (route.HasHandlers) { - RaisePropertyChanged(SelectedIndexProperty, _selectedIndex, newSelectedIndex); - _selectedIndex = newSelectedIndex; + var ev = new SelectionChangedEventArgs( + SelectionChangedEvent, + e.DeselectedItems.ToList(), + e.SelectedItems.ToList()); + RaiseEvent(ev); } + } - if (newSelectedItem != _selectedItem) + /// + /// Called when event is raised on + /// . + /// + /// The sender. + /// The event args. + private void OnSelectionModelLostSelection(object sender, EventArgs e) + { + if (AlwaysSelected && Items is object) { - RaisePropertyChanged(SelectedItemProperty, _selectedItem, newSelectedItem); - _selectedItem = newSelectedItem; + SelectedIndex = 0; } - - var ev = new SelectionChangedEventArgs( - SelectionChangedEvent, - e.DeselectedItems.ToList(), - e.SelectedItems.ToList()); - RaiseEvent(ev); } /// @@ -750,23 +700,6 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Called when the currently selected item is lost and the selection must be changed - /// depending on the property. - /// - private void LostSelection() - { - var items = Items?.Cast(); - var index = -1; - - if (items != null && AlwaysSelected) - { - index = Math.Min(SelectedIndex, items.Count() - 1); - } - - SelectedIndex = index; - } - /// /// Sets a container's 'selected' class or . /// @@ -809,16 +742,6 @@ namespace Avalonia.Controls.Primitives } } - private void UpdateContainerSelection() - { - foreach (var container in ItemContainerGenerator.Containers) - { - MarkContainerSelected( - container.ContainerControl, - Selection.IsSelected(container.Index) != false); - } - } - /// /// Sets an item container's 'selected' class or . /// @@ -834,52 +757,92 @@ namespace Avalonia.Controls.Primitives } } - private void UpdateFinished() + /// + /// Sets an item container's 'selected' class or . + /// + /// The item. + /// Whether the item should be selected or deselected. + private int MarkItemSelected(object item, bool selected) { - Selection.Source = Items; + var index = IndexOf(Items, item); - if (_updateSelectedItem != null) + if (index != -1) { - SelectedItem = _updateSelectedItem; + MarkItemSelected(index, selected); } - else + + return index; + } + + private void UpdateContainerSelection() + { + if (Presenter?.Panel is IPanel panel) { - if (ItemCount == 0 && SelectedIndex != -1) + foreach (var container in panel.Children) { - SelectedIndex = -1; - } - else - { - if (_updateSelectedIndex != int.MinValue) - { - SelectedIndex = _updateSelectedIndex; - } - - if (AlwaysSelected && SelectedIndex == -1) - { - SelectedIndex = 0; - } + MarkContainerSelected( + container, + Selection.IsSelected(ItemContainerGenerator.IndexFromContainer(container))); } } } - private void InternalBeginInit() + private ISelectionModel CreateDefaultSelectionModel() + { + return new SelectionModel + { + SingleSelect = !SelectionMode.HasFlagCustom(SelectionMode.Multiple), + }; + } + + private void InitializeSelectionModel(ISelectionModel model) { - if (_updateCount == 0) + if (_initializing == 0) + { + model.Source = Items; + } + + model.PropertyChanged += OnSelectionModelPropertyChanged; + model.SelectionChanged += OnSelectionModelSelectionChanged; + model.LostSelection += OnSelectionModelLostSelection; + + if (model.SingleSelect) + { + SelectionMode &= ~SelectionMode.Multiple; + } + else + { + SelectionMode |= SelectionMode.Multiple; + } + + _oldSelectedIndex = model.SelectedIndex; + _oldSelectedItem = model.SelectedItem; + + if (AlwaysSelected && model.Count == 0) { - _updateSelectedIndex = int.MinValue; + model.SelectedIndex = 0; } - ++_updateCount; + UpdateContainerSelection(); + + _selectedItemsSync ??= new SelectedItemsSync(model); + _selectedItemsSync.SelectionModel = model; + + if (SelectedIndex != -1) + { + RaiseEvent(new SelectionChangedEventArgs( + SelectionChangedEvent, + Array.Empty(), + Selection.SelectedItems.ToList())); + } } - private void InternalEndInit() + private void DeinitializeSelectionModel(ISelectionModel? model) { - Debug.Assert(_updateCount > 0); - - if (--_updateCount == 0) + if (model is object) { - UpdateFinished(); + model.PropertyChanged -= OnSelectionModelPropertyChanged; + model.SelectionChanged -= OnSelectionModelSelectionChanged; } } } diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index 3084d7fa72..d8d3450c6f 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Avalonia.LogicalTree; +using Avalonia.Media; namespace Avalonia.Controls.Primitives { @@ -7,14 +8,17 @@ namespace Avalonia.Controls.Primitives { private const int AdornerZIndex = int.MaxValue - 100; private const int ChromeZIndex = int.MaxValue - 99; - private const int OverlayZIndex = int.MaxValue - 98; + private const int LightDismissOverlayZIndex = int.MaxValue - 98; + private const int OverlayZIndex = int.MaxValue - 97; private ILogicalRoot _logicalRoot; private readonly List _layers = new List(); - + + public static readonly StyledProperty ChromeOverlayLayerProperty = + AvaloniaProperty.Register(nameof(ChromeOverlayLayer)); public bool IsPopup { get; set; } - + public AdornerLayer AdornerLayer { get @@ -30,10 +34,19 @@ namespace Avalonia.Controls.Primitives { get { - var rv = FindLayer(); - if (rv == null) - AddLayer(rv = new ChromeOverlayLayer(), ChromeZIndex); - return rv; + var current = GetValue(ChromeOverlayLayerProperty); + + if (current is null) + { + var chromeOverlayLayer = new ChromeOverlayLayer(); + AddLayer(chromeOverlayLayer, ChromeZIndex); + + SetValue(ChromeOverlayLayerProperty, chromeOverlayLayer); + + current = chromeOverlayLayer; + } + + return current; } } @@ -44,12 +57,33 @@ namespace Avalonia.Controls.Primitives if (IsPopup) return null; var rv = FindLayer(); - if(rv == null) + if (rv == null) AddLayer(rv = new OverlayLayer(), OverlayZIndex); return rv; } } + public LightDismissOverlayLayer LightDismissOverlayLayer + { + get + { + if (IsPopup) + return null; + var rv = FindLayer(); + if (rv == null) + { + rv = new LightDismissOverlayLayer + { + Background = Brushes.Transparent, + IsVisible = false + }; + + AddLayer(rv, LightDismissOverlayZIndex); + } + return rv; + } + } + T FindLayer() where T : class { foreach (var layer in _layers) @@ -65,7 +99,8 @@ namespace Avalonia.Controls.Primitives layer.ZIndex = zindex; VisualChildren.Add(layer); if (((ILogical)this).IsAttachedToLogicalTree) - ((ILogical)layer).NotifyAttachedToLogicalTree(new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this)); + ((ILogical)layer).NotifyAttachedToLogicalTree( + new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this)); InvalidateArrange(); } diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index e904957429..a92f24a050 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -215,7 +215,7 @@ namespace Avalonia.Controls TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166% // Remove these properties when we switch to fluent as default and removed the old one. - IndeterminateStartingOffset = -(dim / 5d); + IndeterminateStartingOffset = -dim; IndeterminateEndingOffset = dim; var padding = Padding; diff --git a/src/Avalonia.Controls/Repeater/ElementFactory.cs b/src/Avalonia.Controls/Repeater/ElementFactory.cs index 1c1b71af88..644e077221 100644 --- a/src/Avalonia.Controls/Repeater/ElementFactory.cs +++ b/src/Avalonia.Controls/Repeater/ElementFactory.cs @@ -4,8 +4,6 @@ namespace Avalonia.Controls { public abstract class ElementFactory : IElementFactory { - bool IDataTemplate.SupportsRecycling => false; - public IControl Build(object data) { return GetElementCore(new ElementFactoryGetArgs { Data = data }); diff --git a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs index 4b784375a9..dd97cde218 100644 --- a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs +++ b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs @@ -13,7 +13,6 @@ namespace Avalonia.Controls public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate; - public bool SupportsRecycling => false; public IControl Build(object param) => GetElement(null, param); public bool Match(object data) => _dataTemplate.Match(data); diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs index bf1b80f947..9f1c32bf64 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls /// public int OldIndex { get; private set; } - internal void Update(IControl element, int newIndex, int oldIndex) + internal void Update(IControl element, int oldIndex, int newIndex) { Element = element; NewIndex = newIndex; diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index 0d22187b34..bdb0fa3270 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -350,11 +350,14 @@ namespace Avalonia.Controls } // Make sure that only the target child can be the anchor during the bring into view operation. - foreach (var child in _owner.Children) + if (_scroller is object) { - if (child != targetChild) + foreach (var child in _owner.Children) { - _scroller.UnregisterAnchorCandidate(child); + if (child != targetChild) + { + _scroller.UnregisterAnchorCandidate(child); + } } } @@ -469,13 +472,7 @@ namespace Avalonia.Controls parent = parent.VisualParent; } - if (_scroller == null) - { - // We usually update the viewport in the post arrange handler. But, since we don't have - // a scroller, let's do it now. - UpdateViewport(Rect.Empty); - } - else if (!_managingViewportDisabled) + if (!_managingViewportDisabled) { _owner.EffectiveViewportChanged += OnEffectiveViewportChanged; _effectiveViewportChangedSubscribed = true; diff --git a/src/Avalonia.Controls/SelectedItems.cs b/src/Avalonia.Controls/SelectedItems.cs deleted file mode 100644 index a3acb48765..0000000000 --- a/src/Avalonia.Controls/SelectedItems.cs +++ /dev/null @@ -1,49 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections; -using System.Collections.Generic; - -#nullable enable - -namespace Avalonia.Controls -{ - public interface ISelectedItemInfo - { - public IndexPath Path { get; } - } - - internal class SelectedItems : IReadOnlyList - where Tinfo : ISelectedItemInfo - { - private readonly List _infos; - private readonly Func, int, TValue> _getAtImpl; - - public SelectedItems( - List infos, - int count, - Func, int, TValue> getAtImpl) - { - _infos = infos; - _getAtImpl = getAtImpl; - Count = count; - } - - public TValue this[int index] => _getAtImpl(_infos, index); - - public int Count { get; } - - public IEnumerator GetEnumerator() - { - for (var i = 0; i < Count; ++i) - { - yield return this[i]; - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/src/Avalonia.Controls/Selection/ISelectionModel.cs b/src/Avalonia.Controls/Selection/ISelectionModel.cs new file mode 100644 index 0000000000..3b8fd0c8b7 --- /dev/null +++ b/src/Avalonia.Controls/Selection/ISelectionModel.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + public interface ISelectionModel : INotifyPropertyChanged + { + IEnumerable? Source { get; set; } + bool SingleSelect { get; set; } + int SelectedIndex { get; set; } + IReadOnlyList SelectedIndexes { get; } + object? SelectedItem { get; set; } + IReadOnlyList SelectedItems { get; } + int AnchorIndex { get; set; } + int Count { get; } + + public event EventHandler? IndexesChanged; + public event EventHandler? SelectionChanged; + public event EventHandler? LostSelection; + public event EventHandler? SourceReset; + + public void BeginBatchUpdate(); + public void EndBatchUpdate(); + bool IsSelected(int index); + void Select(int index); + void Deselect(int index); + void SelectRange(int start, int end); + void DeselectRange(int start, int end); + void SelectAll(); + void Clear(); + } + + public static class SelectionModelExtensions + { + public static IDisposable BatchUpdate(this ISelectionModel model) + { + return new BatchUpdateOperation(model); + } + + public struct BatchUpdateOperation : IDisposable + { + private readonly ISelectionModel _owner; + private bool _isDisposed; + + public BatchUpdateOperation(ISelectionModel owner) + { + _owner = owner; + _isDisposed = false; + owner.BeginBatchUpdate(); + } + + public void Dispose() + { + if (!_isDisposed) + { + _owner?.EndBatchUpdate(); + _isDisposed = true; + } + } + } + } +} diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/Selection/IndexRange.cs similarity index 81% rename from src/Avalonia.Controls/IndexRange.cs rename to src/Avalonia.Controls/Selection/IndexRange.cs index e45d013af4..fa7b44faea 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/Selection/IndexRange.cs @@ -8,12 +8,18 @@ using System.Collections.Generic; #nullable enable -namespace Avalonia.Controls +namespace Avalonia.Controls.Selection { internal readonly struct IndexRange : IEquatable { private static readonly IndexRange s_invalid = new IndexRange(int.MinValue, int.MinValue); + public IndexRange(int index) + { + Begin = index; + End = index; + } + public IndexRange(int begin, int end) { // Accept out of order begin/end pairs, just swap them. @@ -87,6 +93,43 @@ namespace Avalonia.Controls public static bool operator ==(IndexRange left, IndexRange right) => left.Equals(right); public static bool operator !=(IndexRange left, IndexRange right) => !(left == right); + public static bool Contains(IReadOnlyList? ranges, int index) + { + if (ranges is null || index < 0) + { + return false; + } + + foreach (var range in ranges) + { + if (range.Contains(index)) + { + return true; + } + } + + return false; + } + + public static int GetAt(IReadOnlyList ranges, int index) + { + var currentIndex = 0; + + foreach (var range in ranges) + { + var currentCount = range.Count; + + if (index >= currentIndex && index < currentIndex + currentCount) + { + return range.Begin + (index - currentIndex); + } + + currentIndex += currentCount; + } + + throw new IndexOutOfRangeException("The index was out of range."); + } + public static int Add( IList ranges, IndexRange range, @@ -132,6 +175,21 @@ namespace Avalonia.Controls return result; } + public static int Add( + IList destination, + IReadOnlyList source, + IList? added = null) + { + var result = 0; + + foreach (var range in source) + { + result += Add(destination, range, added); + } + + return result; + } + public static int Intersect( IList ranges, IndexRange range, @@ -180,10 +238,15 @@ namespace Avalonia.Controls } public static int Remove( - IList ranges, + IList? ranges, IndexRange range, IList? removed = null) { + if (ranges is null) + { + return 0; + } + var result = 0; for (var i = 0; i < ranges.Count; ++i) @@ -224,15 +287,16 @@ namespace Avalonia.Controls return result; } - public static IEnumerable Subtract( - IndexRange lhs, - IEnumerable rhs) + public static int Remove( + IList destination, + IReadOnlyList source, + IList? added = null) { - var result = new List { lhs }; - - foreach (var range in rhs) + var result = 0; + + foreach (var range in source) { - Remove(result, range); + result += Remove(destination, range, added); } return result; diff --git a/src/Avalonia.Controls/Selection/SelectedIndexes.cs b/src/Avalonia.Controls/Selection/SelectedIndexes.cs new file mode 100644 index 0000000000..36df175ed2 --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectedIndexes.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + internal class SelectedIndexes : IReadOnlyList + { + private readonly SelectionModel? _owner; + private readonly IReadOnlyList? _ranges; + + public SelectedIndexes(SelectionModel owner) => _owner = owner; + public SelectedIndexes(IReadOnlyList ranges) => _ranges = ranges; + + public int this[int index] + { + get + { + if (index >= Count) + { + throw new IndexOutOfRangeException("The index was out of range."); + } + + if (_owner?.SingleSelect == true) + { + return _owner.SelectedIndex; + } + else + { + return IndexRange.GetAt(Ranges!, index); + } + } + } + + public int Count + { + get + { + if (_owner?.SingleSelect == true) + { + return _owner.SelectedIndex == -1 ? 0 : 1; + } + else + { + return IndexRange.GetCount(Ranges!); + } + } + } + + private IReadOnlyList Ranges => _ranges ?? _owner!.Ranges!; + + public IEnumerator GetEnumerator() + { + IEnumerator SingleSelect() + { + if (_owner.SelectedIndex >= 0) + { + yield return _owner.SelectedIndex; + } + } + + if (_owner?.SingleSelect == true) + { + return SingleSelect(); + } + else + { + return IndexRange.EnumerateIndices(Ranges).GetEnumerator(); + } + } + + public static SelectedIndexes? Create(IReadOnlyList? ranges) + { + return ranges is object ? new SelectedIndexes(ranges) : null; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Avalonia.Controls/Selection/SelectedItems.cs b/src/Avalonia.Controls/Selection/SelectedItems.cs new file mode 100644 index 0000000000..92781fd54a --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectedItems.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + internal class SelectedItems : IReadOnlyList + { + private readonly SelectionModel? _owner; + private readonly ItemsSourceView? _items; + private readonly IReadOnlyList? _ranges; + + public SelectedItems(SelectionModel owner) => _owner = owner; + + public SelectedItems(IReadOnlyList ranges, ItemsSourceView? items) + { + _ranges = ranges ?? throw new ArgumentNullException(nameof(ranges)); + _items = items; + } + + [MaybeNull] + public T this[int index] + { +#pragma warning disable CS8766 + get +#pragma warning restore CS8766 + { + if (index >= Count) + { + throw new IndexOutOfRangeException("The index was out of range."); + } + + if (_owner?.SingleSelect == true) + { + return _owner.SelectedItem; + } + else if (Items is object) + { + return Items[index]; + } + else + { + return default; + } + } + } + + public int Count + { + get + { + if (_owner?.SingleSelect == true) + { + return _owner.SelectedIndex == -1 ? 0 : 1; + } + else + { + return Ranges is object ? IndexRange.GetCount(Ranges) : 0; + } + } + } + + private ItemsSourceView? Items => _items ?? _owner?.ItemsView; + private IReadOnlyList? Ranges => _ranges ?? _owner!.Ranges; + + public IEnumerator GetEnumerator() + { + if (_owner?.SingleSelect == true) + { + if (_owner.SelectedIndex >= 0) + { +#pragma warning disable CS8603 + yield return _owner.SelectedItem; +#pragma warning restore CS8603 + } + } + else + { + var items = Items; + + foreach (var range in Ranges!) + { + for (var i = range.Begin; i <= range.End; ++i) + { +#pragma warning disable CS8603 + yield return items is object ? items[i] : default; +#pragma warning restore CS8603 + } + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public static SelectedItems? Create( + IReadOnlyList? ranges, + ItemsSourceView? items) + { + return ranges is object ? new SelectedItems(ranges, items) : null; + } + + public class Untyped : IReadOnlyList + { + private readonly IReadOnlyList _source; + public Untyped(IReadOnlyList source) => _source = source; + public object? this[int index] => _source[index]; + public int Count => _source.Count; + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + foreach (var i in _source) + { + yield return i; + } + } + } + } +} diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs new file mode 100644 index 0000000000..7ce2624d02 --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectionModel.cs @@ -0,0 +1,726 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + public class SelectionModel : SelectionNodeBase, ISelectionModel + { + private bool _singleSelect = true; + private int _anchorIndex = -1; + private int _selectedIndex = -1; + private Operation? _operation; + private SelectedIndexes? _selectedIndexes; + private SelectedItems? _selectedItems; + private SelectedItems.Untyped? _selectedItemsUntyped; + private EventHandler? _untypedSelectionChanged; + [AllowNull] private T _initSelectedItem = default; + private bool _hasInitSelectedItem; + + public SelectionModel() + { + } + + public SelectionModel(IEnumerable? source) + { + Source = source; + } + + public new IEnumerable? Source + { + get => base.Source as IEnumerable; + set => SetSource(value); + } + + public bool SingleSelect + { + get => _singleSelect; + set + { + if (_singleSelect != value) + { + if (value == true) + { + using var update = BatchUpdate(); + var selectedIndex = SelectedIndex; + Clear(); + SelectedIndex = selectedIndex; + } + + _singleSelect = value; + RangesEnabled = !value; + + if (RangesEnabled && _selectedIndex >= 0) + { + CommitSelect(new IndexRange(_selectedIndex)); + } + + RaisePropertyChanged(nameof(SingleSelect)); + } + } + } + + public int SelectedIndex + { + get => _selectedIndex; + set + { + using var update = BatchUpdate(); + Clear(); + Select(value); + } + } + + public IReadOnlyList SelectedIndexes => _selectedIndexes ??= new SelectedIndexes(this); + + [MaybeNull, AllowNull] + public T SelectedItem + { + get => ItemsView is object ? GetItemAt(_selectedIndex) : _initSelectedItem; + set + { + if (ItemsView is object) + { + SelectedIndex = ItemsView.IndexOf(value!); + } + else + { + Clear(); + _initSelectedItem = value; + _hasInitSelectedItem = true; + } + } + } + + public IReadOnlyList SelectedItems + { + get + { + if (ItemsView is null && _hasInitSelectedItem) + { + return new[] { _initSelectedItem }; + } + + return _selectedItems ??= new SelectedItems(this); + } + } + + public int AnchorIndex + { + get => _anchorIndex; + set + { + using var update = BatchUpdate(); + var index = CoerceIndex(value); + update.Operation.AnchorIndex = index; + } + } + + public int Count + { + get + { + if (SingleSelect) + { + return _selectedIndex >= 0 ? 1 : 0; + } + else + { + return IndexRange.GetCount(Ranges); + } + } + } + + IEnumerable? ISelectionModel.Source + { + get => Source; + set => SetSource(value); + } + + object? ISelectionModel.SelectedItem + { + get => SelectedItem; + set + { + if (value is T t) + { + SelectedItem = t; + } + else + { + SelectedIndex = -1; + } + } + + } + + IReadOnlyList ISelectionModel.SelectedItems + { + get => _selectedItemsUntyped ??= new SelectedItems.Untyped(SelectedItems); + } + + public event EventHandler? IndexesChanged; + public event EventHandler>? SelectionChanged; + public event EventHandler? LostSelection; + public event EventHandler? SourceReset; + public event PropertyChangedEventHandler? PropertyChanged; + + event EventHandler? ISelectionModel.SelectionChanged + { + add => _untypedSelectionChanged += value; + remove => _untypedSelectionChanged -= value; + } + + public BatchUpdateOperation BatchUpdate() => new BatchUpdateOperation(this); + + public void BeginBatchUpdate() + { + _operation ??= new Operation(this); + ++_operation.UpdateCount; + } + + public void EndBatchUpdate() + { + if (_operation is null || _operation.UpdateCount == 0) + { + throw new InvalidOperationException("No batch update in progress."); + } + + if (--_operation.UpdateCount == 0) + { + // If the collection is currently changing, commit the update when the + // collection change finishes. + if (!IsSourceCollectionChanging) + { + CommitOperation(_operation); + } + } + } + + public bool IsSelected(int index) + { + if (index < 0) + { + return false; + } + else if (SingleSelect) + { + return _selectedIndex == index; + } + else + { + return IndexRange.Contains(Ranges, index); + } + } + + public void Select(int index) => SelectRange(index, index, false, true); + + public void Deselect(int index) => DeselectRange(index, index); + + public void SelectRange(int start, int end) => SelectRange(start, end, false, false); + + public void DeselectRange(int start, int end) + { + using var update = BatchUpdate(); + var o = update.Operation; + var range = CoerceRange(start, end); + + if (range.Begin == -1) + { + return; + } + + if (RangesEnabled) + { + var selected = Ranges.ToList(); + var deselected = new List(); + var operationDeselected = new List(); + + o.DeselectedRanges ??= new List(); + IndexRange.Remove(o.SelectedRanges, range, operationDeselected); + IndexRange.Remove(selected, range, deselected); + IndexRange.Add(o.DeselectedRanges, deselected); + + if (IndexRange.Contains(deselected, o.SelectedIndex) || + IndexRange.Contains(operationDeselected, o.SelectedIndex)) + { + o.SelectedIndex = GetFirstSelectedIndexFromRanges(except: deselected); + } + } + else if(range.Contains(_selectedIndex)) + { + o.SelectedIndex = -1; + } + + _initSelectedItem = default; + _hasInitSelectedItem = false; + } + + public void SelectAll() => SelectRange(0, int.MaxValue); + public void Clear() => DeselectRange(0, int.MaxValue); + + protected void RaisePropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private void SetSource(IEnumerable? value) + { + if (base.Source != value) + { + if (_operation is object) + { + throw new InvalidOperationException("Cannot change source while update is in progress."); + } + + if (base.Source is object && value is object) + { + using var update = BatchUpdate(); + update.Operation.SkipLostSelection = true; + Clear(); + } + + base.Source = value; + + using (var update = BatchUpdate()) + { + update.Operation.IsSourceUpdate = true; + + if (_hasInitSelectedItem) + { + SelectedItem = _initSelectedItem; + _initSelectedItem = default; + _hasInitSelectedItem = false; + } + else + { + TrimInvalidSelections(update.Operation); + } + + RaisePropertyChanged(nameof(Source)); + } + } + } + + private protected override void OnIndexesChanged(int shiftIndex, int shiftDelta) + { + IndexesChanged?.Invoke(this, new SelectionModelIndexesChangedEventArgs(shiftIndex, shiftDelta)); + } + + private protected override void OnSourceReset() + { + _selectedIndex = _anchorIndex = -1; + CommitDeselect(new IndexRange(0, int.MaxValue)); + + if (SourceReset is object) + { + SourceReset.Invoke(this, EventArgs.Empty); + } + else + { + //Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log( + // this, + // "SelectionModel received Reset but no SourceReset handler was registered to handle it. " + + // "Selection may be out of sync.", + // typeof(SelectionModel)); + } + } + + private protected override void OnSelectionChanged(IReadOnlyList deselectedItems) + { + // Note: We're *not* putting this in a using scope. A collection update is still in progress + // so the operation won't get commited by normal means: we have to commit it manually. + var update = BatchUpdate(); + + update.Operation.DeselectedItems = deselectedItems; + + if (_selectedIndex == -1 && LostSelection is object) + { + LostSelection(this, EventArgs.Empty); + } + + CommitOperation(update.Operation); + } + + private protected override CollectionChangeState OnItemsAdded(int index, IList items) + { + var count = items.Count; + var shifted = SelectedIndex >= index; + var shiftCount = shifted ? count : 0; + + _selectedIndex += shiftCount; + _anchorIndex += shiftCount; + + var baseResult = base.OnItemsAdded(index, items); + shifted |= baseResult.ShiftDelta != 0; + + return new CollectionChangeState + { + ShiftIndex = index, + ShiftDelta = shifted ? count : 0, + }; + } + + private protected override CollectionChangeState OnItemsRemoved(int index, IList items) + { + var count = items.Count; + var removedRange = new IndexRange(index, index + count - 1); + var shifted = false; + List? removed; + + var baseResult = base.OnItemsRemoved(index, items); + shifted |= baseResult.ShiftDelta != 0; + removed = baseResult.RemovedItems; + + if (removedRange.Contains(SelectedIndex)) + { + if (SingleSelect) + { +#pragma warning disable CS8604 + removed = new List { (T)items[SelectedIndex - index] }; +#pragma warning restore CS8604 + } + + _selectedIndex = GetFirstSelectedIndexFromRanges(); + } + else if (SelectedIndex >= index) + { + _selectedIndex -= count; + shifted = true; + } + + if (removedRange.Contains(AnchorIndex)) + { + _anchorIndex = GetFirstSelectedIndexFromRanges(); + } + else if (AnchorIndex >= index) + { + _anchorIndex -= count; + shifted = true; + } + + return new CollectionChangeState + { + ShiftIndex = index, + ShiftDelta = shifted ? -count : 0, + RemovedItems = removed, + }; + } + + private protected override void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (_operation?.UpdateCount > 0) + { + throw new InvalidOperationException("Source collection was modified during selection update."); + } + + var oldAnchorIndex = _anchorIndex; + var oldSelectedIndex = _selectedIndex; + + base.OnSourceCollectionChanged(e); + + if (oldSelectedIndex != _selectedIndex) + { + RaisePropertyChanged(nameof(SelectedIndex)); + } + + if (oldAnchorIndex != _anchorIndex) + { + RaisePropertyChanged(nameof(AnchorIndex)); + } + } + + protected override void OnSourceCollectionChangeFinished() + { + if (_operation is object) + { + CommitOperation(_operation); + } + } + + private int GetFirstSelectedIndexFromRanges(List? except = null) + { + if (RangesEnabled) + { + var count = IndexRange.GetCount(Ranges); + var index = 0; + + while (index < count) + { + var result = IndexRange.GetAt(Ranges, index++); + + if (!IndexRange.Contains(except, result)) + { + return result; + } + } + } + + return -1; + } + + private void SelectRange( + int start, + int end, + bool forceSelectedIndex, + bool forceAnchorIndex) + { + if (SingleSelect && start != end) + { + throw new InvalidOperationException("Cannot select range with single selection."); + } + + var range = CoerceRange(start, end); + + if (range.Begin == -1) + { + return; + } + + using var update = BatchUpdate(); + var o = update.Operation; + var selected = new List(); + + if (RangesEnabled) + { + o.SelectedRanges ??= new List(); + IndexRange.Remove(o.DeselectedRanges, range); + IndexRange.Add(o.SelectedRanges, range); + IndexRange.Remove(o.SelectedRanges, Ranges); + + if (o.SelectedIndex == -1 || forceSelectedIndex) + { + o.SelectedIndex = range.Begin; + } + + if (o.AnchorIndex == -1 || forceAnchorIndex) + { + o.AnchorIndex = range.Begin; + } + } + else + { + o.SelectedIndex = o.AnchorIndex = start; + } + + _initSelectedItem = default; + _hasInitSelectedItem = false; + } + + [return: MaybeNull] + private T GetItemAt(int index) + { + if (ItemsView is null || index < 0 || index >= ItemsView.Count) + { + return default; + } + + return ItemsView[index]; + } + + private int CoerceIndex(int index) + { + index = Math.Max(index, -1); + + if (ItemsView is object && index >= ItemsView.Count) + { + index = -1; + } + + return index; + } + + private IndexRange CoerceRange(int start, int end) + { + var max = ItemsView is object ? ItemsView.Count - 1 : int.MaxValue; + + if (start > max || (start < 0 && end < 0)) + { + return new IndexRange(-1); + } + + start = Math.Max(start, 0); + end = Math.Min(end, max); + + return new IndexRange(start, end); + } + + private void TrimInvalidSelections(Operation operation) + { + if (ItemsView is null) + { + return; + } + + var max = ItemsView.Count - 1; + + if (operation.SelectedIndex > max) + { + operation.SelectedIndex = GetFirstSelectedIndexFromRanges(); + } + + if (operation.AnchorIndex > max) + { + operation.AnchorIndex = GetFirstSelectedIndexFromRanges(); + } + + if (RangesEnabled && Ranges.Count > 0) + { + var selected = Ranges.ToList(); + + if (max < 0) + { + operation.DeselectedRanges = selected; + } + else + { + var valid = new IndexRange(0, max); + var removed = new List(); + IndexRange.Intersect(selected, valid, removed); + operation.DeselectedRanges = removed; + } + } + } + + private void CommitOperation(Operation operation) + { + try + { + var oldAnchorIndex = _anchorIndex; + var oldSelectedIndex = _selectedIndex; + var indexesChanged = false; + + if (operation.SelectedIndex == -1 && LostSelection is object && !operation.SkipLostSelection) + { + operation.UpdateCount++; + LostSelection?.Invoke(this, EventArgs.Empty); + } + + _selectedIndex = operation.SelectedIndex; + _anchorIndex = operation.AnchorIndex; + + if (operation.SelectedRanges is object) + { + indexesChanged |= CommitSelect(operation.SelectedRanges) > 0; + } + + if (operation.DeselectedRanges is object) + { + indexesChanged |= CommitDeselect(operation.DeselectedRanges) > 0; + } + + if (SelectionChanged is object || _untypedSelectionChanged is object) + { + IReadOnlyList? deselected = operation.DeselectedRanges; + IReadOnlyList? selected = operation.SelectedRanges; + + if (SingleSelect && oldSelectedIndex != _selectedIndex) + { + if (oldSelectedIndex != -1) + { + deselected = new[] { new IndexRange(oldSelectedIndex) }; + } + + if (_selectedIndex != -1) + { + selected = new[] { new IndexRange(_selectedIndex) }; + } + } + + if (deselected?.Count > 0 || selected?.Count > 0 || operation.DeselectedItems is object) + { + // If the operation was caused by Source being updated, then use a null source + // so that the items will appear as nulls. + var deselectedSource = operation.IsSourceUpdate ? null : ItemsView; + + // If the operation contains DeselectedItems then we're notifying a source + // CollectionChanged event. LostFocus may have caused another item to have been + // selected, but it can't have caused a deselection (as it was called due to + // selection being lost) so we're ok to discard `deselected` here. + var deselectedItems = operation.DeselectedItems ?? + SelectedItems.Create(deselected, deselectedSource); + + var e = new SelectionModelSelectionChangedEventArgs( + SelectedIndexes.Create(deselected), + SelectedIndexes.Create(selected), + deselectedItems, + SelectedItems.Create(selected, ItemsView)); + SelectionChanged?.Invoke(this, e); + _untypedSelectionChanged?.Invoke(this, e); + } + } + + if (oldSelectedIndex != _selectedIndex) + { + indexesChanged = true; + RaisePropertyChanged(nameof(SelectedIndex)); + RaisePropertyChanged(nameof(SelectedItem)); + } + + if (oldAnchorIndex != _anchorIndex) + { + indexesChanged = true; + RaisePropertyChanged(nameof(AnchorIndex)); + } + + if (indexesChanged) + { + RaisePropertyChanged(nameof(SelectedIndexes)); + RaisePropertyChanged(nameof(SelectedItems)); + } + } + finally + { + _operation = null; + } + } + + public struct BatchUpdateOperation : IDisposable + { + private readonly SelectionModel _owner; + private bool _isDisposed; + + public BatchUpdateOperation(SelectionModel owner) + { + _owner = owner; + _isDisposed = false; + owner.BeginBatchUpdate(); + } + + internal Operation Operation => _owner._operation!; + + public void Dispose() + { + if (!_isDisposed) + { + _owner?.EndBatchUpdate(); + _isDisposed = true; + } + } + } + + internal class Operation + { + public Operation(SelectionModel owner) + { + AnchorIndex = owner.AnchorIndex; + SelectedIndex = owner.SelectedIndex; + } + + public int UpdateCount { get; set; } + public bool IsSourceUpdate { get; set; } + public bool SkipLostSelection { get; set; } + public int AnchorIndex { get; set; } + public int SelectedIndex { get; set; } + public List? SelectedRanges { get; set; } + public List? DeselectedRanges { get; set; } + public IReadOnlyList? DeselectedItems { get; set; } + } + } +} diff --git a/src/Avalonia.Controls/Selection/SelectionModelIndexesChangedEventArgs.cs b/src/Avalonia.Controls/Selection/SelectionModelIndexesChangedEventArgs.cs new file mode 100644 index 0000000000..a1fef578a2 --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectionModelIndexesChangedEventArgs.cs @@ -0,0 +1,18 @@ +using System; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + public class SelectionModelIndexesChangedEventArgs : EventArgs + { + public SelectionModelIndexesChangedEventArgs(int startIndex, int delta) + { + StartIndex = startIndex; + Delta = delta; + } + + public int StartIndex { get; } + public int Delta { get; } + } +} diff --git a/src/Avalonia.Controls/Selection/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/Selection/SelectionModelSelectionChangedEventArgs.cs new file mode 100644 index 0000000000..396943592d --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectionModelSelectionChangedEventArgs.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Avalonia.Controls.Selection; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + public abstract class SelectionModelSelectionChangedEventArgs : EventArgs + { + /// + /// Gets the indexes of the items that were removed from the selection. + /// + public abstract IReadOnlyList DeselectedIndexes { get; } + + /// + /// Gets the indexes of the items that were added to the selection. + /// + public abstract IReadOnlyList SelectedIndexes { get; } + + /// + /// Gets the items that were removed from the selection. + /// + public IReadOnlyList DeselectedItems => GetUntypedDeselectedItems(); + + /// + /// Gets the items that were added to the selection. + /// + public IReadOnlyList SelectedItems => GetUntypedSelectedItems(); + + protected abstract IReadOnlyList GetUntypedDeselectedItems(); + protected abstract IReadOnlyList GetUntypedSelectedItems(); + } + + public class SelectionModelSelectionChangedEventArgs : SelectionModelSelectionChangedEventArgs + { + private IReadOnlyList? _deselectedItems; + private IReadOnlyList? _selectedItems; + + public SelectionModelSelectionChangedEventArgs( + IReadOnlyList? deselectedIndices = null, + IReadOnlyList? selectedIndices = null, + IReadOnlyList? deselectedItems = null, + IReadOnlyList? selectedItems = null) + { + DeselectedIndexes = deselectedIndices ?? Array.Empty(); + SelectedIndexes = selectedIndices ?? Array.Empty(); + DeselectedItems = deselectedItems ?? Array.Empty(); + SelectedItems = selectedItems ?? Array.Empty(); + } + + /// + /// Gets the indexes of the items that were removed from the selection. + /// + public override IReadOnlyList DeselectedIndexes { get; } + + /// + /// Gets the indexes of the items that were added to the selection. + /// + public override IReadOnlyList SelectedIndexes { get; } + + /// + /// Gets the items that were removed from the selection. + /// + public new IReadOnlyList DeselectedItems { get; } + + /// + /// Gets the items that were added to the selection. + /// + public new IReadOnlyList SelectedItems { get; } + + protected override IReadOnlyList GetUntypedDeselectedItems() + { + return _deselectedItems ??= (DeselectedItems as IReadOnlyList) ?? + new SelectedItems.Untyped(DeselectedItems); + } + + protected override IReadOnlyList GetUntypedSelectedItems() + { + return _selectedItems ??= (SelectedItems as IReadOnlyList) ?? + new SelectedItems.Untyped(SelectedItems); + } + } +} diff --git a/src/Avalonia.Controls/Selection/SelectionNodeBase.cs b/src/Avalonia.Controls/Selection/SelectionNodeBase.cs new file mode 100644 index 0000000000..ff3b8f43a8 --- /dev/null +++ b/src/Avalonia.Controls/Selection/SelectionNodeBase.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using Avalonia.Controls.Utils; + +#nullable enable + +namespace Avalonia.Controls.Selection +{ + public abstract class SelectionNodeBase : ICollectionChangedListener + { + private IEnumerable? _source; + private bool _rangesEnabled; + private List? _ranges; + private int _collectionChanging; + + protected IEnumerable? Source + { + get => _source; + set + { + if (_source != value) + { + ItemsView?.RemoveListener(this); + _source = value; + ItemsView = value is object ? ItemsSourceView.GetOrCreate(value) : null; + ItemsView?.AddListener(this); + } + } + } + + protected bool IsSourceCollectionChanging => _collectionChanging > 0; + + protected bool RangesEnabled + { + get => _rangesEnabled; + set + { + if (_rangesEnabled != value) + { + _rangesEnabled = value; + + if (!_rangesEnabled) + { + _ranges = null; + } + } + } + } + + internal ItemsSourceView? ItemsView { get; set; } + + internal IReadOnlyList Ranges + { + get + { + if (!RangesEnabled) + { + throw new InvalidOperationException("Ranges not enabled."); + } + + return _ranges ??= new List(); + } + } + + void ICollectionChangedListener.PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + ++_collectionChanging; + } + + void ICollectionChangedListener.Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + OnSourceCollectionChanged(e); + } + + void ICollectionChangedListener.PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e) + { + if (--_collectionChanging == 0) + { + OnSourceCollectionChangeFinished(); + } + } + + protected abstract void OnSourceCollectionChangeFinished(); + + private protected abstract void OnIndexesChanged(int shiftIndex, int shiftDelta); + + private protected abstract void OnSourceReset(); + + private protected abstract void OnSelectionChanged(IReadOnlyList deselectedItems); + + private protected int CommitSelect(IndexRange range) + { + if (RangesEnabled) + { + _ranges ??= new List(); + return IndexRange.Add(_ranges, range); + } + + return 0; + } + + private protected int CommitSelect(IReadOnlyList ranges) + { + if (RangesEnabled) + { + _ranges ??= new List(); + return IndexRange.Add(_ranges, ranges); + } + + return 0; + } + + private protected int CommitDeselect(IndexRange range) + { + if (RangesEnabled) + { + _ranges ??= new List(); + return IndexRange.Remove(_ranges, range); + } + + return 0; + } + + private protected int CommitDeselect(IReadOnlyList ranges) + { + if (RangesEnabled && _ranges is object) + { + return IndexRange.Remove(_ranges, ranges); + } + + return 0; + } + + private protected virtual CollectionChangeState OnItemsAdded(int index, IList items) + { + var count = items.Count; + var shifted = false; + + if (_ranges is object) + { + List? toAdd = null; + + for (var i = 0; i < Ranges!.Count; ++i) + { + var range = Ranges[i]; + + // The range is after the inserted items, need to shift the range right + if (range.End >= index) + { + int begin = range.Begin; + + // If the index left of newIndex is inside the range, + // Split the range and remember the left piece to add later + if (range.Contains(index - 1)) + { + range.Split(index - 1, out var before, out _); + (toAdd ??= new List()).Add(before); + begin = index; + } + + // Shift the range to the right + _ranges[i] = new IndexRange(begin + count, range.End + count); + shifted = true; + } + } + + if (toAdd is object) + { + foreach (var range in toAdd) + { + IndexRange.Add(_ranges, range); + } + } + } + + return new CollectionChangeState + { + ShiftIndex = index, + ShiftDelta = shifted ? count : 0, + }; + } + + private protected virtual CollectionChangeState OnItemsRemoved(int index, IList items) + { + var count = items.Count; + var removedRange = new IndexRange(index, index + count - 1); + bool shifted = false; + List? removed = null; + + if (_ranges is object) + { + var deselected = new List(); + + if (IndexRange.Remove(_ranges, removedRange, deselected) > 0) + { + removed = new List(); + + foreach (var range in deselected) + { + for (var i = range.Begin; i <= range.End; ++i) + { +#pragma warning disable CS8604 + removed.Add((T)items[i - index]); +#pragma warning restore CS8604 + } + } + } + + for (var i = 0; i < Ranges!.Count; ++i) + { + var existing = Ranges[i]; + + if (existing.End > removedRange.Begin) + { + _ranges[i] = new IndexRange(existing.Begin - count, existing.End - count); + shifted = true; + } + } + } + + return new CollectionChangeState + { + ShiftIndex = index, + ShiftDelta = shifted ? -count : 0, + RemovedItems = removed, + }; + } + + private protected virtual void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e) + { + var shiftDelta = 0; + var shiftIndex = -1; + List? removed = null; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + var change = OnItemsAdded(e.NewStartingIndex, e.NewItems); + shiftIndex = change.ShiftIndex; + shiftDelta = change.ShiftDelta; + break; + } + case NotifyCollectionChangedAction.Remove: + { + var change = OnItemsRemoved(e.OldStartingIndex, e.OldItems); + shiftIndex = change.ShiftIndex; + shiftDelta = change.ShiftDelta; + removed = change.RemovedItems; + break; + } + case NotifyCollectionChangedAction.Replace: + { + var removeChange = OnItemsRemoved(e.OldStartingIndex, e.OldItems); + var addChange = OnItemsAdded(e.NewStartingIndex, e.NewItems); + shiftIndex = removeChange.ShiftIndex; + shiftDelta = removeChange.ShiftDelta + addChange.ShiftDelta; + removed = removeChange.RemovedItems; + } + break; + case NotifyCollectionChangedAction.Reset: + OnSourceReset(); + break; + } + + if (shiftDelta != 0) + { + OnIndexesChanged(shiftIndex, shiftDelta); + } + + if (removed is object) + { + OnSelectionChanged(removed); + } + } + + private protected struct CollectionChangeState + { + public int ShiftIndex; + public int ShiftDelta; + public List? RemovedItems; + } + } +} diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs deleted file mode 100644 index aa6552579f..0000000000 --- a/src/Avalonia.Controls/SelectionModel.cs +++ /dev/null @@ -1,894 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Reactive.Linq; -using Avalonia.Controls.Utils; - -#nullable enable - -namespace Avalonia.Controls -{ - public class SelectionModel : ISelectionModel, IDisposable - { - private readonly SelectionNode _rootNode; - private bool _singleSelect; - private bool _autoSelect; - private int _operationCount; - private IndexPath _oldAnchorIndex; - private IReadOnlyList? _selectedIndicesCached; - private IReadOnlyList? _selectedItemsCached; - private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; - - public event EventHandler? ChildrenRequested; - public event PropertyChangedEventHandler? PropertyChanged; - public event EventHandler? SelectionChanged; - - public SelectionModel() - { - _rootNode = new SelectionNode(this, null); - SharedLeafNode = new SelectionNode(this, null); - } - - public object? Source - { - get => _rootNode.Source; - set - { - if (_rootNode.Source != value) - { - var raiseChanged = _rootNode.Source == null && SelectedIndices.Count > 0; - - if (_rootNode.Source != null) - { - // Temporarily prevent auto-select when switching source. - var restoreAutoSelect = _autoSelect; - _autoSelect = false; - - try - { - using (var operation = new Operation(this)) - { - ClearSelection(resetAnchor: true); - } - } - finally - { - _autoSelect = restoreAutoSelect; - } - } - - _rootNode.Source = value; - ApplyAutoSelect(true); - - RaisePropertyChanged("Source"); - - if (raiseChanged) - { - var e = new SelectionModelSelectionChangedEventArgs( - null, - SelectedIndices, - null, - SelectedItems); - OnSelectionChanged(e); - } - } - } - } - - public bool SingleSelect - { - get => _singleSelect; - set - { - if (_singleSelect != value) - { - _singleSelect = value; - var selectedIndices = SelectedIndices; - - if (value && selectedIndices != null && selectedIndices.Count > 0) - { - using var operation = new Operation(this); - - // We want to be single select, so make sure there is only - // one selected item. - var firstSelectionIndexPath = selectedIndices[0]; - ClearSelection(resetAnchor: true); - SelectWithPathImpl(firstSelectionIndexPath, select: true); - SelectedIndex = firstSelectionIndexPath; - } - - RaisePropertyChanged("SingleSelect"); - } - } - } - - public bool RetainSelectionOnReset - { - get => _rootNode.RetainSelectionOnReset; - set => _rootNode.RetainSelectionOnReset = value; - } - - public bool AutoSelect - { - get => _autoSelect; - set - { - if (_autoSelect != value) - { - _autoSelect = value; - ApplyAutoSelect(true); - } - } - } - - public IndexPath AnchorIndex - { - get - { - IndexPath anchor = default; - - if (_rootNode.AnchorIndex >= 0) - { - var path = new List(); - SelectionNode? current = _rootNode; - - while (current?.AnchorIndex >= 0) - { - path.Add(current.AnchorIndex); - current = current.GetAt(current.AnchorIndex, false, default); - } - - anchor = new IndexPath(path); - } - - return anchor; - } - set - { - var oldValue = AnchorIndex; - - if (value != null) - { - SelectionTreeHelper.TraverseIndexPath( - _rootNode, - value, - realizeChildren: true, - (currentNode, path, depth, childIndex) => currentNode.AnchorIndex = path.GetAt(depth)); - } - else - { - _rootNode.AnchorIndex = -1; - } - - if (_operationCount == 0 && oldValue != AnchorIndex) - { - RaisePropertyChanged("AnchorIndex"); - } - } - } - - public IndexPath SelectedIndex - { - get - { - IndexPath selectedIndex = default; - var selectedIndices = SelectedIndices; - - if (selectedIndices?.Count > 0) - { - selectedIndex = selectedIndices[0]; - } - - return selectedIndex; - } - set - { - if (!IsSelectedAt(value) || SelectedItems.Count > 1) - { - using var operation = new Operation(this); - ClearSelection(resetAnchor: true); - SelectWithPathImpl(value, select: true); - } - } - } - - public object? SelectedItem - { - get - { - object? item = null; - var selectedItems = SelectedItems; - - if (selectedItems?.Count > 0) - { - item = selectedItems[0]; - } - - return item; - } - } - - public IReadOnlyList SelectedItems - { - get - { - if (_selectedItemsCached == null) - { - var selectedInfos = new List(); - var count = 0; - - if (_rootNode.Source != null) - { - SelectionTreeHelper.Traverse( - _rootNode, - realizeChildren: false, - currentInfo => - { - if (currentInfo.Node.SelectedCount > 0) - { - selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); - count += currentInfo.Node.SelectedCount; - } - }); - } - - // Instead of creating a dumb vector that takes up the space for all the selected items, - // we create a custom IReadOnlyList implementation that calls back using a delegate to find - // the selected item at a particular index. This avoid having to create the storage and copying - // needed in a dumb vector. This also allows us to expose a tree of selected nodes into an - // easier to consume flat vector view of objects. - var selectedItems = new SelectedItems ( - selectedInfos, - count, - (infos, index) => - { - var currentIndex = 0; - object? item = null; - - foreach (var info in infos) - { - var node = info.Node; - - if (node != null) - { - var currentCount = node.SelectedCount; - - if (index >= currentIndex && index < currentIndex + currentCount) - { - var targetIndex = node.SelectedIndices[index - currentIndex]; - item = node.ItemsSourceView!.GetAt(targetIndex); - break; - } - - currentIndex += currentCount; - } - else - { - throw new InvalidOperationException( - "Selection has changed since SelectedItems property was read."); - } - } - - return item; - }); - - _selectedItemsCached = selectedItems; - } - - return _selectedItemsCached; - } - } - - public IReadOnlyList SelectedIndices - { - get - { - if (_selectedIndicesCached == null) - { - var selectedInfos = new List(); - var count = 0; - - SelectionTreeHelper.Traverse( - _rootNode, - false, - currentInfo => - { - if (currentInfo.Node.SelectedCount > 0) - { - selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); - count += currentInfo.Node.SelectedCount; - } - }); - - // Instead of creating a dumb vector that takes up the space for all the selected indices, - // we create a custom VectorView implimentation that calls back using a delegate to find - // the IndexPath at a particular index. This avoid having to create the storage and copying - // needed in a dumb vector. This also allows us to expose a tree of selected nodes into an - // easier to consume flat vector view of IndexPaths. - var indices = new SelectedItems( - selectedInfos, - count, - (infos, index) => // callback for GetAt(index) - { - var currentIndex = 0; - IndexPath path = default; - - foreach (var info in infos) - { - var node = info.Node; - - if (node != null) - { - var currentCount = node.SelectedCount; - if (index >= currentIndex && index < currentIndex + currentCount) - { - int targetIndex = node.SelectedIndices[index - currentIndex]; - path = info.Path.CloneWithChildIndex(targetIndex); - break; - } - - currentIndex += currentCount; - } - else - { - throw new InvalidOperationException( - "Selection has changed since SelectedIndices property was read."); - } - } - - return path; - }); - - _selectedIndicesCached = indices; - } - - return _selectedIndicesCached; - } - } - - internal SelectionNode SharedLeafNode { get; private set; } - - public void Dispose() - { - ClearSelection(resetAnchor: false); - _rootNode.Cleanup(); - _rootNode.Dispose(); - _selectedIndicesCached = null; - _selectedItemsCached = null; - } - - public void SetAnchorIndex(int index) => AnchorIndex = new IndexPath(index); - - public void SetAnchorIndex(int groupIndex, int index) => AnchorIndex = new IndexPath(groupIndex, index); - - public void Select(int index) - { - using var operation = new Operation(this); - SelectImpl(index, select: true); - } - - public void Select(int groupIndex, int itemIndex) - { - using var operation = new Operation(this); - SelectWithGroupImpl(groupIndex, itemIndex, select: true); - } - - public void SelectAt(IndexPath index) - { - using var operation = new Operation(this); - SelectWithPathImpl(index, select: true); - } - - public void Deselect(int index) - { - using var operation = new Operation(this); - SelectImpl(index, select: false); - } - - public void Deselect(int groupIndex, int itemIndex) - { - using var operation = new Operation(this); - SelectWithGroupImpl(groupIndex, itemIndex, select: false); - } - - public void DeselectAt(IndexPath index) - { - using var operation = new Operation(this); - SelectWithPathImpl(index, select: false); - } - - public bool IsSelected(int index) => _rootNode.IsSelected(index); - - public bool IsSelected(int grouIndex, int itemIndex) - { - return IsSelectedAt(new IndexPath(grouIndex, itemIndex)); - } - - public bool IsSelectedAt(IndexPath index) - { - var path = index; - SelectionNode? node = _rootNode; - - for (int i = 0; i < path.GetSize() - 1; i++) - { - var childIndex = path.GetAt(i); - node = node.GetAt(childIndex, false, default); - - if (node == null) - { - return false; - } - } - - return node.IsSelected(index.GetAt(index.GetSize() - 1)); - } - - public bool? IsSelectedWithPartial(int index) - { - if (index < 0) - { - throw new ArgumentException("Index must be >= 0", nameof(index)); - } - - var isSelected = _rootNode.IsSelectedWithPartial(index); - return isSelected; - } - - public bool? IsSelectedWithPartial(int groupIndex, int itemIndex) - { - if (groupIndex < 0) - { - throw new ArgumentException("Group index must be >= 0", nameof(groupIndex)); - } - - if (itemIndex < 0) - { - throw new ArgumentException("Item index must be >= 0", nameof(itemIndex)); - } - - var isSelected = (bool?)false; - var childNode = _rootNode.GetAt(groupIndex, false, default); - - if (childNode != null) - { - isSelected = childNode.IsSelectedWithPartial(itemIndex); - } - - return isSelected; - } - - public bool? IsSelectedWithPartialAt(IndexPath index) - { - var path = index; - var isRealized = true; - SelectionNode? node = _rootNode; - - for (int i = 0; i < path.GetSize() - 1; i++) - { - var childIndex = path.GetAt(i); - node = node.GetAt(childIndex, false, default); - - if (node == null) - { - isRealized = false; - break; - } - } - - var isSelected = (bool?)false; - - if (isRealized) - { - var size = path.GetSize(); - if (size == 0) - { - isSelected = SelectionNode.ConvertToNullableBool(node!.EvaluateIsSelectedBasedOnChildrenNodes()); - } - else - { - isSelected = node!.IsSelectedWithPartial(path.GetAt(size - 1)); - } - } - - return isSelected; - } - - public void SelectRangeFromAnchor(int index) - { - using var operation = new Operation(this); - SelectRangeFromAnchorImpl(index, select: true); - } - - public void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex) - { - using var operation = new Operation(this); - SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, select: true); - } - - public void SelectRangeFromAnchorTo(IndexPath index) - { - using var operation = new Operation(this); - SelectRangeImpl(AnchorIndex, index, select: true); - } - - public void DeselectRangeFromAnchor(int index) - { - using var operation = new Operation(this); - SelectRangeFromAnchorImpl(index, select: false); - } - - public void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex) - { - using var operation = new Operation(this); - SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, false /* select */); - } - - public void DeselectRangeFromAnchorTo(IndexPath index) - { - using var operation = new Operation(this); - SelectRangeImpl(AnchorIndex, index, select: false); - } - - public void SelectRange(IndexPath start, IndexPath end) - { - using var operation = new Operation(this); - SelectRangeImpl(start, end, select: true); - } - - public void DeselectRange(IndexPath start, IndexPath end) - { - using var operation = new Operation(this); - SelectRangeImpl(start, end, select: false); - } - - public void SelectAll() - { - using var operation = new Operation(this); - - SelectionTreeHelper.Traverse( - _rootNode, - realizeChildren: true, - info => - { - if (info.Node.DataCount > 0) - { - info.Node.SelectAll(); - } - }); - } - - public void ClearSelection() - { - using var operation = new Operation(this); - ClearSelection(resetAnchor: true); - } - - public IDisposable Update() => new Operation(this); - - protected void OnPropertyChanged(string propertyName) - { - RaisePropertyChanged(propertyName); - } - - private void RaisePropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public void OnSelectionInvalidatedDueToCollectionChange( - bool selectionInvalidated, - IReadOnlyList? removedItems) - { - SelectionModelSelectionChangedEventArgs? e = null; - - if (selectionInvalidated) - { - e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); - } - - OnSelectionChanged(e); - ApplyAutoSelect(true); - } - - internal IObservable? ResolvePath( - object data, - IndexPath dataIndexPath, - IndexPath finalIndexPath) - { - IObservable? resolved = null; - - // Raise ChildrenRequested event if there is a handler - if (ChildrenRequested != null) - { - if (_childrenRequestedEventArgs == null) - { - _childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs( - data, - dataIndexPath, - finalIndexPath, - false); - } - else - { - _childrenRequestedEventArgs.Initialize(data, dataIndexPath, finalIndexPath, false); - } - - ChildrenRequested(this, _childrenRequestedEventArgs); - resolved = _childrenRequestedEventArgs.Children; - - // Clear out the values in the args so that it cannot be used after the event handler call. - _childrenRequestedEventArgs.Initialize(null, default, default, true); - } - - return resolved; - } - - private void ClearSelection(bool resetAnchor) - { - SelectionTreeHelper.Traverse( - _rootNode, - realizeChildren: false, - info => info.Node.Clear()); - - if (resetAnchor) - { - AnchorIndex = default; - } - - OnSelectionChanged(); - } - - private void OnSelectionChanged(SelectionModelSelectionChangedEventArgs? e = null) - { - _selectedIndicesCached = null; - _selectedItemsCached = null; - - if (e != null) - { - SelectionChanged?.Invoke(this, e); - - RaisePropertyChanged(nameof(SelectedIndex)); - RaisePropertyChanged(nameof(SelectedIndices)); - - if (_rootNode.Source != null) - { - RaisePropertyChanged(nameof(SelectedItem)); - RaisePropertyChanged(nameof(SelectedItems)); - } - } - } - - private void SelectImpl(int index, bool select) - { - if (_singleSelect) - { - ClearSelection(resetAnchor: true); - } - - var selected = _rootNode.Select(index, select); - - if (selected) - { - AnchorIndex = new IndexPath(index); - } - - OnSelectionChanged(); - } - - private void SelectWithGroupImpl(int groupIndex, int itemIndex, bool select) - { - if (_singleSelect) - { - ClearSelection(resetAnchor: true); - } - - var childNode = _rootNode.GetAt(groupIndex, true, new IndexPath(groupIndex, itemIndex)); - var selected = childNode!.Select(itemIndex, select); - - if (selected) - { - AnchorIndex = new IndexPath(groupIndex, itemIndex); - } - - OnSelectionChanged(); - } - - private void SelectWithPathImpl(IndexPath index, bool select) - { - bool selected = false; - - if (_singleSelect) - { - ClearSelection(resetAnchor: true); - } - - SelectionTreeHelper.TraverseIndexPath( - _rootNode, - index, - true, - (currentNode, path, depth, childIndex) => - { - if (depth == path.GetSize() - 1) - { - selected = currentNode.Select(childIndex, select); - } - } - ); - - if (selected) - { - AnchorIndex = index; - } - - OnSelectionChanged(); - } - - private void SelectRangeFromAnchorImpl(int index, bool select) - { - int anchorIndex = 0; - var anchor = AnchorIndex; - - if (anchor != null) - { - anchorIndex = anchor.GetAt(0); - } - - _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); - OnSelectionChanged(); - } - - private void SelectRangeFromAnchorWithGroupImpl(int endGroupIndex, int endItemIndex, bool select) - { - var startGroupIndex = 0; - var startItemIndex = 0; - var anchorIndex = AnchorIndex; - - if (anchorIndex != null) - { - startGroupIndex = anchorIndex.GetAt(0); - startItemIndex = anchorIndex.GetAt(1); - } - - // Make sure start > end - if (startGroupIndex > endGroupIndex || - (startGroupIndex == endGroupIndex && startItemIndex > endItemIndex)) - { - int temp = startGroupIndex; - startGroupIndex = endGroupIndex; - endGroupIndex = temp; - temp = startItemIndex; - startItemIndex = endItemIndex; - endItemIndex = temp; - } - - for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) - { - var groupNode = _rootNode.GetAt(groupIdx, true, new IndexPath(endGroupIndex, endItemIndex))!; - int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; - int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; - groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); - } - - OnSelectionChanged(); - } - - private void SelectRangeImpl(IndexPath start, IndexPath end, bool select) - { - var winrtStart = start; - var winrtEnd = end; - - // Make sure start <= end - if (winrtEnd.CompareTo(winrtStart) == -1) - { - var temp = winrtStart; - winrtStart = winrtEnd; - winrtEnd = temp; - } - - // Note: Since we do not know the depth of the tree, we have to walk to each leaf - SelectionTreeHelper.TraverseRangeRealizeChildren( - _rootNode, - winrtStart, - winrtEnd, - info => - { - if (info.Path >= winrtStart && info.Path <= winrtEnd) - { - info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); - } - }); - - OnSelectionChanged(); - } - - private void BeginOperation() - { - if (_operationCount++ == 0) - { - _oldAnchorIndex = AnchorIndex; - _rootNode.BeginOperation(); - } - } - - private void EndOperation() - { - if (_operationCount == 0) - { - throw new AvaloniaInternalException("No selection operation in progress."); - } - - SelectionModelSelectionChangedEventArgs? e = null; - - if (--_operationCount == 0) - { - ApplyAutoSelect(false); - - var changes = new List(); - _rootNode.EndOperation(changes); - - if (changes.Count > 0) - { - var changeSet = new SelectionModelChangeSet(changes); - e = changeSet.CreateEventArgs(); - } - - OnSelectionChanged(e); - - if (_oldAnchorIndex != AnchorIndex) - { - RaisePropertyChanged(nameof(AnchorIndex)); - } - - _rootNode.Cleanup(); - _oldAnchorIndex = default; - } - } - - private void ApplyAutoSelect(bool createOperation) - { - if (AutoSelect) - { - _selectedIndicesCached = null; - - if (SelectedIndex == default && _rootNode.ItemsSourceView?.Count > 0) - { - if (createOperation) - { - using var operation = new Operation(this); - SelectImpl(0, true); - } - else - { - SelectImpl(0, true); - } - } - } - } - - internal class SelectedItemInfo : ISelectedItemInfo - { - public SelectedItemInfo(SelectionNode node, IndexPath path) - { - Node = node; - Path = path; - } - - public SelectionNode Node { get; } - public IndexPath Path { get; } - public int Count => Node.SelectedCount; - } - - private struct Operation : IDisposable - { - private readonly SelectionModel _manager; - public Operation(SelectionModel manager) => (_manager = manager).BeginOperation(); - public void Dispose() => _manager.EndOperation(); - } - } -} diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs deleted file mode 100644 index d1df38656a..0000000000 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; - -#nullable enable - -namespace Avalonia.Controls -{ - internal class SelectionModelChangeSet - { - private readonly List _changes; - - public SelectionModelChangeSet(List changes) - { - _changes = changes; - } - - public SelectionModelSelectionChangedEventArgs CreateEventArgs() - { - var deselectedIndexCount = 0; - var selectedIndexCount = 0; - var deselectedItemCount = 0; - var selectedItemCount = 0; - - foreach (var change in _changes) - { - deselectedIndexCount += change.DeselectedCount; - selectedIndexCount += change.SelectedCount; - - if (change.Items != null) - { - deselectedItemCount += change.DeselectedCount; - selectedItemCount += change.SelectedCount; - } - } - - var deselectedIndices = new SelectedItems( - _changes, - deselectedIndexCount, - GetDeselectedIndexAt); - var selectedIndices = new SelectedItems( - _changes, - selectedIndexCount, - GetSelectedIndexAt); - var deselectedItems = new SelectedItems( - _changes, - deselectedItemCount, - GetDeselectedItemAt); - var selectedItems = new SelectedItems( - _changes, - selectedItemCount, - GetSelectedItemAt); - - return new SelectionModelSelectionChangedEventArgs( - deselectedIndices, - selectedIndices, - deselectedItems, - selectedItems); - } - - private IndexPath GetDeselectedIndexAt( - List infos, - int index) - { - static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; - static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; - return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); - } - - private IndexPath GetSelectedIndexAt( - List infos, - int index) - { - static int GetCount(SelectionNodeOperation info) => info.SelectedCount; - static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; - return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); - } - - private object? GetDeselectedItemAt( - List infos, - int index) - { - static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.DeselectedCount : 0; - static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; - return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); - } - - private object? GetSelectedItemAt( - List infos, - int index) - { - static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.SelectedCount : 0; - static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; - return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); - } - - private IndexPath GetIndexAt( - List infos, - int index, - Func getCount, - Func?> getRanges) - { - var currentIndex = 0; - IndexPath path = default; - - foreach (var info in infos) - { - var currentCount = getCount(info); - - if (index >= currentIndex && index < currentIndex + currentCount) - { - int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); - path = info.Path.CloneWithChildIndex(targetIndex); - break; - } - - currentIndex += currentCount; - } - - return path; - } - - private object? GetItemAt( - List infos, - int index, - Func getCount, - Func?> getRanges) - { - var currentIndex = 0; - object? item = null; - - foreach (var info in infos) - { - var currentCount = getCount(info); - - if (index >= currentIndex && index < currentIndex + currentCount) - { - int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); - item = info.Items?.Count > targetIndex ? info.Items?.GetAt(targetIndex) : null; - break; - } - - currentIndex += currentCount; - } - - return item; - } - - private int GetIndexAt(List? ranges, int index) - { - var currentIndex = 0; - - if (ranges != null) - { - foreach (var range in ranges) - { - var currentCount = (range.End - range.Begin) + 1; - - if (index >= currentIndex && index < currentIndex + currentCount) - { - return range.Begin + (index - currentIndex); - } - - currentIndex += currentCount; - } - } - - throw new IndexOutOfRangeException(); - } - } -} diff --git a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs deleted file mode 100644 index b1f3e0b2c4..0000000000 --- a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs +++ /dev/null @@ -1,103 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; - -#nullable enable - -namespace Avalonia.Controls -{ - /// - /// Provides data for the event. - /// - public class SelectionModelChildrenRequestedEventArgs : EventArgs - { - private object? _source; - private IndexPath _sourceIndexPath; - private IndexPath _finalIndexPath; - private bool _throwOnAccess; - - internal SelectionModelChildrenRequestedEventArgs( - object source, - IndexPath sourceIndexPath, - IndexPath finalIndexPath, - bool throwOnAccess) - { - source = source ?? throw new ArgumentNullException(nameof(source)); - Initialize(source, sourceIndexPath, finalIndexPath, throwOnAccess); - } - - /// - /// Gets or sets an observable which produces the children of the - /// object. - /// - public IObservable? Children { get; set; } - - /// - /// Gets the object whose children are being requested. - /// - public object Source - { - get - { - if (_throwOnAccess) - { - throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); - } - - return _source!; - } - } - - /// - /// Gets the index of the object whose children are being requested. - /// - public IndexPath SourceIndex - { - get - { - if (_throwOnAccess) - { - throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); - } - - return _sourceIndexPath; - } - } - - /// - /// Gets the index of the final object which is being attempted to be retrieved. - /// - public IndexPath FinalIndex - { - get - { - if (_throwOnAccess) - { - throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); - } - - return _finalIndexPath; - } - } - - internal void Initialize( - object? source, - IndexPath sourceIndexPath, - IndexPath finalIndexPath, - bool throwOnAccess) - { - if (!throwOnAccess && source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - _source = source; - _sourceIndexPath = sourceIndexPath; - _finalIndexPath = finalIndexPath; - _throwOnAccess = throwOnAccess; - } - } -} diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs deleted file mode 100644 index 5e2efdf331..0000000000 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ /dev/null @@ -1,47 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections.Generic; - -#nullable enable - -namespace Avalonia.Controls -{ - public class SelectionModelSelectionChangedEventArgs : EventArgs - { - public SelectionModelSelectionChangedEventArgs( - IReadOnlyList? deselectedIndices, - IReadOnlyList? selectedIndices, - IReadOnlyList? deselectedItems, - IReadOnlyList? selectedItems) - { - DeselectedIndices = deselectedIndices ?? Array.Empty(); - SelectedIndices = selectedIndices ?? Array.Empty(); - DeselectedItems = deselectedItems ?? Array.Empty(); - SelectedItems= selectedItems ?? Array.Empty(); - } - - /// - /// Gets the indices of the items that were removed from the selection. - /// - public IReadOnlyList DeselectedIndices { get; } - - /// - /// Gets the indices of the items that were added to the selection. - /// - public IReadOnlyList SelectedIndices { get; } - - /// - /// Gets the items that were removed from the selection. - /// - public IReadOnlyList DeselectedItems { get; } - - /// - /// Gets the items that were added to the selection. - /// - public IReadOnlyList SelectedItems { get; } - } -} diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs deleted file mode 100644 index d99606673e..0000000000 --- a/src/Avalonia.Controls/SelectionNode.cs +++ /dev/null @@ -1,971 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using Avalonia.Controls.Utils; - -#nullable enable - -namespace Avalonia.Controls -{ - /// - /// Tracks nested selection. - /// - /// - /// SelectionNode is the internal tree data structure that we keep track of for selection in - /// a nested scenario. This would map to one ItemsSourceView/Collection. This node reacts to - /// collection changes and keeps the selected indices up to date. This can either be a leaf - /// node or a non leaf node. - /// - internal class SelectionNode : IDisposable - { - private readonly SelectionModel _manager; - private readonly List _childrenNodes = new List(); - private readonly SelectionNode? _parent; - private readonly List _selected = new List(); - private readonly List _selectedIndicesCached = new List(); - private IDisposable? _childrenSubscription; - private SelectionNodeOperation? _operation; - private object? _source; - private bool _selectedIndicesCacheIsValid; - private bool _retainSelectionOnReset; - private List? _selectedItems; - - public SelectionNode(SelectionModel manager, SelectionNode? parent) - { - _manager = manager; - _parent = parent; - } - - public int AnchorIndex { get; set; } = -1; - - public bool RetainSelectionOnReset - { - get => _retainSelectionOnReset; - set - { - if (_retainSelectionOnReset != value) - { - _retainSelectionOnReset = value; - - if (_retainSelectionOnReset) - { - _selectedItems = new List(); - PopulateSelectedItemsFromSelectedIndices(); - } - else - { - _selectedItems = null; - } - - foreach (var child in _childrenNodes) - { - if (child != null) - { - child.RetainSelectionOnReset = value; - } - } - } - } - } - - public object? Source - { - get => _source; - set - { - if (_source != value) - { - if (_source != null) - { - ClearSelection(); - ClearChildNodes(); - UnhookCollectionChangedHandler(); - } - - _source = value; - - // Setup ItemsSourceView - var newDataSource = value as ItemsSourceView; - - if (value != null && newDataSource == null) - { - newDataSource = new ItemsSourceView((IEnumerable)value); - } - - ItemsSourceView = newDataSource; - - TrimInvalidSelections(); - PopulateSelectedItemsFromSelectedIndices(); - HookupCollectionChangedHandler(); - OnSelectionChanged(); - } - } - } - - private void TrimInvalidSelections() - { - if (_selected == null || ItemsSourceView == null) - { - return; - } - - var validRange = ItemsSourceView.Count > 0 ? new IndexRange(0, ItemsSourceView.Count - 1) : new IndexRange(-1, -1); - var removed = new List(); - var removedCount = IndexRange.Intersect(_selected, validRange, removed); - - if (removedCount > 0) - { - using var operation = _manager.Update(); - SelectedCount -= removedCount; - OnSelectionChanged(); - _operation!.Deselected(removed); - } - } - - public ItemsSourceView? ItemsSourceView { get; private set; } - public int DataCount => ItemsSourceView?.Count ?? 0; - public int ChildrenNodeCount => _childrenNodes.Count; - public int RealizedChildrenNodeCount { get; private set; } - - public IndexPath IndexPath - { - get - { - var path = new List(); ; - var parent = _parent; - var child = this; - - while (parent != null) - { - var childNodes = parent._childrenNodes; - var index = childNodes.IndexOf(child); - - // We are walking up to the parent, so the path will be backwards - path.Insert(0, index); - child = parent; - parent = parent._parent; - } - - return new IndexPath(path); - } - } - - // For a genuine tree view, we dont know which node is leaf until we - // actually walk to it, so currently the tree builds up to the leaf. I don't - // create a bunch of leaf node instances - instead i use the same instance m_leafNode to avoid - // an explosion of node objects. However, I'm still creating the m_childrenNodes - // collection unfortunately. - public SelectionNode? GetAt(int index, bool realizeChild, IndexPath finalIndexPath) - { - SelectionNode? child = null; - - if (realizeChild) - { - if (ItemsSourceView == null || index < 0 || index >= ItemsSourceView.Count) - { - throw new IndexOutOfRangeException(); - } - - if (_childrenNodes.Count == 0) - { - if (ItemsSourceView != null) - { - for (int i = 0; i < ItemsSourceView.Count; i++) - { - _childrenNodes.Add(null); - } - } - } - - if (_childrenNodes[index] == null) - { - var childData = ItemsSourceView!.GetAt(index); - IObservable? resolver = null; - - if (childData != null) - { - var childDataIndexPath = IndexPath.CloneWithChildIndex(index); - resolver = _manager.ResolvePath(childData, childDataIndexPath, finalIndexPath); - } - - if (resolver != null) - { - child = new SelectionNode(_manager, parent: this); - child.SetChildrenObservable(resolver); - } - else if (childData is IEnumerable || childData is IList) - { - child = new SelectionNode(_manager, parent: this); - child.Source = childData; - } - else - { - child = _manager.SharedLeafNode; - } - - if (_operation != null && child != _manager.SharedLeafNode) - { - child.BeginOperation(); - } - - _childrenNodes[index] = child; - RealizedChildrenNodeCount++; - } - else - { - child = _childrenNodes[index]; - } - } - else - { - if (_childrenNodes.Count > 0) - { - child = _childrenNodes[index]; - } - } - - return child; - } - - public void SetChildrenObservable(IObservable resolver) - { - _childrenSubscription = resolver.Subscribe(x => - { - if (Source != null) - { - using (_manager.Update()) - { - SelectionTreeHelper.Traverse( - this, - realizeChildren: false, - info => info.Node.Clear()); - } - } - - Source = x; - }); - } - - public int SelectedCount { get; private set; } - - public bool IsSelected(int index) - { - var isSelected = false; - - foreach (var range in _selected) - { - if (range.Contains(index)) - { - isSelected = true; - break; - } - } - - return isSelected; - } - - // True -> Selected - // False -> Not Selected - // Null -> Some descendents are selected and some are not - public bool? IsSelectedWithPartial() - { - var isSelected = (bool?)false; - - if (_parent != null) - { - var parentsChildren = _parent._childrenNodes; - - var myIndexInParent = parentsChildren.IndexOf(this); - - if (myIndexInParent != -1) - { - isSelected = _parent.IsSelectedWithPartial(myIndexInParent); - } - } - - return isSelected; - } - - // True -> Selected - // False -> Not Selected - // Null -> Some descendents are selected and some are not - public bool? IsSelectedWithPartial(int index) - { - SelectionState selectionState; - - if (_childrenNodes.Count == 0 || // no nodes realized - _childrenNodes.Count <= index || // target node is not realized - _childrenNodes[index] == null || // target node is not realized - _childrenNodes[index] == _manager.SharedLeafNode) // target node is a leaf node. - { - // Ask parent if the target node is selected. - selectionState = IsSelected(index) ? SelectionState.Selected : SelectionState.NotSelected; - } - else - { - // targetNode is the node representing the index. This node is the parent. - // targetNode is a non-leaf node, containing one or many children nodes. Evaluate - // based on children of targetNode. - var targetNode = _childrenNodes[index]; - selectionState = targetNode!.EvaluateIsSelectedBasedOnChildrenNodes(); - } - - return ConvertToNullableBool(selectionState); - } - - public int SelectedIndex - { - get => SelectedCount > 0 ? SelectedIndices[0] : -1; - set - { - if (IsValidIndex(value) && (SelectedCount != 1 || !IsSelected(value))) - { - ClearSelection(); - - if (value != -1) - { - Select(value, true); - } - } - } - } - - public List SelectedIndices - { - get - { - if (!_selectedIndicesCacheIsValid) - { - _selectedIndicesCacheIsValid = true; - - foreach (var range in _selected) - { - for (int index = range.Begin; index <= range.End; index++) - { - // Avoid duplicates - if (!_selectedIndicesCached.Contains(index)) - { - _selectedIndicesCached.Add(index); - } - } - } - - // Sort the list for easy consumption - _selectedIndicesCached.Sort(); - } - - return _selectedIndicesCached; - } - } - - public IEnumerable SelectedItems - { - get => SelectedIndices.Select(x => ItemsSourceView!.GetAt(x)); - } - - public void Dispose() - { - _childrenSubscription?.Dispose(); - ItemsSourceView?.Dispose(); - ClearChildNodes(); - UnhookCollectionChangedHandler(); - } - - public void BeginOperation() - { - if (_operation != null) - { - throw new AvaloniaInternalException("Selection operation already in progress."); - } - - _operation = new SelectionNodeOperation(this); - - for (var i = 0; i < _childrenNodes.Count; ++i) - { - var child = _childrenNodes[i]; - - if (child != null && child != _manager.SharedLeafNode) - { - child.BeginOperation(); - } - } - } - - public void EndOperation(List changes) - { - if (_operation == null) - { - throw new AvaloniaInternalException("No selection operation in progress."); - } - - if (_operation.HasChanges) - { - changes.Add(_operation); - } - - _operation = null; - - for (var i = 0; i < _childrenNodes.Count; ++i) - { - var child = _childrenNodes[i]; - - if (child != null && child != _manager.SharedLeafNode) - { - child.EndOperation(changes); - } - } - } - - public bool Cleanup() - { - var result = SelectedCount == 0; - - for (var i = 0; i < _childrenNodes.Count; ++i) - { - var child = _childrenNodes[i]; - - if (child != null) - { - if (child.Cleanup()) - { - child.Dispose(); - _childrenNodes[i] = null; - } - else - { - result = false; - } - } - } - - return result; - } - - public bool Select(int index, bool select) - { - return Select(index, select, raiseOnSelectionChanged: true); - } - - public bool ToggleSelect(int index) - { - return Select(index, !IsSelected(index)); - } - - public void SelectAll() - { - if (ItemsSourceView != null) - { - var size = ItemsSourceView.Count; - - if (size > 0) - { - SelectRange(new IndexRange(0, size - 1), select: true); - } - } - } - - public void Clear() => ClearSelection(); - - public bool SelectRange(IndexRange range, bool select) - { - if (IsValidIndex(range.Begin) && IsValidIndex(range.End)) - { - if (select) - { - AddRange(range, raiseOnSelectionChanged: true); - } - else - { - RemoveRange(range, raiseOnSelectionChanged: true); - } - - return true; - } - - return false; - } - - private void HookupCollectionChangedHandler() - { - if (ItemsSourceView != null) - { - ItemsSourceView.CollectionChanged += OnSourceListChanged; - } - } - - private void UnhookCollectionChangedHandler() - { - if (ItemsSourceView != null) - { - ItemsSourceView.CollectionChanged -= OnSourceListChanged; - } - } - - private bool IsValidIndex(int index) - { - return ItemsSourceView == null || (index >= 0 && index < ItemsSourceView.Count); - } - - private void AddRange(IndexRange addRange, bool raiseOnSelectionChanged) - { - var selected = new List(); - - SelectedCount += IndexRange.Add(_selected, addRange, selected); - - if (selected.Count > 0) - { - _operation?.Selected(selected); - - if (_selectedItems != null && ItemsSourceView != null) - { - for (var i = addRange.Begin; i <= addRange.End; ++i) - { - _selectedItems.Add(ItemsSourceView!.GetAt(i)); - } - } - - if (raiseOnSelectionChanged) - { - OnSelectionChanged(); - } - } - } - - private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) - { - var removed = new List(); - - SelectedCount -= IndexRange.Remove(_selected, removeRange, removed); - - if (removed.Count > 0) - { - _operation?.Deselected(removed); - - if (_selectedItems != null) - { - for (var i = removeRange.Begin; i <= removeRange.End; ++i) - { - _selectedItems.Remove(ItemsSourceView!.GetAt(i)); - } - } - - if (raiseOnSelectionChanged) - { - OnSelectionChanged(); - } - } - } - - private void ClearSelection() - { - // Deselect all items - if (_selected.Count > 0) - { - _operation?.Deselected(_selected); - _selected.Clear(); - OnSelectionChanged(); - } - - _selectedItems?.Clear(); - SelectedCount = 0; - AnchorIndex = -1; - } - - private void ClearChildNodes() - { - for (int i = 0; i < _childrenNodes.Count; i++) - { - var child = _childrenNodes[i]; - - if (child != null && child != _manager.SharedLeafNode) - { - child.Dispose(); - _childrenNodes[i] = null; - } - } - - RealizedChildrenNodeCount = 0; - } - - private bool Select(int index, bool select, bool raiseOnSelectionChanged) - { - if (IsValidIndex(index)) - { - // Ignore duplicate selection calls - if (IsSelected(index) == select) - { - return true; - } - - var range = new IndexRange(index, index); - - if (select) - { - AddRange(range, raiseOnSelectionChanged); - } - else - { - RemoveRange(range, raiseOnSelectionChanged); - } - - return true; - } - - return false; - } - - private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) - { - bool selectionInvalidated = false; - List? removed = null; - - switch (args.Action) - { - case NotifyCollectionChangedAction.Add: - { - selectionInvalidated = OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); - break; - } - - case NotifyCollectionChangedAction.Remove: - { - (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); - break; - } - - case NotifyCollectionChangedAction.Reset: - { - if (_selectedItems == null) - { - ClearSelection(); - } - else - { - removed = RecreateSelectionFromSelectedItems(); - } - - selectionInvalidated = true; - break; - } - - case NotifyCollectionChangedAction.Replace: - { - (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); - selectionInvalidated |= OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); - break; - } - } - - if (selectionInvalidated) - { - OnSelectionChanged(); - } - - _manager.OnSelectionInvalidatedDueToCollectionChange(selectionInvalidated, removed); - } - - private bool OnItemsAdded(int index, int count) - { - var selectionInvalidated = false; - - // Update ranges for leaf items - var toAdd = new List(); - - for (int i = 0; i < _selected.Count; i++) - { - var range = _selected[i]; - - // The range is after the inserted items, need to shift the range right - if (range.End >= index) - { - int begin = range.Begin; - - // If the index left of newIndex is inside the range, - // Split the range and remember the left piece to add later - if (range.Contains(index - 1)) - { - range.Split(index - 1, out var before, out _); - toAdd.Add(before); - begin = index; - } - - // Shift the range to the right - _selected[i] = new IndexRange(begin + count, range.End + count); - selectionInvalidated = true; - } - } - - // Add the left sides of the split ranges - _selected.AddRange(toAdd); - - // Update for non-leaf if we are tracking non-leaf nodes - if (_childrenNodes.Count > 0) - { - selectionInvalidated = true; - for (int i = 0; i < count; i++) - { - _childrenNodes.Insert(index, null); - } - } - - // Adjust the anchor - if (AnchorIndex >= index) - { - AnchorIndex += count; - } - - // Check if adding a node invalidated an ancestors - // selection state. For example if parent was selected before - // adding a new item makes the parent partially selected now. - if (!selectionInvalidated) - { - var parent = _parent; - - while (parent != null) - { - var isSelected = parent.IsSelectedWithPartial(); - - // If a parent is selected, then it will become partially selected. - // If it is not selected or partially selected - there is no change. - if (isSelected == true) - { - selectionInvalidated = true; - break; - } - - parent = parent._parent; - } - } - - return selectionInvalidated; - } - - private (bool, List) OnItemsRemoved(int index, IList items) - { - var selectionInvalidated = false; - var removed = new List(); - var count = items.Count; - var isSelected = false; - - for (int i = 0; i <= count - 1; i++) - { - if (IsSelected(index + i)) - { - isSelected = true; - removed.Add(items[i]); - } - } - - if (isSelected) - { - var removeRange = new IndexRange(index, index + count - 1); - SelectedCount -= IndexRange.Remove(_selected, removeRange); - selectionInvalidated = true; - - if (_selectedItems != null) - { - foreach (var i in items) - { - _selectedItems.Remove(i); - } - } - } - - for (int i = 0; i < _selected.Count; i++) - { - var range = _selected[i]; - - // The range is after the removed items, need to shift the range left - if (range.End > index) - { - // Shift the range to the left - _selected[i] = new IndexRange(range.Begin - count, range.End - count); - selectionInvalidated = true; - } - } - - // Update for non-leaf if we are tracking non-leaf nodes - if (_childrenNodes.Count > 0) - { - selectionInvalidated = true; - for (int i = 0; i < count; i++) - { - if (_childrenNodes[index] != null) - { - removed.AddRange(_childrenNodes[index]!.SelectedItems); - RealizedChildrenNodeCount--; - _childrenNodes[index]!.Dispose(); - } - _childrenNodes.RemoveAt(index); - } - } - - //Adjust the anchor - if (AnchorIndex >= index) - { - AnchorIndex -= count; - } - - return (selectionInvalidated, removed); - } - - private void OnSelectionChanged() - { - _selectedIndicesCacheIsValid = false; - _selectedIndicesCached.Clear(); - } - - public static bool? ConvertToNullableBool(SelectionState isSelected) - { - bool? result = null; // PartialySelected - - if (isSelected == SelectionState.Selected) - { - result = true; - } - else if (isSelected == SelectionState.NotSelected) - { - result = false; - } - - return result; - } - - public SelectionState EvaluateIsSelectedBasedOnChildrenNodes() - { - var selectionState = SelectionState.NotSelected; - int realizedChildrenNodeCount = RealizedChildrenNodeCount; - int selectedCount = SelectedCount; - - if (realizedChildrenNodeCount != 0 || selectedCount != 0) - { - // There are realized children or some selected leaves. - int dataCount = DataCount; - if (realizedChildrenNodeCount == 0 && selectedCount > 0) - { - // All nodes are leaves under it - we didn't create children nodes as an optimization. - // See if all/some or none of the leaves are selected. - selectionState = dataCount != selectedCount ? - SelectionState.PartiallySelected : - dataCount == selectedCount ? SelectionState.Selected : SelectionState.NotSelected; - } - else - { - // There are child nodes, walk them individually and evaluate based on each child - // being selected/not selected or partially selected. - selectedCount = 0; - int notSelectedCount = 0; - for (int i = 0; i < ChildrenNodeCount; i++) - { - var child = GetAt(i, false, default); - - if (child != null) - { - // child is realized, ask it. - var isChildSelected = IsSelectedWithPartial(i); - if (isChildSelected == null) - { - selectionState = SelectionState.PartiallySelected; - break; - } - else if (isChildSelected == true) - { - selectedCount++; - } - else - { - notSelectedCount++; - } - } - else - { - // not realized. - if (IsSelected(i)) - { - selectedCount++; - } - else - { - notSelectedCount++; - } - } - - if (selectedCount > 0 && notSelectedCount > 0) - { - selectionState = SelectionState.PartiallySelected; - break; - } - } - - if (selectionState != SelectionState.PartiallySelected) - { - if (selectedCount != 0 && selectedCount != dataCount) - { - selectionState = SelectionState.PartiallySelected; - } - else - { - selectionState = selectedCount == dataCount ? SelectionState.Selected : SelectionState.NotSelected; - } - } - } - } - - return selectionState; - } - - private void PopulateSelectedItemsFromSelectedIndices() - { - if (_selectedItems != null) - { - _selectedItems.Clear(); - - foreach (var i in SelectedIndices) - { - _selectedItems.Add(ItemsSourceView!.GetAt(i)); - } - } - } - - private List RecreateSelectionFromSelectedItems() - { - var removed = new List(); - - _selected.Clear(); - SelectedCount = 0; - - for (var i = 0; i < _selectedItems!.Count; ++i) - { - var item = _selectedItems[i]; - var index = ItemsSourceView!.IndexOf(item); - - if (index != -1) - { - IndexRange.Add(_selected, new IndexRange(index, index)); - ++SelectedCount; - } - else - { - removed.Add(item); - _selectedItems.RemoveAt(i--); - } - } - - return removed; - } - - public enum SelectionState - { - Selected, - NotSelected, - PartiallySelected - } - } -} diff --git a/src/Avalonia.Controls/SelectionNodeOperation.cs b/src/Avalonia.Controls/SelectionNodeOperation.cs deleted file mode 100644 index 9622a52f00..0000000000 --- a/src/Avalonia.Controls/SelectionNodeOperation.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -#nullable enable - -namespace Avalonia.Controls -{ - internal class SelectionNodeOperation : ISelectedItemInfo - { - private readonly SelectionNode _owner; - private List? _selected; - private List? _deselected; - private int _selectedCount = -1; - private int _deselectedCount = -1; - - public SelectionNodeOperation(SelectionNode owner) - { - _owner = owner; - } - - public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; - public List? SelectedRanges => _selected; - public List? DeselectedRanges => _deselected; - public IndexPath Path => _owner.IndexPath; - public ItemsSourceView? Items => _owner.ItemsSourceView; - - public int SelectedCount - { - get - { - if (_selectedCount == -1) - { - _selectedCount = (_selected != null) ? IndexRange.GetCount(_selected) : 0; - } - - return _selectedCount; - } - } - - public int DeselectedCount - { - get - { - if (_deselectedCount == -1) - { - _deselectedCount = (_deselected != null) ? IndexRange.GetCount(_deselected) : 0; - } - - return _deselectedCount; - } - } - - public void Selected(IndexRange range) - { - Add(range, ref _selected, _deselected); - _selectedCount = -1; - } - - public void Selected(IEnumerable ranges) - { - foreach (var range in ranges) - { - Selected(range); - } - } - - public void Deselected(IndexRange range) - { - Add(range, ref _deselected, _selected); - _deselectedCount = -1; - } - - public void Deselected(IEnumerable ranges) - { - foreach (var range in ranges) - { - Deselected(range); - } - } - - private static void Add( - IndexRange range, - ref List? add, - List? remove) - { - if (remove != null) - { - var removed = new List(); - IndexRange.Remove(remove, range, removed); - var selected = IndexRange.Subtract(range, removed); - - if (selected.Any()) - { - add ??= new List(); - - foreach (var r in selected) - { - IndexRange.Add(add, r); - } - } - } - else - { - add ??= new List(); - IndexRange.Add(add, range); - } - } - } -} diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs index d454a29021..1afd86a11e 100644 --- a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs +++ b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs @@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates /// /// Builds a control for a piece of data. /// - public class FuncDataTemplate : FuncTemplate, IDataTemplate + public class FuncDataTemplate : FuncTemplate, IRecyclingDataTemplate { /// /// The default data template used in the case where no matching data template is found. @@ -30,10 +30,8 @@ namespace Avalonia.Controls.Templates }, true); - /// - /// The implementation of the method. - /// private readonly Func _match; + private readonly bool _supportsRecycling; /// /// Initializes a new instance of the class. @@ -70,12 +68,9 @@ namespace Avalonia.Controls.Templates Contract.Requires(match != null); _match = match; - SupportsRecycling = supportsRecycling; + _supportsRecycling = supportsRecycling; } - /// - public bool SupportsRecycling { get; } - /// /// Checks to see if this data template matches the specified data. /// @@ -88,6 +83,24 @@ namespace Avalonia.Controls.Templates return _match(data); } + /// + /// Creates or recycles a control to display the specified data. + /// + /// The data to display. + /// An optional control to recycle. + /// + /// The control if supplied and applicable to + /// , otherwise a new control. + /// + /// + /// The caller should ensure that any control passed to + /// originated from the same data template. + /// + public IControl Build(object data, IControl existing) + { + return _supportsRecycling && existing is object ? existing : Build(data); + } + /// /// Determines of an object is of the specified type. /// diff --git a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs index d08616b968..cd0e3ad603 100644 --- a/src/Avalonia.Controls/Templates/FuncTemplate`2.cs +++ b/src/Avalonia.Controls/Templates/FuncTemplate`2.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace Avalonia.Controls.Templates { /// @@ -18,9 +20,7 @@ namespace Avalonia.Controls.Templates /// The function used to create the control. public FuncTemplate(Func func) { - Contract.Requires(func != null); - - _func = func; + _func = func ?? throw new ArgumentNullException(nameof(func)); } /// diff --git a/src/Avalonia.Controls/Templates/IDataTemplate.cs b/src/Avalonia.Controls/Templates/IDataTemplate.cs index cfde029eb8..0368748a0b 100644 --- a/src/Avalonia.Controls/Templates/IDataTemplate.cs +++ b/src/Avalonia.Controls/Templates/IDataTemplate.cs @@ -1,3 +1,7 @@ +using System; + +#nullable enable + namespace Avalonia.Controls.Templates { /// @@ -5,12 +9,6 @@ namespace Avalonia.Controls.Templates /// public interface IDataTemplate : ITemplate { - /// - /// Gets a value indicating whether the data template supports recycling of the generated - /// control. - /// - bool SupportsRecycling { get; } - /// /// Checks to see if this data template matches the specified data. /// @@ -20,4 +18,4 @@ namespace Avalonia.Controls.Templates /// bool Match(object data); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs b/src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs new file mode 100644 index 0000000000..25956a9c9a --- /dev/null +++ b/src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs @@ -0,0 +1,25 @@ +#nullable enable + +namespace Avalonia.Controls.Templates +{ + /// + /// An that supports recycling existing elements. + /// + public interface IRecyclingDataTemplate : IDataTemplate + { + /// + /// Creates or recycles a control to display the specified data. + /// + /// The data to display. + /// An optional control to recycle. + /// + /// The control if supplied and applicable to + /// , otherwise a new control. + /// + /// + /// The caller should ensure that any control passed to + /// originated from the same data template. + /// + IControl Build(object data, IControl? existing); + } +} diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 394699ce64..73a1ae3335 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -18,6 +18,15 @@ namespace Avalonia.Controls { public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost { + public static KeyGesture CutGesture { get; } = AvaloniaLocator.Current + .GetService()?.Cut.FirstOrDefault(); + + public static KeyGesture CopyGesture { get; } = AvaloniaLocator.Current + .GetService()?.Copy.FirstOrDefault(); + + public static KeyGesture PasteGesture { get; } = AvaloniaLocator.Current + .GetService()?.Paste.FirstOrDefault(); + public static readonly StyledProperty AcceptsReturnProperty = AvaloniaProperty.Register(nameof(AcceptsReturn)); @@ -95,6 +104,30 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(NewLine), textbox => textbox.NewLine, (textbox, newline) => textbox.NewLine = newline); + public static readonly StyledProperty InnerLeftContentProperty = + AvaloniaProperty.Register(nameof(InnerLeftContent)); + + public static readonly StyledProperty InnerRightContentProperty = + AvaloniaProperty.Register(nameof(InnerRightContent)); + + public static readonly StyledProperty RevealPasswordProperty = + AvaloniaProperty.Register(nameof(RevealPassword)); + + public static readonly DirectProperty CanCutProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanCut), + o => o.CanCut); + + public static readonly DirectProperty CanCopyProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanCopy), + o => o.CanCopy); + + public static readonly DirectProperty CanPasteProperty = + AvaloniaProperty.RegisterDirect( + nameof(CanPaste), + o => o.CanPaste); + struct UndoRedoState : IEquatable { public string Text { get; } @@ -117,6 +150,9 @@ namespace Avalonia.Controls private UndoRedoHelper _undoRedoHelper; private bool _isUndoingRedoing; private bool _ignoreTextChanges; + private bool _canCut; + private bool _canCopy; + private bool _canPaste; private string _newLine = Environment.NewLine; private static readonly string[] invalidCharacters = new String[1] { "\u007f" }; @@ -134,7 +170,7 @@ namespace Avalonia.Controls { if (acceptsReturn) { - return wrapping == TextWrapping.NoWrap ? + return wrapping != TextWrapping.Wrap ? ScrollBarVisibility.Auto : ScrollBarVisibility.Disabled; } @@ -148,6 +184,8 @@ namespace Avalonia.Controls horizontalScrollBarVisibility, BindingPriority.Style); _undoRedoHelper = new UndoRedoHelper(this); + + UpdatePseudoclasses(); } public bool AcceptsReturn @@ -285,7 +323,7 @@ namespace Avalonia.Controls else { HandleTextInput(value); - } + } _undoRedoHelper.Snapshot(); } } @@ -326,6 +364,24 @@ namespace Avalonia.Controls set { SetValue(UseFloatingWatermarkProperty, value); } } + public object InnerLeftContent + { + get { return GetValue(InnerLeftContentProperty); } + set { SetValue(InnerLeftContentProperty, value); } + } + + public object InnerRightContent + { + get { return GetValue(InnerRightContentProperty); } + set { SetValue(InnerRightContentProperty, value); } + } + + public bool RevealPassword + { + get { return GetValue(RevealPasswordProperty); } + set { SetValue(RevealPasswordProperty, value); } + } + public TextWrapping TextWrapping { get { return GetValue(TextWrappingProperty); } @@ -340,6 +396,41 @@ namespace Avalonia.Controls get { return _newLine; } set { SetAndRaise(NewLineProperty, ref _newLine, value); } } + + /// + /// Clears the current selection, maintaining the + /// + public void ClearSelection() + { + SelectionStart = SelectionEnd = CaretIndex; + } + + /// + /// Property for determining if the Cut command can be executed. + /// + public bool CanCut + { + get { return _canCut; } + private set { SetAndRaise(CanCutProperty, ref _canCut, value); } + } + + /// + /// Property for determining if the Copy command can be executed. + /// + public bool CanCopy + { + get { return _canCopy; } + private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); } + } + + /// + /// Property for determining if the Paste command can be executed. + /// + public bool CanPaste + { + get { return _canPaste; } + private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); } + } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { @@ -351,6 +442,26 @@ namespace Avalonia.Controls } } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == TextProperty) + { + UpdatePseudoclasses(); + UpdateCommandStates(); + } + } + + private void UpdateCommandStates() + { + var text = GetSelection(); + var isSelectionNullOrEmpty = string.IsNullOrEmpty(text); + CanCopy = !IsPasswordBox && !isSelectionNullOrEmpty; + CanCut = !IsPasswordBox && !isSelectionNullOrEmpty && !IsReadOnly; + CanPaste = !IsReadOnly; + } + protected override void OnGotFocus(GotFocusEventArgs e) { base.OnGotFocus(e); @@ -365,14 +476,23 @@ namespace Avalonia.Controls SelectAll(); } + UpdateCommandStates(); + _presenter?.ShowCaret(); } protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); - SelectionStart = 0; - SelectionEnd = 0; + + if (ContextMenu == null || !ContextMenu.IsOpen) + { + ClearSelection(); + RevealPassword = false; + } + + UpdateCommandStates(); + _presenter?.HideCaret(); } @@ -399,7 +519,7 @@ namespace Avalonia.Controls text = Text ?? string.Empty; SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex)); CaretIndex += input.Length; - SelectionStart = SelectionEnd = CaretIndex; + ClearSelection(); _undoRedoHelper.DiscardRedo(); } } @@ -415,19 +535,31 @@ namespace Avalonia.Controls return text; } - private async void Copy() + public async void Cut() { + var text = GetSelection(); + if (text is null) return; + + _undoRedoHelper.Snapshot(); + Copy(); + DeleteSelection(); + _undoRedoHelper.Snapshot(); + } + + public async void Copy() + { + var text = GetSelection(); + if (text is null) return; + await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) - .SetTextAsync(GetSelection()); + .SetTextAsync(text); } - private async void Paste() + public async void Paste() { var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync(); - if (text == null) - { - return; - } + + if (text is null) return; _undoRedoHelper.Snapshot(); HandleTextInput(text); @@ -466,23 +598,18 @@ namespace Avalonia.Controls { if (!IsPasswordBox) { - _undoRedoHelper.Snapshot(); - Copy(); - DeleteSelection(); - _undoRedoHelper.Snapshot(); + Cut(); } handled = true; } else if (Match(keymap.Paste)) { - Paste(); handled = true; } else if (Match(keymap.Undo)) { - try { _isUndoingRedoing = true; @@ -617,7 +744,7 @@ namespace Avalonia.Controls SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex)); CaretIndex -= removedCharacters; - SelectionStart = SelectionEnd = CaretIndex; + ClearSelection(); } _undoRedoHelper.Snapshot(); @@ -690,7 +817,7 @@ namespace Avalonia.Controls } else if (movement) { - SelectionStart = SelectionEnd = CaretIndex; + ClearSelection(); } if (handled || movement) @@ -756,7 +883,7 @@ namespace Avalonia.Controls // if it did not, we change the selection to where the user clicked var firstSelection = Math.Min(SelectionStart, SelectionEnd); var lastSelection = Math.Max(SelectionStart, SelectionEnd); - var didClickInSelection = SelectionStart != SelectionEnd && + var didClickInSelection = SelectionStart != SelectionEnd && caretIndex >= firstSelection && caretIndex <= lastSelection; if (!didClickInSelection) { @@ -803,6 +930,11 @@ namespace Avalonia.Controls } } + public void Clear() + { + Text = string.Empty; + } + private int DeleteCharacter(int index) { var start = index + 1; @@ -992,7 +1124,8 @@ namespace Avalonia.Controls var end = Math.Max(selectionStart, selectionEnd); var text = Text; SetTextInternal(text.Substring(0, start) + text.Substring(end)); - SelectionStart = SelectionEnd = CaretIndex = start; + CaretIndex = start; + ClearSelection(); return true; } else @@ -1068,6 +1201,11 @@ namespace Avalonia.Controls SelectionEnd = CaretIndex; } + private void UpdatePseudoclasses() + { + PseudoClasses.Set(":empty", string.IsNullOrWhiteSpace(Text)); + } + private bool IsPasswordBox => PasswordChar != default(char); UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState @@ -1076,7 +1214,8 @@ namespace Avalonia.Controls set { Text = value.Text; - SelectionStart = SelectionEnd = CaretIndex = value.CaretPosition; + CaretIndex = value.CaretPosition; + ClearSelection(); } } } diff --git a/src/Avalonia.Controls/TickBar.cs b/src/Avalonia.Controls/TickBar.cs index 22145d8742..6ea5277a55 100644 --- a/src/Avalonia.Controls/TickBar.cs +++ b/src/Avalonia.Controls/TickBar.cs @@ -39,11 +39,15 @@ namespace Avalonia.Controls { static TickBar() { - AffectsRender(ReservedSpaceProperty, + AffectsRender(FillProperty, + IsDirectionReversedProperty, + ReservedSpaceProperty, MaximumProperty, MinimumProperty, OrientationProperty, - TickFrequencyProperty); + PlacementProperty, + TickFrequencyProperty, + TicksProperty); } public TickBar() : base() @@ -137,7 +141,7 @@ namespace Avalonia.Controls /// /// The Ticks property contains collection of value of type Double which /// are the logical positions use to draw the ticks. - /// The property value is a . + /// The property value is a . /// public AvaloniaList Ticks { @@ -169,7 +173,6 @@ namespace Avalonia.Controls public static readonly StyledProperty PlacementProperty = AvaloniaProperty.Register(nameof(Placement), 0d); - /// /// Placement property specified how the Tick will be placed. /// This property affects the way ticks are drawn. @@ -189,7 +192,7 @@ namespace Avalonia.Controls /// /// TickBar will use ReservedSpaceProperty for left and right spacing (for horizontal orientation) or - /// tob and bottom spacing (for vertical orienation). + /// top and bottom spacing (for vertical orienation). /// The space on both sides of TickBar is half of specified ReservedSpace. /// This property has type of . /// @@ -201,7 +204,7 @@ namespace Avalonia.Controls /// /// Draw ticks. - /// Ticks can be draw in 8 diffrent ways depends on Placment property and IsDirectionReversed property. + /// Ticks can be draw in 8 different ways depends on Placement property and IsDirectionReversed property. /// /// This function also draw selection-tick(s) if IsSelectionRangeEnabled is 'true' and /// SelectionStart and SelectionEnd are valid. @@ -211,9 +214,7 @@ namespace Avalonia.Controls /// /// The secondary ticks (all other ticks, including selection-tics) height will be 75% of TickBar's render size. /// - /// Brush that use to fill ticks is specified by Shape.Fill property. - /// - /// Pen that use to draw ticks is specified by Shape.Pen property. + /// Brush that use to fill ticks is specified by Fill property. /// public override void Render(DrawingContext dc) { @@ -222,7 +223,6 @@ namespace Avalonia.Controls var tickLen = 0.0d; // Height for Primary Tick (for Mininum and Maximum value) var tickLen2 = 0.0d; // Height for Secondary Tick var logicalToPhysical = 1.0; - var progression = 1.0d; var startPoint = new Point(); var endPoint = new Point(); var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height; @@ -242,7 +242,6 @@ namespace Avalonia.Controls startPoint = new Point(halfReservedSpace, size.Height); endPoint = new Point(halfReservedSpace + size.Width, size.Height); logicalToPhysical = size.Width / range; - progression = 1; break; case TickBarPlacement.Bottom: @@ -255,7 +254,6 @@ namespace Avalonia.Controls startPoint = new Point(halfReservedSpace, 0d); endPoint = new Point(halfReservedSpace + size.Width, 0d); logicalToPhysical = size.Width / range; - progression = 1; break; case TickBarPlacement.Left: @@ -269,7 +267,6 @@ namespace Avalonia.Controls startPoint = new Point(size.Width, size.Height + halfReservedSpace); endPoint = new Point(size.Width, halfReservedSpace); logicalToPhysical = size.Height / range * -1; - progression = -1; break; case TickBarPlacement.Right: @@ -282,7 +279,6 @@ namespace Avalonia.Controls startPoint = new Point(0d, size.Height + halfReservedSpace); endPoint = new Point(0d, halfReservedSpace); logicalToPhysical = size.Height / range * -1; - progression = -1; break; }; @@ -291,7 +287,6 @@ namespace Avalonia.Controls // Invert direciton of the ticks if (IsDirectionReversed) { - progression *= -progression; logicalToPhysical *= -1; // swap startPoint & endPoint diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index b458b15c64..d56ff5752f 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -55,7 +55,7 @@ namespace Avalonia.Controls /// /// Stores the current instance in the control. /// - private static readonly AttachedProperty ToolTipProperty = + internal static readonly AttachedProperty ToolTipProperty = AvaloniaProperty.RegisterAttached("ToolTip"); private IPopupHost _popup; @@ -66,6 +66,7 @@ namespace Avalonia.Controls static ToolTip() { TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged); + IsOpenProperty.Changed.Subscribe(ToolTipService.Instance.TipOpenChanged); IsOpenProperty.Changed.Subscribe(IsOpenChanged); } diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index 569697304f..341ab2fe81 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -28,20 +28,40 @@ namespace Avalonia.Controls { control.PointerEnter -= ControlPointerEnter; control.PointerLeave -= ControlPointerLeave; - control.DetachedFromVisualTree -= ControlDetaching; } if (e.NewValue != null) { control.PointerEnter += ControlPointerEnter; control.PointerLeave += ControlPointerLeave; + } + + if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip)) + { + var tip = control.GetValue(ToolTip.ToolTipProperty); + + tip.Content = e.NewValue; + } + } + + internal void TipOpenChanged(AvaloniaPropertyChangedEventArgs e) + { + var control = (Control)e.Sender; + + if (e.OldValue is false && e.NewValue is true) + { control.DetachedFromVisualTree += ControlDetaching; } + else if(e.OldValue is true && e.NewValue is false) + { + control.DetachedFromVisualTree -= ControlDetaching; + } } private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e) { var control = (Control)sender; + control.DetachedFromVisualTree -= ControlDetaching; Close(control); } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 611f0c9290..3d24f60463 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -280,10 +280,10 @@ namespace Avalonia.Controls } /// - double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; + double ILayoutRoot.LayoutScaling => PlatformImpl?.RenderScaling ?? 1; /// - double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1; + double IRenderRoot.RenderScaling => PlatformImpl?.RenderScaling ?? 1; IStyleHost IStyleHost.StylingParent => _globalStyles; diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index a91655855c..b4c30e0149 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -2,9 +2,11 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Reactive.Linq; +using Avalonia.Collections; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; using Avalonia.Controls.Utils; @@ -44,29 +46,16 @@ namespace Avalonia.Controls o => o.SelectedItems, (o, v) => o.SelectedItems = v); - /// - /// Defines the property. - /// - public static readonly DirectProperty SelectionProperty = - SelectingItemsControl.SelectionProperty.AddOwner( - o => o.Selection, - (o, v) => o.Selection = v); - /// /// Defines the property. /// public static readonly StyledProperty SelectionModeProperty = ListBox.SelectionModeProperty.AddOwner(); - /// - /// Defines the property. - /// - public static RoutedEvent SelectionChangedEvent = - SelectingItemsControl.SelectionChangedEvent; - + private static readonly IList Empty = Array.Empty(); private object _selectedItem; - private ISelectionModel _selection; - private readonly SelectedItemsSync _selectedItems; + private IList _selectedItems; + private bool _syncingSelectedItems; /// /// Initializes static members of the class. @@ -76,13 +65,6 @@ namespace Avalonia.Controls // HACK: Needed or SelectedItem property will not be found in Release build. } - public TreeView() - { - // Setting Selection to null causes a default SelectionModel to be created. - Selection = null; - _selectedItems = new SelectedItemsSync(Selection); - } - /// /// Occurs when the control's selection changes. /// @@ -125,94 +107,56 @@ namespace Avalonia.Controls /// public object SelectedItem { - get => Selection.SelectedItem; - set => Selection.SelectedIndex = IndexFromItem(value); - } + get => _selectedItem; + set + { + var selectedItems = SelectedItems; - /// - /// Gets or sets the selected items. - /// - protected IList SelectedItems - { - get => _selectedItems.GetOrCreateItems(); - set => _selectedItems.SetItems(value); + SetAndRaise(SelectedItemProperty, ref _selectedItem, value); + + if (value != null) + { + if (selectedItems.Count != 1 || selectedItems[0] != value) + { + _syncingSelectedItems = true; + SelectSingleItem(value); + _syncingSelectedItems = false; + } + } + else if (SelectedItems.Count > 0) + { + SelectedItems.Clear(); + } + } } /// - /// Gets or sets a model holding the current selection. + /// Gets or sets the selected items. /// - public ISelectionModel Selection + public IList SelectedItems { - get => _selection; - set + get { - value ??= new SelectionModel + if (_selectedItems == null) { - SingleSelect = !SelectionMode.HasFlagCustom(SelectionMode.Multiple), - AutoSelect = SelectionMode.HasFlagCustom(SelectionMode.AlwaysSelected), - RetainSelectionOnReset = true, - }; - - if (_selection != value) - { - if (value == null) - { - throw new ArgumentNullException(nameof(value), "Cannot set Selection to null."); - } - else if (value.Source != null && value.Source != Items) - { - throw new ArgumentException("Selection has invalid Source."); - } - - List oldSelection = null; - - if (_selection != null) - { - oldSelection = Selection.SelectedItems.ToList(); - _selection.PropertyChanged -= OnSelectionModelPropertyChanged; - _selection.SelectionChanged -= OnSelectionModelSelectionChanged; - _selection.ChildrenRequested -= OnSelectionModelChildrenRequested; - MarkContainersUnselected(); - } - - _selection = value; - - if (_selection != null) - { - _selection.Source = Items; - _selection.PropertyChanged += OnSelectionModelPropertyChanged; - _selection.SelectionChanged += OnSelectionModelSelectionChanged; - _selection.ChildrenRequested += OnSelectionModelChildrenRequested; - - if (_selection.SingleSelect) - { - SelectionMode &= ~SelectionMode.Multiple; - } - else - { - SelectionMode |= SelectionMode.Multiple; - } - - if (_selection.AutoSelect) - { - SelectionMode |= SelectionMode.AlwaysSelected; - } - else - { - SelectionMode &= ~SelectionMode.AlwaysSelected; - } - - UpdateContainerSelection(); + _selectedItems = new AvaloniaList(); + SubscribeToSelectedItems(); + } - var selectedItem = SelectedItem; + return _selectedItems; + } - if (_selectedItem != selectedItem) - { - RaisePropertyChanged(SelectedItemProperty, _selectedItem, selectedItem); - _selectedItem = selectedItem; - } - } + set + { + if (value?.IsFixedSize == true || value?.IsReadOnly == true) + { + throw new NotSupportedException( + "Cannot use a fixed size or read-only collection as SelectedItems."); } + + UnsubscribeFromSelectedItems(); + _selectedItems = value ?? new AvaloniaList(); + SubscribeToSelectedItems(); } } @@ -245,13 +189,186 @@ namespace Avalonia.Controls /// Note that this method only selects nodes currently visible due to their parent nodes /// being expanded: it does not expand nodes. /// - public void SelectAll() => Selection.SelectAll(); + public void SelectAll() + { + SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items); + } /// /// Deselects all items in the . /// - public void UnselectAll() => Selection.ClearSelection(); + public void UnselectAll() + { + SelectedItems.Clear(); + } + + /// + /// Subscribes to the CollectionChanged event, if any. + /// + private void SubscribeToSelectedItems() + { + if (_selectedItems is INotifyCollectionChanged incc) + { + incc.CollectionChanged += SelectedItemsCollectionChanged; + } + + SelectedItemsCollectionChanged( + _selectedItems, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + private void SelectSingleItem(object item) + { + SelectedItems.Clear(); + SelectedItems.Add(item); + } + + /// + /// Called when the CollectionChanged event is raised. + /// + /// The event sender. + /// The event args. + private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + IList added = null; + IList removed = null; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + + SelectedItemsAdded(e.NewItems.Cast().ToArray()); + + if (AutoScrollToSelectedItem) + { + var container = (TreeViewItem)ItemContainerGenerator.Index.ContainerFromItem(e.NewItems[0]); + + container?.BringIntoView(); + } + + added = e.NewItems; + + break; + case NotifyCollectionChangedAction.Remove: + + if (!_syncingSelectedItems) + { + if (SelectedItems.Count == 0) + { + SelectedItem = null; + } + else + { + var selectedIndex = SelectedItems.IndexOf(_selectedItem); + + if (selectedIndex == -1) + { + var old = _selectedItem; + _selectedItem = SelectedItems[0]; + + RaisePropertyChanged(SelectedItemProperty, old, _selectedItem); + } + } + } + + foreach (var item in e.OldItems) + { + MarkItemSelected(item, false); + } + + removed = e.OldItems; + + break; + case NotifyCollectionChangedAction.Reset: + + foreach (IControl container in ItemContainerGenerator.Index.Containers) + { + MarkContainerSelected(container, false); + } + + if (SelectedItems.Count > 0) + { + SelectedItemsAdded(SelectedItems); + + added = SelectedItems; + } + else if (!_syncingSelectedItems) + { + SelectedItem = null; + } + + break; + case NotifyCollectionChangedAction.Replace: + + foreach (var item in e.OldItems) + { + MarkItemSelected(item, false); + } + + foreach (var item in e.NewItems) + { + MarkItemSelected(item, true); + } + + if (SelectedItem != SelectedItems[0] && !_syncingSelectedItems) + { + var oldItem = SelectedItem; + var item = SelectedItems[0]; + _selectedItem = item; + RaisePropertyChanged(SelectedItemProperty, oldItem, item); + } + + added = e.NewItems; + removed = e.OldItems; + + break; + } + + if (added?.Count > 0 || removed?.Count > 0) + { + var changed = new SelectionChangedEventArgs( + SelectingItemsControl.SelectionChangedEvent, + removed ?? Empty, + added ?? Empty); + RaiseEvent(changed); + } + } + + private void MarkItemSelected(object item, bool selected) + { + var container = ItemContainerGenerator.Index.ContainerFromItem(item); + + MarkContainerSelected(container, selected); + } + + private void SelectedItemsAdded(IList items) + { + if (items.Count == 0) + { + return; + } + + foreach (object item in items) + { + MarkItemSelected(item, true); + } + + if (SelectedItem == null && !_syncingSelectedItems) + { + SetAndRaise(SelectedItemProperty, ref _selectedItem, items[0]); + } + } + + /// + /// Unsubscribes from the CollectionChanged event, if any. + /// + private void UnsubscribeFromSelectedItems() + { + if (_selectedItems is INotifyCollectionChanged incc) + { + incc.CollectionChanged -= SelectedItemsCollectionChanged; + } + } (bool handled, IInputElement next) ICustomKeyboardNavigation.GetNext(IInputElement element, NavigationDirection direction) { @@ -334,86 +451,6 @@ namespace Avalonia.Controls } } - /// - /// Called when is raised. - /// - /// The sender. - /// The event args. - private void OnSelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(SelectionModel.AnchorIndex) && AutoScrollToSelectedItem) - { - var container = ContainerFromIndex(Selection.AnchorIndex); - - if (container != null) - { - DispatcherTimer.RunOnce(container.BringIntoView, TimeSpan.Zero); - } - } - } - - /// - /// Called when is raised. - /// - /// The sender. - /// The event args. - private void OnSelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) - { - void Mark(IndexPath index, bool selected) - { - var container = ContainerFromIndex(index); - - if (container != null) - { - MarkContainerSelected(container, selected); - } - } - - foreach (var i in e.SelectedIndices) - { - Mark(i, true); - } - - foreach (var i in e.DeselectedIndices) - { - Mark(i, false); - } - - var newSelectedItem = SelectedItem; - - if (newSelectedItem != _selectedItem) - { - RaisePropertyChanged(SelectedItemProperty, _selectedItem, newSelectedItem); - _selectedItem = newSelectedItem; - } - - var ev = new SelectionChangedEventArgs( - SelectionChangedEvent, - e.DeselectedItems.ToList(), - e.SelectedItems.ToList()); - RaiseEvent(ev); - } - - private void OnSelectionModelChildrenRequested(object sender, SelectionModelChildrenRequestedEventArgs e) - { - var container = ItemContainerGenerator.Index.ContainerFromItem(e.Source) as TreeViewItem; - - if (container is object) - { - if (e.SourceIndex.IsAncestorOf(e.FinalIndex)) - { - container.IsExpanded = true; - container.ApplyTemplate(); - container.Presenter?.ApplyTemplate(); - } - - e.Children = Observable.CombineLatest( - container.GetObservable(TreeViewItem.IsExpandedProperty), - container.GetObservable(ItemsProperty), - (expanded, items) => expanded ? items : null); - } - } - private TreeViewItem GetContainerInDirection( TreeViewItem from, NavigationDirection direction, @@ -467,12 +504,6 @@ namespace Avalonia.Controls return result; } - protected override void ItemsChanged(AvaloniaPropertyChangedEventArgs e) - { - Selection.Source = Items; - base.ItemsChanged(e); - } - /// protected override void OnPointerPressed(PointerPressedEventArgs e) { @@ -494,18 +525,6 @@ namespace Avalonia.Controls } } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (change.Property == SelectionModeProperty) - { - var mode = change.NewValue.GetValueOrDefault(); - Selection.SingleSelect = !mode.HasFlagCustom(SelectionMode.Multiple); - Selection.AutoSelect = mode.HasFlagCustom(SelectionMode.AlwaysSelected); - } - } - /// /// Updates the selection for an item based on user interaction. /// @@ -521,9 +540,9 @@ namespace Avalonia.Controls bool toggleModifier = false, bool rightButton = false) { - var index = IndexFromContainer((TreeViewItem)container); + var item = ItemContainerGenerator.Index.ItemFromContainer(container); - if (index.GetSize() == 0) + if (item == null) { return; } @@ -540,48 +559,41 @@ namespace Avalonia.Controls var multi = (mode & SelectionMode.Multiple) != 0; var range = multi && selectedContainer != null && rangeModifier; - if (!select) + if (rightButton) { - Selection.DeselectAt(index); - } - else if (rightButton) - { - if (!Selection.IsSelectedAt(index)) + if (!SelectedItems.Contains(item)) { - Selection.SelectedIndex = index; + SelectSingleItem(item); } } else if (!toggle && !range) { - Selection.SelectedIndex = index; + SelectSingleItem(item); } else if (multi && range) { - using var operation = Selection.Update(); - var anchor = Selection.AnchorIndex; - - if (anchor.GetSize() == 0) - { - anchor = new IndexPath(0); - } - - Selection.ClearSelection(); - Selection.AnchorIndex = anchor; - Selection.SelectRangeFromAnchorTo(index); + SynchronizeItems( + SelectedItems, + GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); } else { - if (Selection.IsSelectedAt(index)) - { - Selection.DeselectAt(index); - } - else if (multi) + var i = SelectedItems.IndexOf(item); + + if (i != -1) { - Selection.SelectAt(index); + SelectedItems.Remove(item); } else { - Selection.SelectedIndex = index; + if (multi) + { + SelectedItems.Add(item); + } + else + { + SelectedItem = item; + } } } } @@ -604,6 +616,117 @@ namespace Avalonia.Controls } } + /// + /// Find which node is first in hierarchy. + /// + /// Search root. + /// Nodes to find. + /// Node to find. + /// Found first node. + private static TreeViewItem FindFirstNode(TreeView treeView, TreeViewItem nodeA, TreeViewItem nodeB) + { + return FindInContainers(treeView.ItemContainerGenerator, nodeA, nodeB); + } + + private static TreeViewItem FindInContainers(ITreeItemContainerGenerator containerGenerator, + TreeViewItem nodeA, + TreeViewItem nodeB) + { + IEnumerable containers = containerGenerator.Containers; + + foreach (ItemContainerInfo container in containers) + { + TreeViewItem node = FindFirstNode(container.ContainerControl as TreeViewItem, nodeA, nodeB); + + if (node != null) + { + return node; + } + } + + return null; + } + + private static TreeViewItem FindFirstNode(TreeViewItem node, TreeViewItem nodeA, TreeViewItem nodeB) + { + if (node == null) + { + return null; + } + + TreeViewItem match = node == nodeA ? nodeA : node == nodeB ? nodeB : null; + + if (match != null) + { + return match; + } + + return FindInContainers(node.ItemContainerGenerator, nodeA, nodeB); + } + + /// + /// Returns all items that belong to containers between and . + /// The range is inclusive. + /// + /// From container. + /// To container. + private List GetItemsInRange(TreeViewItem from, TreeViewItem to) + { + var items = new List(); + + if (from == null || to == null) + { + return items; + } + + TreeViewItem firstItem = FindFirstNode(this, from, to); + + if (firstItem == null) + { + return items; + } + + bool wasReversed = false; + + if (firstItem == to) + { + var temp = from; + + from = to; + to = temp; + + wasReversed = true; + } + + TreeViewItem node = from; + + while (node != to) + { + var item = ItemContainerGenerator.Index.ItemFromContainer(node); + + if (item != null) + { + items.Add(item); + } + + node = GetContainerInDirection(node, NavigationDirection.Down, true); + } + + var toItem = ItemContainerGenerator.Index.ItemFromContainer(to); + + if (toItem != null) + { + items.Add(toItem); + } + + if (wasReversed) + { + items.Reverse(); + } + + return items; + } + /// /// Updates the selection based on an event that may have originated in a container that /// belongs to the control. @@ -709,90 +832,26 @@ namespace Avalonia.Controls } } - private void MarkContainersUnselected() - { - foreach (var container in ItemContainerGenerator.Index.Containers) - { - MarkContainerSelected(container, false); - } - } - - private void UpdateContainerSelection() - { - var index = ItemContainerGenerator.Index; - - foreach (var container in index.Containers) - { - var i = IndexFromContainer((TreeViewItem)container); - - MarkContainerSelected( - container, - Selection.IsSelectedAt(i) != false); - } - } - - private static IndexPath IndexFromContainer(TreeViewItem container) - { - var result = new List(); - - while (true) - { - if (container.Level == 0) - { - var treeView = container.FindAncestorOfType(); - - if (treeView == null) - { - return default; - } - - result.Add(treeView.ItemContainerGenerator.IndexFromContainer(container)); - result.Reverse(); - return new IndexPath(result); - } - else - { - var parent = container.FindAncestorOfType(); - - if (parent == null) - { - return default; - } - - result.Add(parent.ItemContainerGenerator.IndexFromContainer(container)); - container = parent; - } - } - } - - private IndexPath IndexFromItem(object item) + /// + /// Makes a list of objects equal another (though doesn't preserve order). + /// + /// The items collection. + /// The desired items. + private static void SynchronizeItems(IList items, IEnumerable desired) { - var container = ItemContainerGenerator.Index.ContainerFromItem(item) as TreeViewItem; + var list = items.Cast().ToList(); + var toRemove = list.Except(desired).ToList(); + var toAdd = desired.Except(list).ToList(); - if (container != null) + foreach (var i in toRemove) { - return IndexFromContainer(container); + items.Remove(i); } - return default; - } - - private TreeViewItem ContainerFromIndex(IndexPath index) - { - TreeViewItem treeViewItem = null; - - for (var i = 0; i < index.GetSize(); ++i) + foreach (var i in toAdd) { - var generator = treeViewItem?.ItemContainerGenerator ?? ItemContainerGenerator; - treeViewItem = generator.ContainerFromIndex(index.GetAt(i)) as TreeViewItem; - - if (treeViewItem == null) - { - return null; - } + items.Add(i); } - - return treeViewItem; } } } diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index 438cbc8b27..3128753781 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -141,7 +141,7 @@ namespace Avalonia.Controls.Utils var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y; if (radiusX != 0 || radiusY != 0) { - context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); + context.ArcTo(keypoints.RightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); } // Right diff --git a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs new file mode 100644 index 0000000000..c26c5e0513 --- /dev/null +++ b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Runtime.CompilerServices; +using Avalonia.Threading; +using Avalonia.Utilities; + +#nullable enable + +namespace Avalonia.Controls.Utils +{ + internal interface ICollectionChangedListener + { + void PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); + void Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); + void PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e); + } + + internal class CollectionChangedEventManager + { + public static CollectionChangedEventManager Instance { get; } = new CollectionChangedEventManager(); + + private ConditionalWeakTable _entries = + new ConditionalWeakTable(); + + private CollectionChangedEventManager() + { + } + + public void AddListener(INotifyCollectionChanged collection, ICollectionChangedListener listener) + { + collection = collection ?? throw new ArgumentNullException(nameof(collection)); + listener = listener ?? throw new ArgumentNullException(nameof(listener)); + Dispatcher.UIThread.VerifyAccess(); + + if (!_entries.TryGetValue(collection, out var entry)) + { + entry = new Entry(collection); + _entries.Add(collection, entry); + } + + foreach (var l in entry.Listeners) + { + if (l.TryGetTarget(out var target) && target == listener) + { + throw new InvalidOperationException( + "Collection listener already added for this collection/listener combination."); + } + } + + entry.Listeners.Add(new WeakReference(listener)); + } + + public void RemoveListener(INotifyCollectionChanged collection, ICollectionChangedListener listener) + { + collection = collection ?? throw new ArgumentNullException(nameof(collection)); + listener = listener ?? throw new ArgumentNullException(nameof(listener)); + Dispatcher.UIThread.VerifyAccess(); + + if (_entries.TryGetValue(collection, out var entry)) + { + var listeners = entry.Listeners; + + for (var i = 0; i < listeners.Count; ++i) + { + if (listeners[i].TryGetTarget(out var target) && target == listener) + { + listeners.RemoveAt(i); + + if (listeners.Count == 0) + { + entry.Dispose(); + _entries.Remove(collection); + } + + return; + } + } + } + + throw new InvalidOperationException( + "Collection listener not registered for this collection/listener combination."); + } + + private class Entry : IWeakSubscriber, IDisposable + { + private INotifyCollectionChanged _collection; + + public Entry(INotifyCollectionChanged collection) + { + _collection = collection; + Listeners = new List>(); + WeakSubscriptionManager.Subscribe( + _collection, + nameof(INotifyCollectionChanged.CollectionChanged), + this); + } + + public List> Listeners { get; } + + public void Dispose() + { + WeakSubscriptionManager.Unsubscribe( + _collection, + nameof(INotifyCollectionChanged.CollectionChanged), + this); + } + + void IWeakSubscriber.OnEvent(object sender, NotifyCollectionChangedEventArgs e) + { + static void Notify( + INotifyCollectionChanged incc, + NotifyCollectionChangedEventArgs args, + List> listeners) + { + foreach (var l in listeners) + { + if (l.TryGetTarget(out var target)) + { + target.PreChanged(incc, args); + } + } + + foreach (var l in listeners) + { + if (l.TryGetTarget(out var target)) + { + target.Changed(incc, args); + } + } + + foreach (var l in listeners) + { + if (l.TryGetTarget(out var target)) + { + target.PostChanged(incc, args); + } + } + } + + var l = Listeners.ToList(); + + if (Dispatcher.UIThread.CheckAccess()) + { + Notify(_collection, e, l); + } + else + { + var eCapture = e; + Dispatcher.UIThread.Post(() => Notify(_collection, eCapture, l)); + } + } + } + } +} diff --git a/src/Avalonia.Controls/Utils/SelectedItemsSync.cs b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs index c127771990..83b62c7b6e 100644 --- a/src/Avalonia.Controls/Utils/SelectedItemsSync.cs +++ b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs @@ -1,8 +1,10 @@ using System; using System.Collections; using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using Avalonia.Collections; +using Avalonia.Controls.Selection; #nullable enable @@ -11,110 +13,118 @@ namespace Avalonia.Controls.Utils /// /// Synchronizes an with a list of SelectedItems. /// - internal class SelectedItemsSync + internal class SelectedItemsSync : IDisposable { - private IList? _items; + private ISelectionModel _selectionModel; + private IList _selectedItems; private bool _updatingItems; private bool _updatingModel; public SelectedItemsSync(ISelectionModel model) { - model = model ?? throw new ArgumentNullException(nameof(model)); - Model = model; + _selectionModel = model ?? throw new ArgumentNullException(nameof(model)); + _selectedItems = new AvaloniaList(); + SyncSelectedItemsWithSelectionModel(); + SubscribeToSelectedItems(_selectedItems); + SubscribeToSelectionModel(model); } - public ISelectionModel Model { get; private set; } - - public IList GetOrCreateItems() + public ISelectionModel SelectionModel { - if (_items == null) + get => _selectionModel; + set { - var items = new AvaloniaList(Model.SelectedItems); - items.CollectionChanged += ItemsCollectionChanged; - Model.SelectionChanged += SelectionModelSelectionChanged; - _items = items; + if (_selectionModel != value) + { + value = value ?? throw new ArgumentNullException(nameof(value)); + UnsubscribeFromSelectionModel(_selectionModel); + _selectionModel = value; + SubscribeToSelectionModel(_selectionModel); + SyncSelectedItemsWithSelectionModel(); + } } - - return _items; } - - public void SetItems(IList? items) + + public IList SelectedItems { - items ??= new AvaloniaList(); - - if (items.IsFixedSize) + get => _selectedItems; + set { - throw new NotSupportedException( - "Cannot assign fixed size selection to SelectedItems."); - } + value ??= new AvaloniaList(); - if (_items is INotifyCollectionChanged incc) - { - incc.CollectionChanged -= ItemsCollectionChanged; - } + if (_selectedItems != value) + { + if (value.IsFixedSize) + { + throw new NotSupportedException( + "Cannot assign fixed size selection to SelectedItems."); + } - if (_items == null) - { - Model.SelectionChanged += SelectionModelSelectionChanged; + UnsubscribeFromSelectedItems(_selectedItems); + _selectedItems = value; + SubscribeToSelectedItems(_selectedItems); + SyncSelectionModelWithSelectedItems(); + } } + } + + public void Dispose() + { + UnsubscribeFromSelectedItems(_selectedItems); + UnsubscribeFromSelectionModel(_selectionModel); + } + + private void SyncSelectedItemsWithSelectionModel() + { + _updatingItems = true; try { - _updatingModel = true; - _items = items; + _selectedItems.Clear(); - using (Model.Update()) + if (_selectionModel.Source is object) { - Model.ClearSelection(); - Add(items); - } - - if (_items is INotifyCollectionChanged incc2) - { - incc2.CollectionChanged += ItemsCollectionChanged; + foreach (var i in _selectionModel.SelectedItems) + { + _selectedItems.Add(i); + } } } finally { - _updatingModel = false; + _updatingItems = false; } } - public void SetModel(ISelectionModel model) + private void SyncSelectionModelWithSelectedItems() { - model = model ?? throw new ArgumentNullException(nameof(model)); + _updatingModel = true; - if (_items != null) + try { - Model.SelectionChanged -= SelectionModelSelectionChanged; - Model = model; - Model.SelectionChanged += SelectionModelSelectionChanged; - - try + if (_selectionModel.Source is object) { - _updatingItems = true; - _items.Clear(); - - foreach (var i in model.SelectedItems) + using (_selectionModel.BatchUpdate()) { - _items.Add(i); + SelectionModel.Clear(); + Add(_selectedItems); } } - finally - { - _updatingItems = false; - } + } + finally + { + _updatingModel = false; } } - private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (_updatingItems) { return; } - if (_items == null) + if (_selectedItems == null) { throw new AvaloniaInternalException("CollectionChanged raised but we don't have items."); } @@ -123,18 +133,18 @@ namespace Avalonia.Controls.Utils { foreach (var i in e.OldItems) { - var index = IndexOf(Model.Source, i); + var index = IndexOf(SelectionModel.Source, i); if (index != -1) { - Model.Deselect(index); + SelectionModel.Deselect(index); } } } try { - using var operation = Model.Update(); + using var operation = SelectionModel.BatchUpdate(); _updatingModel = true; @@ -151,8 +161,8 @@ namespace Avalonia.Controls.Utils Add(e.NewItems); break; case NotifyCollectionChangedAction.Reset: - Model.ClearSelection(); - Add(_items); + SelectionModel.Clear(); + Add(_selectedItems); break; } } @@ -166,25 +176,35 @@ namespace Avalonia.Controls.Utils { foreach (var i in newItems) { - var index = IndexOf(Model.Source, i); + var index = IndexOf(SelectionModel.Source, i); if (index != -1) { - Model.Select(index); + SelectionModel.Select(index); } } } - private void SelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + private void SelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e) { - if (_updatingModel) + if (e.PropertyName == nameof(ISelectionModel.Source)) { - return; + if (_selectedItems.Count > 0) + { + SyncSelectionModelWithSelectedItems(); + } + else + { + SyncSelectedItemsWithSelectionModel(); + } } + } - if (_items == null) + private void SelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + { + if (_updatingModel || _selectionModel.Source is null) { - throw new AvaloniaInternalException("SelectionModelChanged raised but we don't have items."); + return; } try @@ -196,12 +216,12 @@ namespace Avalonia.Controls.Utils foreach (var i in deselected) { - _items.Remove(i); + _selectedItems.Remove(i); } foreach (var i in selected) { - _items.Add(i); + _selectedItems.Add(i); } } finally @@ -210,7 +230,43 @@ namespace Avalonia.Controls.Utils } } - private static int IndexOf(object source, object item) + private void SelectionModelSourceReset(object sender, EventArgs e) + { + SyncSelectionModelWithSelectedItems(); + } + + + private void SubscribeToSelectedItems(IList selectedItems) + { + if (selectedItems is INotifyCollectionChanged incc) + { + incc.CollectionChanged += SelectedItemsCollectionChanged; + } + } + + private void SubscribeToSelectionModel(ISelectionModel model) + { + model.PropertyChanged += SelectionModelPropertyChanged; + model.SelectionChanged += SelectionModelSelectionChanged; + model.SourceReset += SelectionModelSourceReset; + } + + private void UnsubscribeFromSelectedItems(IList selectedItems) + { + if (selectedItems is INotifyCollectionChanged incc) + { + incc.CollectionChanged -= SelectedItemsCollectionChanged; + } + } + + private void UnsubscribeFromSelectionModel(ISelectionModel model) + { + model.PropertyChanged -= SelectionModelPropertyChanged; + model.SelectionChanged -= SelectionModelSelectionChanged; + model.SourceReset -= SelectionModelSourceReset; + } + + private static int IndexOf(object? source, object? item) { if (source is IList l) { diff --git a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs deleted file mode 100644 index 5adf5bdeea..0000000000 --- a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs +++ /dev/null @@ -1,189 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. - -using System; -using System.Collections.Generic; -using System.Linq; - -#nullable enable - -namespace Avalonia.Controls.Utils -{ - internal static class SelectionTreeHelper - { - public static void TraverseIndexPath( - SelectionNode root, - IndexPath path, - bool realizeChildren, - Action nodeAction) - { - var node = root; - - for (int depth = 0; depth < path.GetSize(); depth++) - { - int childIndex = path.GetAt(depth); - nodeAction(node, path, depth, childIndex); - - if (depth < path.GetSize() - 1) - { - node = node.GetAt(childIndex, realizeChildren, path)!; - } - } - } - - public static void Traverse( - SelectionNode root, - bool realizeChildren, - Action nodeAction) - { - var pendingNodes = new List(); - var current = new IndexPath(null); - - pendingNodes.Add(new TreeWalkNodeInfo(root, current)); - - while (pendingNodes.Count > 0) - { - var nextNode = pendingNodes.Last(); - pendingNodes.RemoveAt(pendingNodes.Count - 1); - int count = realizeChildren ? nextNode.Node.DataCount : nextNode.Node.ChildrenNodeCount; - for (int i = count - 1; i >= 0; i--) - { - var child = nextNode.Node.GetAt(i, realizeChildren, nextNode.Path); - var childPath = nextNode.Path.CloneWithChildIndex(i); - if (child != null) - { - pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, nextNode.Node)); - } - } - - // Queue the children first and then perform the action. This way - // the action can remove the children in the action if necessary - nodeAction(nextNode); - } - } - - public static void TraverseRangeRealizeChildren( - SelectionNode root, - IndexPath start, - IndexPath end, - Action nodeAction) - { - var pendingNodes = new List(); - var current = start; - - // Build up the stack to account for the depth first walk up to the - // start index path. - TraverseIndexPath( - root, - start, - true, - (node, path, depth, childIndex) => - { - var currentPath = StartPath(path, depth); - bool isStartPath = IsSubSet(start, currentPath); - bool isEndPath = IsSubSet(end, currentPath); - - int startIndex = depth < start.GetSize() && isStartPath ? start.GetAt(depth) : 0; - int endIndex = depth < end.GetSize() && isEndPath ? end.GetAt(depth) : node.DataCount - 1; - - for (int i = endIndex; i >= startIndex; i--) - { - var child = node.GetAt(i, true, end); - if (child != null) - { - var childPath = currentPath.CloneWithChildIndex(i); - pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, node)); - } - } - }); - - // From the start index path, do a depth first walk as long as the - // current path is less than the end path. - while (pendingNodes.Count > 0) - { - var info = pendingNodes.Last(); - pendingNodes.RemoveAt(pendingNodes.Count - 1); - int depth = info.Path.GetSize(); - bool isStartPath = IsSubSet(start, info.Path); - bool isEndPath = IsSubSet(end, info.Path); - int startIndex = depth < start.GetSize() && isStartPath ? start.GetAt(depth) : 0; - int endIndex = depth < end.GetSize() && isEndPath ? end.GetAt(depth) : info.Node.DataCount - 1; - for (int i = endIndex; i >= startIndex; i--) - { - var child = info.Node.GetAt(i, true, end); - if (child != null) - { - var childPath = info.Path.CloneWithChildIndex(i); - pendingNodes.Add(new TreeWalkNodeInfo(child, childPath, info.Node)); - } - } - - nodeAction(info); - - if (info.Path.CompareTo(end) == 0) - { - // We reached the end index path. stop iterating. - break; - } - } - } - - private static bool IsSubSet(IndexPath path, IndexPath subset) - { - var subsetSize = subset.GetSize(); - if (path.GetSize() < subsetSize) - { - return false; - } - - for (int i = 0; i < subsetSize; i++) - { - if (path.GetAt(i) != subset.GetAt(i)) - { - return false; - } - } - - return true; - } - - private static IndexPath StartPath(IndexPath path, int length) - { - var subPath = new List(); - for (int i = 0; i < length; i++) - { - subPath.Add(path.GetAt(i)); - } - - return new IndexPath(subPath); - } - - public struct TreeWalkNodeInfo - { - public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath, SelectionNode? parent) - { - node = node ?? throw new ArgumentNullException(nameof(node)); - - Node = node; - Path = indexPath; - ParentNode = parent; - } - - public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath) - { - node = node ?? throw new ArgumentNullException(nameof(node)); - - Node = node; - Path = indexPath; - ParentNode = null; - } - - public SelectionNode Node { get; } - public IndexPath Path { get; } - public SelectionNode? ParentNode { get; } - }; - - } -} diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 18d8c89f49..317b6d3f2e 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -6,6 +6,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls.Chrome; using Avalonia.Controls.Platform; +using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; @@ -70,8 +71,7 @@ namespace Avalonia.Controls /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot { - private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>(); - private TitleBar _managedTitleBar; + private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>(); private bool _isExtendedIntoWindowDecorations; private Thickness _windowDecorationMargin; private Thickness _offScreenMargin; @@ -552,20 +552,6 @@ namespace Avalonia.Controls IsExtendedIntoWindowDecorations = isExtended; WindowDecorationMargin = PlatformImpl.ExtendedMargins; OffScreenMargin = PlatformImpl.OffScreenMargin; - - if (PlatformImpl.NeedsManagedDecorations) - { - if (_managedTitleBar == null) - { - _managedTitleBar = new TitleBar(this); - _managedTitleBar.Attach(); - } - } - else - { - _managedTitleBar?.Detach(); - _managedTitleBar = null; - } } /// @@ -666,8 +652,8 @@ namespace Avalonia.Controls PlatformImpl?.Show(); Renderer?.Start(); + SetWindowStartupLocation(Owner?.PlatformImpl); } - SetWindowStartupLocation(Owner?.PlatformImpl); OnOpened(EventArgs.Empty); } @@ -818,7 +804,7 @@ namespace Avalonia.Controls private void SetWindowStartupLocation(IWindowBaseImpl owner = null) { - var scaling = owner?.Scaling ?? PlatformImpl?.Scaling ?? 1; + var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1; // TODO: We really need non-client size here. var rect = new PixelRect( diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index eb6e7319f5..1efd6c8c1d 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -39,6 +39,7 @@ namespace Avalonia.Controls public static readonly StyledProperty TopmostProperty = AvaloniaProperty.Register(nameof(Topmost)); + private int _autoSizing; private bool _hasExecutedInitialLayoutPass; private bool _isActive; private bool _ignoreVisibilityChange; @@ -97,11 +98,7 @@ namespace Avalonia.Controls /// /// Whether an auto-size operation is in progress. /// - protected bool AutoSizing - { - get; - private set; - } + protected bool AutoSizing => _autoSizing > 0; /// /// Gets or sets the owner of the window. @@ -186,8 +183,8 @@ namespace Avalonia.Controls /// protected IDisposable BeginAutoSizing() { - AutoSizing = true; - return Disposable.Create(() => AutoSizing = false); + ++_autoSizing; + return Disposable.Create(() => --_autoSizing); } /// diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 76ea8a3445..35bd0f97e1 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -9,6 +9,7 @@ 0.7.0 CS1591 true + Avalonia @@ -26,4 +27,5 @@ + diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 59862da230..b009778f97 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -69,6 +69,8 @@ namespace Avalonia.DesignerSupport window = new Window() {Content = (Control)control}; } + Design.ApplyDesignModeProperties(window, control); + if (!window.IsSet(Window.SizeToContentProperty)) { if (double.IsNaN(window.Width)) @@ -83,7 +85,6 @@ namespace Avalonia.DesignerSupport } } window.Show(); - Design.ApplyDesignModeProperties(window, control); return window; } } diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js index 3057f269a5..3750351165 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js @@ -93,7 +93,7 @@ const config = { plugins: [ new Printer(), - new CleanWebpackPlugin([path.resolve(__dirname, 'build')]), + new CleanWebpackPlugin([path.resolve(__dirname, 'build')], { verbose: false }), new MiniCssExtractPlugin({ filename: "[name].[chunkhash]h" + ".css", diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index dce24df9d9..25c26be91e 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -36,6 +36,7 @@ namespace Avalonia.DesignerSupport.Remote { } + public double DesktopScaling => 1.0; public PixelPoint Position { get; set; } public Action PositionChanged { get; set; } public Action Deactivated { get; set; } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 168cdbc03f..f377b1bcd1 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -21,7 +21,8 @@ namespace Avalonia.DesignerSupport.Remote public IPlatformHandle Handle { get; } public Size MaxAutoSizeHint { get; } public Size ClientSize { get; } - public double Scaling { get; } = 1.0; + public double RenderScaling { get; } = 1.0; + public double DesktopScaling => 1.0; public IEnumerable Surfaces { get; } public Action Input { get; set; } public Action Paint { get; set; } diff --git a/src/Avalonia.Desktop/Avalonia.Desktop.csproj b/src/Avalonia.Desktop/Avalonia.Desktop.csproj index 72b7b5c1e7..a9589b7f2b 100644 --- a/src/Avalonia.Desktop/Avalonia.Desktop.csproj +++ b/src/Avalonia.Desktop/Avalonia.Desktop.csproj @@ -11,4 +11,6 @@ + + diff --git a/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj b/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj index c0e46416c1..805aae2d1a 100644 --- a/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj +++ b/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj @@ -17,4 +17,5 @@ + diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index 5f49a46839..db8684747d 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -2,6 +2,7 @@ netstandard2.0 Avalonia + Avalonia.Diagnostics @@ -28,4 +29,5 @@ + diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs index c06fbec801..be3564e781 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs @@ -7,8 +7,6 @@ namespace Avalonia.Diagnostics { internal class ViewLocator : IDataTemplate { - public bool SupportsRecycling => false; - public IControl Build(object data) { var name = data.GetType().FullName.Replace("ViewModel", "View"); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 4e7129da41..915ba9eeee 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -81,7 +81,7 @@ namespace Avalonia.Diagnostics.ViewModels if (o is AvaloniaObject ao) { return AvaloniaPropertyRegistry.Instance.GetRegistered(ao) - .Concat(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(ao.GetType())) + .Union(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(ao.GetType())) .Select(x => new AvaloniaPropertyViewModel(ao, x)); } else diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index acc3ef16c2..1c49b24f52 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -18,12 +18,13 @@ namespace Avalonia.Diagnostics.ViewModels private int _selectedTab; private string _focusedControl; private string _pointerOverElement; + private bool _shouldVisualizeMarginPadding = true; public MainViewModel(IControl root) { _root = root; - _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root)); - _visualTree = new TreePageViewModel(VisualTreeNode.Create(root)); + _logicalTree = new TreePageViewModel(this, LogicalTreeNode.Create(root)); + _visualTree = new TreePageViewModel(this, VisualTreeNode.Create(root)); _events = new EventsPageViewModel(root); UpdateFocusedControl(); @@ -34,6 +35,17 @@ namespace Avalonia.Diagnostics.ViewModels Console = new ConsoleViewModel(UpdateConsoleContext); } + public bool ShouldVisualizeMarginPadding + { + get => _shouldVisualizeMarginPadding; + set => RaiseAndSetIfChanged(ref _shouldVisualizeMarginPadding, value); + } + + public void ToggleVisualizeMarginPadding() + { + ShouldVisualizeMarginPadding = !ShouldVisualizeMarginPadding; + } + public ConsoleViewModel Console { get; } public ViewModelBase Content diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs index cb5f5b1fda..d9a0d17518 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs @@ -83,27 +83,6 @@ namespace Avalonia.Diagnostics.ViewModels private set; } - public IndexPath Index - { - get - { - var indices = new List(); - var child = this; - var parent = Parent; - - while (parent is object) - { - indices.Add(IndexOf(parent.Children, child)); - child = child.Parent; - parent = parent.Parent; - } - - indices.Add(0); - indices.Reverse(); - return new IndexPath(indices); - } - } - public void Dispose() { _classesSubscription.Dispose(); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs index ec48cff399..d02c8994eb 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Controls; +using Avalonia.Controls.Selection; using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels @@ -10,25 +11,16 @@ namespace Avalonia.Diagnostics.ViewModels private ControlDetailsViewModel _details; private string _propertyFilter; - public TreePageViewModel(TreeNode[] nodes) + public TreePageViewModel(MainViewModel mainView, TreeNode[] nodes) { + MainView = mainView; Nodes = nodes; - Selection = new SelectionModel - { - SingleSelect = true, - Source = Nodes - }; + } - Selection.SelectionChanged += (s, e) => - { - SelectedNode = (TreeNode)Selection.SelectedItem; - }; - } + public MainViewModel MainView { get; } public TreeNode[] Nodes { get; protected set; } - public SelectionModel Selection { get; } - public TreeNode SelectedNode { get => _selectedNode; @@ -103,8 +95,8 @@ namespace Avalonia.Diagnostics.ViewModels if (node != null) { + SelectedNode = node; ExpandNode(node.Parent); - Selection.SelectedIndex = node.Index; } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml index 663722acba..0165398718 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainView.xaml @@ -16,6 +16,15 @@ + + + + + + + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml index 4ddb320175..a1e6ca7d37 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml @@ -6,7 +6,7 @@ + SelectedItem="{Binding SelectedNode, Mode=TwoWay}"> diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 633d18ddd8..1b61986ce6 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -1,7 +1,7 @@ +using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Shapes; using Avalonia.Diagnostics.ViewModels; using Avalonia.Input; using Avalonia.Markup.Xaml; @@ -11,45 +11,78 @@ namespace Avalonia.Diagnostics.Views { internal class TreePageView : UserControl { - private Control _adorner; + private readonly Panel _adorner; + private AdornerLayer _currentLayer; private TreeView _tree; public TreePageView() { - this.InitializeComponent(); + InitializeComponent(); _tree.ItemContainerGenerator.Index.Materialized += TreeViewItemMaterialized; + + _adorner = new Panel + { + ClipToBounds = false, + Children = + { + //Padding frame + new Border { BorderBrush = new SolidColorBrush(Colors.Green, 0.5) }, + //Content frame + new Border { Background = new SolidColorBrush(Color.FromRgb(160, 197, 232), 0.5) }, + //Margin frame + new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) } + }, + }; } protected void AddAdorner(object sender, PointerEventArgs e) { var node = (TreeNode)((Control)sender).DataContext; - var layer = AdornerLayer.GetAdornerLayer(node.Visual); + var visual = (Visual)node.Visual; + + _currentLayer = AdornerLayer.GetAdornerLayer(visual); - if (layer != null) + if (_currentLayer == null || + _currentLayer.Children.Contains(_adorner)) { - if (_adorner != null) - { - ((Panel)_adorner.Parent).Children.Remove(_adorner); - _adorner = null; - } + return; + } - _adorner = new Rectangle - { - Fill = new SolidColorBrush(0x80a0c5e8), - [AdornerLayer.AdornedElementProperty] = node.Visual, - }; + _currentLayer.Children.Add(_adorner); + AdornerLayer.SetAdornedElement(_adorner, visual); + + var vm = (TreePageViewModel) DataContext; - layer.Children.Add(_adorner); + if (vm.MainView.ShouldVisualizeMarginPadding) + { + var paddingBorder = (Border)_adorner.Children[0]; + paddingBorder.BorderThickness = visual.GetValue(PaddingProperty); + + var contentBorder = (Border)_adorner.Children[1]; + contentBorder.Margin = visual.GetValue(PaddingProperty); + + var marginBorder = (Border)_adorner.Children[2]; + marginBorder.BorderThickness = visual.GetValue(MarginProperty); + marginBorder.Margin = InvertThickness(visual.GetValue(MarginProperty)); } } + private static Thickness InvertThickness(Thickness input) + { + return new Thickness(-input.Left, -input.Top, -input.Right, -input.Bottom); + } + protected void RemoveAdorner(object sender, PointerEventArgs e) { - if (_adorner != null) + foreach (var border in _adorner.Children.OfType()) { - ((Panel)_adorner.Parent).Children.Remove(_adorner); - _adorner = null; + border.Margin = default; + border.Padding = default; + border.BorderThickness = default; } + + _currentLayer?.Children.Remove(_adorner); + _currentLayer = null; } private void InitializeComponent() diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj index f54ddf32be..dd476e2559 100644 --- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj +++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj @@ -15,4 +15,6 @@ + + diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.xaml b/src/Avalonia.Dialogs/ManagedFileChooser.xaml index 8a0451f982..227cc1afc0 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooser.xaml +++ b/src/Avalonia.Dialogs/ManagedFileChooser.xaml @@ -66,7 +66,7 @@ - + DockPanel.Dock="Left" Focusable="False"> diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index d9fd3b78a2..260d7350d2 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0 @@ -10,6 +10,5 @@ - - + diff --git a/src/Avalonia.Headless/Avalonia.Headless.csproj b/src/Avalonia.Headless/Avalonia.Headless.csproj index b871316a2c..ac591b68a8 100644 --- a/src/Avalonia.Headless/Avalonia.Headless.csproj +++ b/src/Avalonia.Headless/Avalonia.Headless.csproj @@ -1,8 +1,9 @@  netstandard2.0 + - + diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index f01d335fc7..3ae6c8c30e 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -21,7 +21,11 @@ namespace Avalonia.Headless public IEnumerable InstalledFontNames { get; } = new[] { "Tahoma" }; - public bool SupportsIndividualRoundRects => throw new NotImplementedException(); + public bool SupportsIndividualRoundRects => false; + + public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul; + + public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888; public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList spans) { @@ -51,7 +55,7 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(size, dpi); } - public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null) + public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat) { return new HeadlessBitmapStub(size, dpi); } @@ -66,7 +70,7 @@ namespace Avalonia.Headless return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); } - public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride) + public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride) { return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96)); } diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs index 5bd46b6714..8f4fa5e304 100644 --- a/src/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs @@ -41,7 +41,8 @@ namespace Avalonia.Headless } public Size ClientSize { get; set; } - public double Scaling { get; } = 1; + public double RenderScaling { get; } = 1; + public double DesktopScaling => RenderScaling; public IEnumerable Surfaces { get; } public Action Input { get; set; } public Action Paint { get; set; } @@ -62,9 +63,9 @@ namespace Avalonia.Headless public IInputRoot InputRoot { get; set; } - public Point PointToClient(PixelPoint point) => point.ToPoint(Scaling); + public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling); - public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, Scaling); + public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling); public void SetCursor(IPlatformHandle cursor) { @@ -201,7 +202,7 @@ namespace Avalonia.Headless public ILockedFramebuffer Lock() { - var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, Scaling), new Vector(96, 96) * Scaling); + var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling); var fb = bmp.Lock(); return new FramebufferProxy(fb, () => { diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj index ea560ce2ea..2204778afe 100644 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ b/src/Avalonia.Input/Avalonia.Input.csproj @@ -10,4 +10,5 @@ + diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index 0efc20b196..cdfcd94692 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -96,8 +96,11 @@ namespace Avalonia.Input if (s_lastPress.TryGetTarget(out var target) && target == e.Source) { - var et = e.InitialPressMouseButton != MouseButton.Right ? TappedEvent : RightTappedEvent; - e.Source.RaiseEvent(new RoutedEventArgs(et)); + if (e.InitialPressMouseButton == MouseButton.Left || e.InitialPressMouseButton == MouseButton.Right) + { + var et = e.InitialPressMouseButton != MouseButton.Right ? TappedEvent : RightTappedEvent; + e.Source.RaiseEvent(new RoutedEventArgs(et)); + } } } } diff --git a/src/Avalonia.Input/InputExtensions.cs b/src/Avalonia.Input/InputExtensions.cs index 4babe711f2..cbe36583e6 100644 --- a/src/Avalonia.Input/InputExtensions.cs +++ b/src/Avalonia.Input/InputExtensions.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Input { /// @@ -22,7 +24,7 @@ namespace Avalonia.Input /// public static IEnumerable GetInputElementsAt(this IInputElement element, Point p) { - Contract.Requires(element != null); + element = element ?? throw new ArgumentNullException(nameof(element)); return element.GetVisualsAt(p, s_hitTestDelegate).Cast(); } @@ -33,13 +35,34 @@ namespace Avalonia.Input /// The element to test. /// The point on . /// The topmost at the specified position. - public static IInputElement InputHitTest(this IInputElement element, Point p) + public static IInputElement? InputHitTest(this IInputElement element, Point p) { - Contract.Requires(element != null); + element = element ?? throw new ArgumentNullException(nameof(element)); return element.GetVisualAt(p, s_hitTestDelegate) as IInputElement; } + /// + /// Returns the topmost active input element at a point on an . + /// + /// The element to test. + /// The point on . + /// + /// A filter predicate. If the predicate returns false then the visual and all its + /// children will be excluded from the results. + /// + /// The topmost at the specified position. + public static IInputElement? InputHitTest( + this IInputElement element, + Point p, + Func filter) + { + element = element ?? throw new ArgumentNullException(nameof(element)); + filter = filter ?? throw new ArgumentNullException(nameof(filter)); + + return element.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement; + } + private static bool IsHitTestVisible(IVisual visual) { var element = visual as IInputElement; diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index 730ca2bd6e..2da23410d6 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -11,4 +11,5 @@ + \ No newline at end of file diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index 410a0791b1..076ec56cd6 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -8,4 +8,5 @@ + diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj index 1a2bdeef1e..f084411c2f 100644 --- a/src/Avalonia.Native/Avalonia.Native.csproj +++ b/src/Avalonia.Native/Avalonia.Native.csproj @@ -7,6 +7,7 @@ /usr/bin/castxml /usr/local/bin/castxml true + false @@ -18,7 +19,7 @@ - + diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index 0f2551ffeb..6d1b95b997 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -11,7 +11,7 @@ namespace Avalonia.Native class AvaloniaNativeMenuExporter : ITopLevelNativeMenuExporter { private IAvaloniaNativeFactory _factory; - private bool _resetQueued; + private bool _resetQueued = true; private bool _exported = false; private IAvnWindow _nativeWindow; private NativeMenu _menu; @@ -39,8 +39,7 @@ namespace Avalonia.Native public void SetNativeMenu(NativeMenu menu) { _menu = menu == null ? new NativeMenu() : menu; - - DoLayoutReset(); + DoLayoutReset(true); } internal void UpdateIfNeeded() @@ -74,31 +73,34 @@ namespace Avalonia.Native return result; } - void DoLayoutReset() + private void DoLayoutReset(bool forceUpdate = false) { - _resetQueued = false; - - if (_nativeWindow is null) + if (_resetQueued || forceUpdate) { - var appMenu = NativeMenu.GetMenu(Application.Current); + _resetQueued = false; - if (appMenu == null) + if (_nativeWindow is null) { - appMenu = CreateDefaultAppMenu(); - NativeMenu.SetMenu(Application.Current, appMenu); - } + var appMenu = NativeMenu.GetMenu(Application.Current); - SetMenu(appMenu); - } - else - { - if (_menu != null) + if (appMenu == null) + { + appMenu = CreateDefaultAppMenu(); + NativeMenu.SetMenu(Application.Current, appMenu); + } + + SetMenu(appMenu); + } + else { - SetMenu(_nativeWindow, _menu); + if (_menu != null) + { + SetMenu(_nativeWindow, _menu); + } } - } - _exported = true; + _exported = true; + } } internal void QueueReset() @@ -106,7 +108,7 @@ namespace Avalonia.Native if (_resetQueued) return; _resetQueued = true; - Dispatcher.UIThread.Post(DoLayoutReset, DispatcherPriority.Background); + Dispatcher.UIThread.Post(() => DoLayoutReset(), DispatcherPriority.Background); } private void SetMenu(NativeMenu menu) diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index cfd47d48de..804cf7f8ac 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -110,11 +110,20 @@ namespace Avalonia.Native .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta)) .Bind().ToConstant(new MacOSMountedVolumeInfoProvider()) - .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)) - ; + .Bind().ToConstant(new AvaloniaNativeDragSource(_factory)); + if (_options.UseGpu) - AvaloniaLocator.CurrentMutable.Bind() - .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay())); + { + try + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay())); + } + catch (Exception) + { + // ignored + } + } } public IWindowImpl CreateWindow() diff --git a/src/Avalonia.Native/Generated/Enumerations.cs b/src/Avalonia.Native/Generated/Enumerations.cs new file mode 100644 index 0000000000..0b30ce50c0 --- /dev/null +++ b/src/Avalonia.Native/Generated/Enumerations.cs @@ -0,0 +1,628 @@ +// + +namespace Avalonia.Native.Interop +{ + /// + /// No documentation. + /// + /// AvnDragDropEffects + /// AvnDragDropEffects + public enum AvnDragDropEffects : System.Int32 + { + /// + /// No documentation. + /// + /// None + /// None + None = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// Copy + /// Copy + Copy = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// Move + /// Move + Move = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// Link + /// Link + Link = unchecked ((System.Int32)(4))} + + /// + /// No documentation. + /// + /// AvnDragEventType + /// AvnDragEventType + public enum AvnDragEventType : System.Int32 + { + /// + /// No documentation. + /// + /// Enter + /// Enter + Enter = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// Over + /// Over + Over = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// Leave + /// Leave + Leave = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// Drop + /// Drop + Drop = unchecked ((System.Int32)(3))} + + /// + /// No documentation. + /// + /// AvnExtendClientAreaChromeHints + /// AvnExtendClientAreaChromeHints + public enum AvnExtendClientAreaChromeHints : System.Int32 + { + /// + /// No documentation. + /// + /// AvnNoChrome + /// AvnNoChrome + AvnNoChrome = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// AvnSystemChrome + /// AvnSystemChrome + AvnSystemChrome = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// AvnPreferSystemChrome + /// AvnPreferSystemChrome + AvnPreferSystemChrome = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// AvnOSXThickTitleBar + /// AvnOSXThickTitleBar + AvnOSXThickTitleBar = unchecked ((System.Int32)(8)), + /// + /// No documentation. + /// + /// AvnDefaultChrome + /// AvnDefaultChrome + AvnDefaultChrome = unchecked ((System.Int32)(1))} + + /// + /// No documentation. + /// + /// AvnInputModifiers + /// AvnInputModifiers + public enum AvnInputModifiers : System.Int32 + { + /// + /// No documentation. + /// + /// AvnInputModifiersNone + /// AvnInputModifiersNone + AvnInputModifiersNone = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// Alt + /// Alt + Alt = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// Control + /// Control + Control = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// Shift + /// Shift + Shift = unchecked ((System.Int32)(4)), + /// + /// No documentation. + /// + /// Windows + /// Windows + Windows = unchecked ((System.Int32)(8)), + /// + /// No documentation. + /// + /// LeftMouseButton + /// LeftMouseButton + LeftMouseButton = unchecked ((System.Int32)(16)), + /// + /// No documentation. + /// + /// RightMouseButton + /// RightMouseButton + RightMouseButton = unchecked ((System.Int32)(32)), + /// + /// No documentation. + /// + /// MiddleMouseButton + /// MiddleMouseButton + MiddleMouseButton = unchecked ((System.Int32)(64)), + /// + /// No documentation. + /// + /// XButton1MouseButton + /// XButton1MouseButton + XButton1MouseButton = unchecked ((System.Int32)(128)), + /// + /// No documentation. + /// + /// XButton2MouseButton + /// XButton2MouseButton + XButton2MouseButton = unchecked ((System.Int32)(256))} + + /// + /// No documentation. + /// + /// AvnMenuItemToggleType + /// AvnMenuItemToggleType + public enum AvnMenuItemToggleType : System.Int32 + { + /// + /// No documentation. + /// + /// None + /// None + None = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// CheckMark + /// CheckMark + CheckMark = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// Radio + /// Radio + Radio = unchecked ((System.Int32)(2))} + + /// + /// No documentation. + /// + /// AvnPixelFormat + /// AvnPixelFormat + public enum AvnPixelFormat : System.Int32 + { + /// + /// No documentation. + /// + /// kAvnRgb565 + /// kAvnRgb565 + KAvnRgb565 = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// kAvnRgba8888 + /// kAvnRgba8888 + KAvnRgba8888 = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// kAvnBgra8888 + /// kAvnBgra8888 + KAvnBgra8888 = unchecked ((System.Int32)(2))} + + /// + /// No documentation. + /// + /// AvnRawKeyEventType + /// AvnRawKeyEventType + public enum AvnRawKeyEventType : System.Int32 + { + /// + /// No documentation. + /// + /// KeyDown + /// KeyDown + KeyDown = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// KeyUp + /// KeyUp + KeyUp = unchecked ((System.Int32)(1))} + + /// + /// No documentation. + /// + /// AvnRawMouseEventType + /// AvnRawMouseEventType + public enum AvnRawMouseEventType : System.Int32 + { + /// + /// No documentation. + /// + /// LeaveWindow + /// LeaveWindow + LeaveWindow = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// LeftButtonDown + /// LeftButtonDown + LeftButtonDown = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// LeftButtonUp + /// LeftButtonUp + LeftButtonUp = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// RightButtonDown + /// RightButtonDown + RightButtonDown = unchecked ((System.Int32)(3)), + /// + /// No documentation. + /// + /// RightButtonUp + /// RightButtonUp + RightButtonUp = unchecked ((System.Int32)(4)), + /// + /// No documentation. + /// + /// MiddleButtonDown + /// MiddleButtonDown + MiddleButtonDown = unchecked ((System.Int32)(5)), + /// + /// No documentation. + /// + /// MiddleButtonUp + /// MiddleButtonUp + MiddleButtonUp = unchecked ((System.Int32)(6)), + /// + /// No documentation. + /// + /// XButton1Down + /// XButton1Down + XButton1Down = unchecked ((System.Int32)(7)), + /// + /// No documentation. + /// + /// XButton1Up + /// XButton1Up + XButton1Up = unchecked ((System.Int32)(8)), + /// + /// No documentation. + /// + /// XButton2Down + /// XButton2Down + XButton2Down = unchecked ((System.Int32)(9)), + /// + /// No documentation. + /// + /// XButton2Up + /// XButton2Up + XButton2Up = unchecked ((System.Int32)(10)), + /// + /// No documentation. + /// + /// Move + /// Move + Move = unchecked ((System.Int32)(11)), + /// + /// No documentation. + /// + /// Wheel + /// Wheel + Wheel = unchecked ((System.Int32)(12)), + /// + /// No documentation. + /// + /// NonClientLeftButtonDown + /// NonClientLeftButtonDown + NonClientLeftButtonDown = unchecked ((System.Int32)(13)), + /// + /// No documentation. + /// + /// TouchBegin + /// TouchBegin + TouchBegin = unchecked ((System.Int32)(14)), + /// + /// No documentation. + /// + /// TouchUpdate + /// TouchUpdate + TouchUpdate = unchecked ((System.Int32)(15)), + /// + /// No documentation. + /// + /// TouchEnd + /// TouchEnd + TouchEnd = unchecked ((System.Int32)(16)), + /// + /// No documentation. + /// + /// TouchCancel + /// TouchCancel + TouchCancel = unchecked ((System.Int32)(17))} + + /// + /// No documentation. + /// + /// AvnStandardCursorType + /// AvnStandardCursorType + public enum AvnStandardCursorType : System.Int32 + { + /// + /// No documentation. + /// + /// CursorArrow + /// CursorArrow + CursorArrow = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// CursorIbeam + /// CursorIbeam + CursorIbeam = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// CursorWait + /// CursorWait + CursorWait = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// CursorCross + /// CursorCross + CursorCross = unchecked ((System.Int32)(3)), + /// + /// No documentation. + /// + /// CursorUpArrow + /// CursorUpArrow + CursorUpArrow = unchecked ((System.Int32)(4)), + /// + /// No documentation. + /// + /// CursorSizeWestEast + /// CursorSizeWestEast + CursorSizeWestEast = unchecked ((System.Int32)(5)), + /// + /// No documentation. + /// + /// CursorSizeNorthSouth + /// CursorSizeNorthSouth + CursorSizeNorthSouth = unchecked ((System.Int32)(6)), + /// + /// No documentation. + /// + /// CursorSizeAll + /// CursorSizeAll + CursorSizeAll = unchecked ((System.Int32)(7)), + /// + /// No documentation. + /// + /// CursorNo + /// CursorNo + CursorNo = unchecked ((System.Int32)(8)), + /// + /// No documentation. + /// + /// CursorHand + /// CursorHand + CursorHand = unchecked ((System.Int32)(9)), + /// + /// No documentation. + /// + /// CursorAppStarting + /// CursorAppStarting + CursorAppStarting = unchecked ((System.Int32)(10)), + /// + /// No documentation. + /// + /// CursorHelp + /// CursorHelp + CursorHelp = unchecked ((System.Int32)(11)), + /// + /// No documentation. + /// + /// CursorTopSide + /// CursorTopSide + CursorTopSide = unchecked ((System.Int32)(12)), + /// + /// No documentation. + /// + /// CursorBottomSize + /// CursorBottomSize + CursorBottomSize = unchecked ((System.Int32)(13)), + /// + /// No documentation. + /// + /// CursorLeftSide + /// CursorLeftSide + CursorLeftSide = unchecked ((System.Int32)(14)), + /// + /// No documentation. + /// + /// CursorRightSide + /// CursorRightSide + CursorRightSide = unchecked ((System.Int32)(15)), + /// + /// No documentation. + /// + /// CursorTopLeftCorner + /// CursorTopLeftCorner + CursorTopLeftCorner = unchecked ((System.Int32)(16)), + /// + /// No documentation. + /// + /// CursorTopRightCorner + /// CursorTopRightCorner + CursorTopRightCorner = unchecked ((System.Int32)(17)), + /// + /// No documentation. + /// + /// CursorBottomLeftCorner + /// CursorBottomLeftCorner + CursorBottomLeftCorner = unchecked ((System.Int32)(18)), + /// + /// No documentation. + /// + /// CursorBottomRightCorner + /// CursorBottomRightCorner + CursorBottomRightCorner = unchecked ((System.Int32)(19)), + /// + /// No documentation. + /// + /// CursorDragMove + /// CursorDragMove + CursorDragMove = unchecked ((System.Int32)(20)), + /// + /// No documentation. + /// + /// CursorDragCopy + /// CursorDragCopy + CursorDragCopy = unchecked ((System.Int32)(21)), + /// + /// No documentation. + /// + /// CursorDragLink + /// CursorDragLink + CursorDragLink = unchecked ((System.Int32)(22)), + /// + /// No documentation. + /// + /// CursorNone + /// CursorNone + CursorNone = unchecked ((System.Int32)(23))} + + /// + /// No documentation. + /// + /// AvnWindowEdge + /// AvnWindowEdge + public enum AvnWindowEdge : System.Int32 + { + /// + /// No documentation. + /// + /// WindowEdgeNorthWest + /// WindowEdgeNorthWest + WindowEdgeNorthWest = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// WindowEdgeNorth + /// WindowEdgeNorth + WindowEdgeNorth = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// WindowEdgeNorthEast + /// WindowEdgeNorthEast + WindowEdgeNorthEast = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// WindowEdgeWest + /// WindowEdgeWest + WindowEdgeWest = unchecked ((System.Int32)(3)), + /// + /// No documentation. + /// + /// WindowEdgeEast + /// WindowEdgeEast + WindowEdgeEast = unchecked ((System.Int32)(4)), + /// + /// No documentation. + /// + /// WindowEdgeSouthWest + /// WindowEdgeSouthWest + WindowEdgeSouthWest = unchecked ((System.Int32)(5)), + /// + /// No documentation. + /// + /// WindowEdgeSouth + /// WindowEdgeSouth + WindowEdgeSouth = unchecked ((System.Int32)(6)), + /// + /// No documentation. + /// + /// WindowEdgeSouthEast + /// WindowEdgeSouthEast + WindowEdgeSouthEast = unchecked ((System.Int32)(7))} + + /// + /// No documentation. + /// + /// AvnWindowState + /// AvnWindowState + public enum AvnWindowState : System.Int32 + { + /// + /// No documentation. + /// + /// Normal + /// Normal + Normal = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// Minimized + /// Minimized + Minimized = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// Maximized + /// Maximized + Maximized = unchecked ((System.Int32)(2)), + /// + /// No documentation. + /// + /// FullScreen + /// FullScreen + FullScreen = unchecked ((System.Int32)(3))} + + /// + /// No documentation. + /// + /// SystemDecorations + /// SystemDecorations + public enum SystemDecorations : System.Int32 + { + /// + /// No documentation. + /// + /// SystemDecorationsNone + /// SystemDecorationsNone + SystemDecorationsNone = unchecked ((System.Int32)(0)), + /// + /// No documentation. + /// + /// SystemDecorationsBorderOnly + /// SystemDecorationsBorderOnly + SystemDecorationsBorderOnly = unchecked ((System.Int32)(1)), + /// + /// No documentation. + /// + /// SystemDecorationsFull + /// SystemDecorationsFull + SystemDecorationsFull = unchecked ((System.Int32)(2))} +} \ No newline at end of file diff --git a/src/Avalonia.Native/Generated/Functions.cs b/src/Avalonia.Native/Generated/Functions.cs new file mode 100644 index 0000000000..6ab648dc50 --- /dev/null +++ b/src/Avalonia.Native/Generated/Functions.cs @@ -0,0 +1,5 @@ +// + +namespace Avalonia.Native.Interop +{ +} \ No newline at end of file diff --git a/src/Avalonia.Native/Generated/Interfaces.cs b/src/Avalonia.Native/Generated/Interfaces.cs new file mode 100644 index 0000000000..161ada1e50 --- /dev/null +++ b/src/Avalonia.Native/Generated/Interfaces.cs @@ -0,0 +1,3092 @@ +// + +namespace Avalonia.Native.Interop +{ + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f01")] + public partial class IAvaloniaNativeFactory : SharpGen.Runtime.ComObject + { + public IAvaloniaNativeFactory(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvaloniaNativeFactory(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvaloniaNativeFactory(nativePtr); + /// + /// No documentation. + /// + /// GetMacOptions + /// GetMacOptions + public Avalonia.Native.Interop.IAvnMacOptions MacOptions + { + get => GetMacOptions(); + } + + /// + /// No documentation. + /// + /// SetAppMenu + /// SetAppMenu + public Avalonia.Native.Interop.IAvnMenu AppMenu + { + set => SetAppMenu(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::Initialize([In] IAvnGCHandleDeallocatorCallback* deallocator) + /// IAvaloniaNativeFactory::Initialize + public unsafe void Initialize(Avalonia.Native.Interop.IAvnGCHandleDeallocatorCallback deallocator) + { + System.IntPtr deallocator_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + deallocator_ = SharpGen.Runtime.CppObject.ToCallbackPtr(deallocator); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)deallocator_, (*(void ***)this._nativePointer)[3]); + System.GC.KeepAlive(deallocator); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// IAvnMacOptions* IAvaloniaNativeFactory::GetMacOptions() + /// IAvaloniaNativeFactory::GetMacOptions + internal unsafe Avalonia.Native.Interop.IAvnMacOptions GetMacOptions() + { + Avalonia.Native.Interop.IAvnMacOptions __result__; + System.IntPtr __result__native = System.IntPtr.Zero; + __result__native = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (*(void ***)this._nativePointer)[4]); + if (__result__native != System.IntPtr.Zero) + __result__ = new Avalonia.Native.Interop.IAvnMacOptions(__result__native); + else + __result__ = null; + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateWindow([In] IAvnWindowEvents* cb,[In] IAvnGlContext* gl,[In] IAvnWindow** ppv) + /// IAvaloniaNativeFactory::CreateWindow + public unsafe Avalonia.Native.Interop.IAvnWindow CreateWindow(Avalonia.Native.Interop.IAvnWindowEvents cb, Avalonia.Native.Interop.IAvnGlContext gl) + { + System.IntPtr cb_ = System.IntPtr.Zero; + System.IntPtr gl_ = System.IntPtr.Zero; + Avalonia.Native.Interop.IAvnWindow vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + cb_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cb); + gl_ = SharpGen.Runtime.CppObject.ToCallbackPtr(gl); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)cb_, (void *)gl_, &vOut_, (*(void ***)this._nativePointer)[5]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnWindow(vOut_); + else + vOut = null; + System.GC.KeepAlive(cb); + System.GC.KeepAlive(gl); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreatePopup([In] IAvnWindowEvents* cb,[In] IAvnGlContext* gl,[In] IAvnPopup** ppv) + /// IAvaloniaNativeFactory::CreatePopup + public unsafe Avalonia.Native.Interop.IAvnPopup CreatePopup(Avalonia.Native.Interop.IAvnWindowEvents cb, Avalonia.Native.Interop.IAvnGlContext gl) + { + System.IntPtr cb_ = System.IntPtr.Zero; + System.IntPtr gl_ = System.IntPtr.Zero; + Avalonia.Native.Interop.IAvnPopup vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + cb_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cb); + gl_ = SharpGen.Runtime.CppObject.ToCallbackPtr(gl); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)cb_, (void *)gl_, &vOut_, (*(void ***)this._nativePointer)[6]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnPopup(vOut_); + else + vOut = null; + System.GC.KeepAlive(cb); + System.GC.KeepAlive(gl); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreatePlatformThreadingInterface([In] IAvnPlatformThreadingInterface** ppv) + /// IAvaloniaNativeFactory::CreatePlatformThreadingInterface + public unsafe Avalonia.Native.Interop.IAvnPlatformThreadingInterface CreatePlatformThreadingInterface() + { + Avalonia.Native.Interop.IAvnPlatformThreadingInterface vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[7]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnPlatformThreadingInterface(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateSystemDialogs([In] IAvnSystemDialogs** ppv) + /// IAvaloniaNativeFactory::CreateSystemDialogs + public unsafe Avalonia.Native.Interop.IAvnSystemDialogs CreateSystemDialogs() + { + Avalonia.Native.Interop.IAvnSystemDialogs vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[8]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnSystemDialogs(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateScreens([In] IAvnScreens** ppv) + /// IAvaloniaNativeFactory::CreateScreens + public unsafe Avalonia.Native.Interop.IAvnScreens CreateScreens() + { + Avalonia.Native.Interop.IAvnScreens vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[9]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnScreens(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateClipboard([In] IAvnClipboard** ppv) + /// IAvaloniaNativeFactory::CreateClipboard + public unsafe Avalonia.Native.Interop.IAvnClipboard CreateClipboard() + { + Avalonia.Native.Interop.IAvnClipboard vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[10]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnClipboard(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateDndClipboard([In] IAvnClipboard** ppv) + /// IAvaloniaNativeFactory::CreateDndClipboard + public unsafe Avalonia.Native.Interop.IAvnClipboard CreateDndClipboard() + { + Avalonia.Native.Interop.IAvnClipboard vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[11]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnClipboard(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateCursorFactory([In] IAvnCursorFactory** ppv) + /// IAvaloniaNativeFactory::CreateCursorFactory + public unsafe Avalonia.Native.Interop.IAvnCursorFactory CreateCursorFactory() + { + Avalonia.Native.Interop.IAvnCursorFactory vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[12]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnCursorFactory(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::ObtainGlDisplay([In] IAvnGlDisplay** ppv) + /// IAvaloniaNativeFactory::ObtainGlDisplay + public unsafe Avalonia.Native.Interop.IAvnGlDisplay ObtainGlDisplay() + { + Avalonia.Native.Interop.IAvnGlDisplay vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[13]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnGlDisplay(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::SetAppMenu([In] IAvnMenu* menu) + /// IAvaloniaNativeFactory::SetAppMenu + internal unsafe void SetAppMenu(Avalonia.Native.Interop.IAvnMenu menu) + { + System.IntPtr menu_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + menu_ = SharpGen.Runtime.CppObject.ToCallbackPtr(menu); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)menu_, (*(void ***)this._nativePointer)[14]); + System.GC.KeepAlive(menu); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateMenu([In] IAvnMenuEvents* cb,[In] IAvnMenu** ppv) + /// IAvaloniaNativeFactory::CreateMenu + public unsafe Avalonia.Native.Interop.IAvnMenu CreateMenu(Avalonia.Native.Interop.IAvnMenuEvents cb) + { + System.IntPtr cb_ = System.IntPtr.Zero; + Avalonia.Native.Interop.IAvnMenu vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + cb_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cb); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)cb_, &vOut_, (*(void ***)this._nativePointer)[15]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnMenu(vOut_); + else + vOut = null; + System.GC.KeepAlive(cb); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateMenuItem([In] IAvnMenuItem** ppv) + /// IAvaloniaNativeFactory::CreateMenuItem + public unsafe Avalonia.Native.Interop.IAvnMenuItem CreateMenuItem() + { + Avalonia.Native.Interop.IAvnMenuItem vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[16]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnMenuItem(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvaloniaNativeFactory::CreateMenuItemSeperator([In] IAvnMenuItem** ppv) + /// IAvaloniaNativeFactory::CreateMenuItemSeperator + public unsafe Avalonia.Native.Interop.IAvnMenuItem CreateMenuItemSeperator() + { + Avalonia.Native.Interop.IAvnMenuItem vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[17]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnMenuItem(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + } + + class IAvnActionCallbackShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnActionCallbackVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnActionCallbackVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new RunDelegate(Run)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void RunDelegate(System.IntPtr thisObject); + private static unsafe void Run(System.IntPtr thisObject) + { + try + { + IAvnActionCallback @this = (IAvnActionCallback)ToShadow(thisObject).Callback; + @this.Run(); + } + catch (System.Exception __exception__) + { + IAvnActionCallback @this = (IAvnActionCallback)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnActionCallbackShadow.IAvnActionCallbackVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f08"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnActionCallbackShadow))] + public partial interface IAvnActionCallback : SharpGen.Runtime.IUnknown + { + void Run(); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0f")] + public partial class IAvnClipboard : SharpGen.Runtime.ComObject + { + public IAvnClipboard(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnClipboard(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnClipboard(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnClipboard::GetText([In] char* type,[In] IAvnString** ppv) + /// IAvnClipboard::GetText + public unsafe Avalonia.Native.Interop.IAvnString GetText(System.String type) + { + System.IntPtr type_; + Avalonia.Native.Interop.IAvnString vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + type_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(type); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)type_, &vOut_, (*(void ***)this._nativePointer)[3]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnString(vOut_); + else + vOut = null; + System.Runtime.InteropServices.Marshal.FreeHGlobal(type_); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnClipboard::SetText([In] char* type,[In] void* utf8Text) + /// IAvnClipboard::SetText + public unsafe void SetText(System.String type, System.IntPtr utf8Text) + { + System.IntPtr type_; + SharpGen.Runtime.Result __result__; + type_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(type); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)type_, (void *)utf8Text, (*(void ***)this._nativePointer)[4]); + System.Runtime.InteropServices.Marshal.FreeHGlobal(type_); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnClipboard::ObtainFormats([In] IAvnStringArray** ppv) + /// IAvnClipboard::ObtainFormats + public unsafe Avalonia.Native.Interop.IAvnStringArray ObtainFormats() + { + Avalonia.Native.Interop.IAvnStringArray vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[5]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnStringArray(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnClipboard::GetStrings([In] char* type,[In] IAvnStringArray** ppv) + /// IAvnClipboard::GetStrings + public unsafe Avalonia.Native.Interop.IAvnStringArray GetStrings(System.String type) + { + System.IntPtr type_; + Avalonia.Native.Interop.IAvnStringArray vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + type_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(type); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)type_, &vOut_, (*(void ***)this._nativePointer)[6]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnStringArray(vOut_); + else + vOut = null; + System.Runtime.InteropServices.Marshal.FreeHGlobal(type_); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnClipboard::SetBytes([In] char* type,[In] void* utf8Text,[In] int len) + /// IAvnClipboard::SetBytes + public unsafe void SetBytes(System.String type, System.IntPtr utf8Text, System.Int32 len) + { + System.IntPtr type_; + SharpGen.Runtime.Result __result__; + type_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(type); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)type_, (void *)utf8Text, len, (*(void ***)this._nativePointer)[7]); + System.Runtime.InteropServices.Marshal.FreeHGlobal(type_); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnClipboard::GetBytes([In] char* type,[In] IAvnString** ppv) + /// IAvnClipboard::GetBytes + public unsafe Avalonia.Native.Interop.IAvnString GetBytes(System.String type) + { + System.IntPtr type_; + Avalonia.Native.Interop.IAvnString vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + type_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(type); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)type_, &vOut_, (*(void ***)this._nativePointer)[8]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnString(vOut_); + else + vOut = null; + System.Runtime.InteropServices.Marshal.FreeHGlobal(type_); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnClipboard::Clear() + /// IAvnClipboard::Clear + public unsafe void Clear() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[9]); + __result__.CheckError(); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f10")] + public partial class IAvnCursor : SharpGen.Runtime.ComObject + { + public IAvnCursor(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnCursor(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnCursor(nativePtr); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f11")] + public partial class IAvnCursorFactory : SharpGen.Runtime.ComObject + { + public IAvnCursorFactory(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnCursorFactory(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnCursorFactory(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnCursorFactory::GetCursor([In] AvnStandardCursorType cursorType,[Out] IAvnCursor** retOut) + /// IAvnCursorFactory::GetCursor + public unsafe Avalonia.Native.Interop.IAvnCursor GetCursor(Avalonia.Native.Interop.AvnStandardCursorType cursorType) + { + Avalonia.Native.Interop.IAvnCursor retOut; + System.IntPtr retOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)cursorType), &retOut_, (*(void ***)this._nativePointer)[3]); + if (retOut_ != System.IntPtr.Zero) + retOut = new Avalonia.Native.Interop.IAvnCursor(retOut_); + else + retOut = null; + __result__.CheckError(); + return retOut; + } + } + + class IAvnDndResultCallbackShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnDndResultCallbackVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnDndResultCallbackVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new OnDragAndDropCompleteDelegate(OnDragAndDropComplete)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void OnDragAndDropCompleteDelegate(System.IntPtr thisObject, int arg0); + private static unsafe void OnDragAndDropComplete(System.IntPtr thisObject, int param0) + { + try + { + Avalonia.Native.Interop.AvnDragDropEffects effecct = default (Avalonia.Native.Interop.AvnDragDropEffects); + effecct = (Avalonia.Native.Interop.AvnDragDropEffects)param0; + IAvnDndResultCallback @this = (IAvnDndResultCallback)ToShadow(thisObject).Callback; + @this.OnDragAndDropComplete(effecct); + } + catch (System.Exception __exception__) + { + IAvnDndResultCallback @this = (IAvnDndResultCallback)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnDndResultCallbackShadow.IAvnDndResultCallbackVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f21"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnDndResultCallbackShadow))] + public partial interface IAvnDndResultCallback : SharpGen.Runtime.IUnknown + { + void OnDragAndDropComplete(Avalonia.Native.Interop.AvnDragDropEffects effecct); + } + + class IAvnGCHandleDeallocatorCallbackShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnGCHandleDeallocatorCallbackVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnGCHandleDeallocatorCallbackVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new FreeGCHandleDelegate(FreeGCHandle)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void FreeGCHandleDelegate(System.IntPtr thisObject, void *arg0); + private static unsafe void FreeGCHandle(System.IntPtr thisObject, void *param0) + { + try + { + System.IntPtr handle = default (System.IntPtr); + handle = (System.IntPtr)param0; + IAvnGCHandleDeallocatorCallback @this = (IAvnGCHandleDeallocatorCallback)ToShadow(thisObject).Callback; + @this.FreeGCHandle(handle); + } + catch (System.Exception __exception__) + { + IAvnGCHandleDeallocatorCallback @this = (IAvnGCHandleDeallocatorCallback)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnGCHandleDeallocatorCallbackShadow.IAvnGCHandleDeallocatorCallbackVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f22"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnGCHandleDeallocatorCallbackShadow))] + public partial interface IAvnGCHandleDeallocatorCallback : SharpGen.Runtime.IUnknown + { + void FreeGCHandle(System.IntPtr handle); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f14")] + public partial class IAvnGlContext : SharpGen.Runtime.ComObject + { + public IAvnGlContext(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnGlContext(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnGlContext(nativePtr); + /// + /// No documentation. + /// + /// GetSampleCount + /// GetSampleCount + public System.Int32 SampleCount + { + get => GetSampleCount(); + } + + /// + /// No documentation. + /// + /// GetStencilSize + /// GetStencilSize + public System.Int32 StencilSize + { + get => GetStencilSize(); + } + + /// + /// No documentation. + /// + /// GetNativeHandle + /// GetNativeHandle + public System.IntPtr NativeHandle + { + get => GetNativeHandle(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnGlContext::MakeCurrent([In] IUnknown** ppv) + /// IAvnGlContext::MakeCurrent + public unsafe SharpGen.Runtime.IUnknown MakeCurrent() + { + SharpGen.Runtime.IUnknown vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &vOut_, (*(void ***)this._nativePointer)[3]); + if (vOut_ != System.IntPtr.Zero) + vOut = new SharpGen.Runtime.ComObject(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnGlContext::LegacyMakeCurrent() + /// IAvnGlContext::LegacyMakeCurrent + public unsafe void LegacyMakeCurrent() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// int IAvnGlContext::GetSampleCount() + /// IAvnGlContext::GetSampleCount + internal unsafe System.Int32 GetSampleCount() + { + System.Int32 __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[5]); + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// int IAvnGlContext::GetStencilSize() + /// IAvnGlContext::GetStencilSize + internal unsafe System.Int32 GetStencilSize() + { + System.Int32 __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[6]); + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// void* IAvnGlContext::GetNativeHandle() + /// IAvnGlContext::GetNativeHandle + internal unsafe System.IntPtr GetNativeHandle() + { + System.IntPtr __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (*(void ***)this._nativePointer)[7]); + return __result__; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f13")] + public partial class IAvnGlDisplay : SharpGen.Runtime.ComObject + { + public IAvnGlDisplay(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnGlDisplay(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnGlDisplay(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnGlDisplay::CreateContext([In] IAvnGlContext* share,[In] IAvnGlContext** ppv) + /// IAvnGlDisplay::CreateContext + public unsafe Avalonia.Native.Interop.IAvnGlContext CreateContext(Avalonia.Native.Interop.IAvnGlContext share) + { + System.IntPtr share_ = System.IntPtr.Zero; + Avalonia.Native.Interop.IAvnGlContext vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + share_ = SharpGen.Runtime.CppObject.ToCallbackPtr(share); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)share_, &vOut_, (*(void ***)this._nativePointer)[3]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnGlContext(vOut_); + else + vOut = null; + System.GC.KeepAlive(share); + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// void IAvnGlDisplay::LegacyClearCurrentContext() + /// IAvnGlDisplay::LegacyClearCurrentContext + public unsafe void LegacyClearCurrentContext() + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (*(void ***)this._nativePointer)[4]); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnGlDisplay::WrapContext([In] void* native,[In] IAvnGlContext** ppv) + /// IAvnGlDisplay::WrapContext + public unsafe Avalonia.Native.Interop.IAvnGlContext WrapContext(System.IntPtr native) + { + Avalonia.Native.Interop.IAvnGlContext vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)native, &vOut_, (*(void ***)this._nativePointer)[5]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnGlContext(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// void* IAvnGlDisplay::GetProcAddress([In] char* proc) + /// IAvnGlDisplay::GetProcAddress + public unsafe System.IntPtr GetProcAddress(System.String rocRef) + { + System.IntPtr rocRef_; + System.IntPtr __result__; + rocRef_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(rocRef); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (void *)rocRef_, (*(void ***)this._nativePointer)[6]); + System.Runtime.InteropServices.Marshal.FreeHGlobal(rocRef_); + return __result__; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f16")] + public partial class IAvnGlSurfaceRenderingSession : SharpGen.Runtime.ComObject + { + public IAvnGlSurfaceRenderingSession(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnGlSurfaceRenderingSession(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnGlSurfaceRenderingSession(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnGlSurfaceRenderingSession::GetPixelSize([In] AvnPixelSize* ret) + /// IAvnGlSurfaceRenderingSession::GetPixelSize + public unsafe Avalonia.Native.Interop.AvnPixelSize GetPixelSize() + { + Avalonia.Native.Interop.AvnPixelSize ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnGlSurfaceRenderingSession::GetScaling([In] double* ret) + /// IAvnGlSurfaceRenderingSession::GetScaling + public unsafe System.Double GetScaling() + { + System.Double ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + return ret; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f15")] + public partial class IAvnGlSurfaceRenderTarget : SharpGen.Runtime.ComObject + { + public IAvnGlSurfaceRenderTarget(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnGlSurfaceRenderTarget(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnGlSurfaceRenderTarget(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnGlSurfaceRenderTarget::BeginDrawing([In] IAvnGlSurfaceRenderingSession** ret) + /// IAvnGlSurfaceRenderTarget::BeginDrawing + public unsafe Avalonia.Native.Interop.IAvnGlSurfaceRenderingSession BeginDrawing() + { + Avalonia.Native.Interop.IAvnGlSurfaceRenderingSession ret; + System.IntPtr ret_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret_, (*(void ***)this._nativePointer)[3]); + if (ret_ != System.IntPtr.Zero) + ret = new Avalonia.Native.Interop.IAvnGlSurfaceRenderingSession(ret_); + else + ret = null; + __result__.CheckError(); + return ret; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0a")] + public partial class IAvnLoopCancellation : SharpGen.Runtime.ComObject + { + public IAvnLoopCancellation(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnLoopCancellation(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnLoopCancellation(nativePtr); + /// + /// No documentation. + /// + /// void IAvnLoopCancellation::Cancel() + /// IAvnLoopCancellation::Cancel + public unsafe void Cancel() + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (*(void ***)this._nativePointer)[3]); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f07")] + public partial class IAvnMacOptions : SharpGen.Runtime.ComObject + { + public IAvnMacOptions(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnMacOptions(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnMacOptions(nativePtr); + /// + /// No documentation. + /// + /// SetShowInDock + /// SetShowInDock + public System.Int32 ShowInDock + { + set => SetShowInDock(value); + } + + /// + /// No documentation. + /// + /// SetApplicationTitle + /// SetApplicationTitle + public System.IntPtr ApplicationTitle + { + set => SetApplicationTitle(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMacOptions::SetShowInDock([In] int show) + /// IAvnMacOptions::SetShowInDock + internal unsafe void SetShowInDock(System.Int32 show) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, show, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMacOptions::SetApplicationTitle([In] void* utf8string) + /// IAvnMacOptions::SetApplicationTitle + internal unsafe void SetApplicationTitle(System.IntPtr utf8string) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)utf8string, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f17")] + public partial class IAvnMenu : SharpGen.Runtime.ComObject + { + public IAvnMenu(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnMenu(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnMenu(nativePtr); + /// + /// No documentation. + /// + /// SetTitle + /// SetTitle + public System.IntPtr Title + { + set => SetTitle(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenu::InsertItem([In] int index,[In] IAvnMenuItem* item) + /// IAvnMenu::InsertItem + public unsafe void InsertItem(System.Int32 index, Avalonia.Native.Interop.IAvnMenuItem item) + { + System.IntPtr item_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + item_ = SharpGen.Runtime.CppObject.ToCallbackPtr(item); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, index, (void *)item_, (*(void ***)this._nativePointer)[3]); + System.GC.KeepAlive(item); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenu::RemoveItem([In] IAvnMenuItem* item) + /// IAvnMenu::RemoveItem + public unsafe void RemoveItem(Avalonia.Native.Interop.IAvnMenuItem item) + { + System.IntPtr item_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + item_ = SharpGen.Runtime.CppObject.ToCallbackPtr(item); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)item_, (*(void ***)this._nativePointer)[4]); + System.GC.KeepAlive(item); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenu::SetTitle([In] void* utf8String) + /// IAvnMenu::SetTitle + internal unsafe void SetTitle(System.IntPtr utf8String) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)utf8String, (*(void ***)this._nativePointer)[5]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnMenu::Clear() + /// IAvnMenu::Clear + public unsafe void Clear() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[6]); + __result__.CheckError(); + } + } + + class IAvnMenuEventsShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnMenuEventsVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnMenuEventsVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new NeedsUpdateDelegate(NeedsUpdate)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void NeedsUpdateDelegate(System.IntPtr thisObject); + private static unsafe void NeedsUpdate(System.IntPtr thisObject) + { + try + { + IAvnMenuEvents @this = (IAvnMenuEvents)ToShadow(thisObject).Callback; + @this.NeedsUpdate(); + } + catch (System.Exception __exception__) + { + IAvnMenuEvents @this = (IAvnMenuEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnMenuEventsShadow.IAvnMenuEventsVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f1A"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnMenuEventsShadow))] + public partial interface IAvnMenuEvents : SharpGen.Runtime.IUnknown + { + void NeedsUpdate(); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f19")] + public partial class IAvnMenuItem : SharpGen.Runtime.ComObject + { + public IAvnMenuItem(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnMenuItem(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnMenuItem(nativePtr); + /// + /// No documentation. + /// + /// SetSubMenu + /// SetSubMenu + public Avalonia.Native.Interop.IAvnMenu SubMenu + { + set => SetSubMenu(value); + } + + /// + /// No documentation. + /// + /// SetTitle + /// SetTitle + public System.IntPtr Title + { + set => SetTitle(value); + } + + /// + /// No documentation. + /// + /// SetIsChecked + /// SetIsChecked + public System.Boolean IsChecked + { + set => SetIsChecked(value); + } + + /// + /// No documentation. + /// + /// SetToggleType + /// SetToggleType + public Avalonia.Native.Interop.AvnMenuItemToggleType ToggleType + { + set => SetToggleType(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetSubMenu([In] IAvnMenu* menu) + /// IAvnMenuItem::SetSubMenu + internal unsafe void SetSubMenu(Avalonia.Native.Interop.IAvnMenu menu) + { + System.IntPtr menu_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + menu_ = SharpGen.Runtime.CppObject.ToCallbackPtr(menu); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)menu_, (*(void ***)this._nativePointer)[3]); + System.GC.KeepAlive(menu); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetTitle([In] void* utf8String) + /// IAvnMenuItem::SetTitle + internal unsafe void SetTitle(System.IntPtr utf8String) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)utf8String, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetGesture([In] void* utf8String,[In] AvnInputModifiers modifiers) + /// IAvnMenuItem::SetGesture + public unsafe void SetGesture(System.IntPtr utf8String, Avalonia.Native.Interop.AvnInputModifiers modifiers) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)utf8String, unchecked ((System.Int32)modifiers), (*(void ***)this._nativePointer)[5]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetAction([In] IAvnPredicateCallback* predicate,[In] IAvnActionCallback* callback) + /// IAvnMenuItem::SetAction + public unsafe void SetAction(Avalonia.Native.Interop.IAvnPredicateCallback redicateRef, Avalonia.Native.Interop.IAvnActionCallback callback) + { + System.IntPtr redicateRef_ = System.IntPtr.Zero; + System.IntPtr callback_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + redicateRef_ = SharpGen.Runtime.CppObject.ToCallbackPtr(redicateRef); + callback_ = SharpGen.Runtime.CppObject.ToCallbackPtr(callback); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)redicateRef_, (void *)callback_, (*(void ***)this._nativePointer)[6]); + System.GC.KeepAlive(redicateRef); + System.GC.KeepAlive(callback); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetIsChecked([In] bool isChecked) + /// IAvnMenuItem::SetIsChecked + internal unsafe void SetIsChecked(System.Boolean isChecked) + { + System.Byte isChecked_; + SharpGen.Runtime.Result __result__; + isChecked_ = (System.Byte)(isChecked ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, isChecked_, (*(void ***)this._nativePointer)[7]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetToggleType([In] AvnMenuItemToggleType toggleType) + /// IAvnMenuItem::SetToggleType + internal unsafe void SetToggleType(Avalonia.Native.Interop.AvnMenuItemToggleType toggleType) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)toggleType), (*(void ***)this._nativePointer)[8]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnMenuItem::SetIcon([In] void* data,[In] size_t length) + /// IAvnMenuItem::SetIcon + public unsafe void SetIcon(System.IntPtr data, SharpGen.Runtime.PointerSize length) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)data, (void *)length, (*(void ***)this._nativePointer)[9]); + __result__.CheckError(); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f20")] + public partial class IAvnNativeControlHost : SharpGen.Runtime.ComObject + { + public IAvnNativeControlHost(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnNativeControlHost(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnNativeControlHost(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnNativeControlHost::CreateDefaultChild([In] void* parent,[Out] void** retOut) + /// IAvnNativeControlHost::CreateDefaultChild + public unsafe System.IntPtr CreateDefaultChild(System.IntPtr arentRef) + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)arentRef, &retOut, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// IAvnNativeControlHostTopLevelAttachment* IAvnNativeControlHost::CreateAttachment() + /// IAvnNativeControlHost::CreateAttachment + public unsafe Avalonia.Native.Interop.IAvnNativeControlHostTopLevelAttachment CreateAttachment() + { + Avalonia.Native.Interop.IAvnNativeControlHostTopLevelAttachment __result__; + System.IntPtr __result__native = System.IntPtr.Zero; + __result__native = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (*(void ***)this._nativePointer)[4]); + if (__result__native != System.IntPtr.Zero) + __result__ = new Avalonia.Native.Interop.IAvnNativeControlHostTopLevelAttachment(__result__native); + else + __result__ = null; + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// void IAvnNativeControlHost::DestroyDefaultChild([In] void* child) + /// IAvnNativeControlHost::DestroyDefaultChild + public unsafe void DestroyDefaultChild(System.IntPtr child) + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (void *)child, (*(void ***)this._nativePointer)[5]); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f21")] + public partial class IAvnNativeControlHostTopLevelAttachment : SharpGen.Runtime.ComObject + { + public IAvnNativeControlHostTopLevelAttachment(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnNativeControlHostTopLevelAttachment(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnNativeControlHostTopLevelAttachment(nativePtr); + /// + /// No documentation. + /// + /// GetParentHandle + /// GetParentHandle + public System.IntPtr ParentHandle + { + get => GetParentHandle(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// void* IAvnNativeControlHostTopLevelAttachment::GetParentHandle() + /// IAvnNativeControlHostTopLevelAttachment::GetParentHandle + internal unsafe System.IntPtr GetParentHandle() + { + System.IntPtr __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (*(void ***)this._nativePointer)[3]); + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnNativeControlHostTopLevelAttachment::InitializeWithChildHandle([In] void* child) + /// IAvnNativeControlHostTopLevelAttachment::InitializeWithChildHandle + public unsafe void InitializeWithChildHandle(System.IntPtr child) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)child, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnNativeControlHostTopLevelAttachment::AttachTo([In] IAvnNativeControlHost* host) + /// IAvnNativeControlHostTopLevelAttachment::AttachTo + public unsafe void AttachTo(Avalonia.Native.Interop.IAvnNativeControlHost host) + { + System.IntPtr host_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + host_ = SharpGen.Runtime.CppObject.ToCallbackPtr(host); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)host_, (*(void ***)this._nativePointer)[5]); + System.GC.KeepAlive(host); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// void IAvnNativeControlHostTopLevelAttachment::ShowInBounds([In] float x,[In] float y,[In] float width,[In] float height) + /// IAvnNativeControlHostTopLevelAttachment::ShowInBounds + public unsafe void ShowInBounds(System.Single x, System.Single y, System.Single width, System.Single height) + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, x, y, width, height, (*(void ***)this._nativePointer)[6]); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// void IAvnNativeControlHostTopLevelAttachment::HideWithSize([In] float width,[In] float height) + /// IAvnNativeControlHostTopLevelAttachment::HideWithSize + public unsafe void HideWithSize(System.Single width, System.Single height) + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, width, height, (*(void ***)this._nativePointer)[7]); + } + + /// + /// No documentation. + /// + /// void IAvnNativeControlHostTopLevelAttachment::ReleaseChild() + /// IAvnNativeControlHostTopLevelAttachment::ReleaseChild + public unsafe void ReleaseChild() + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (*(void ***)this._nativePointer)[8]); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0b")] + public partial class IAvnPlatformThreadingInterface : SharpGen.Runtime.ComObject + { + public IAvnPlatformThreadingInterface(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnPlatformThreadingInterface(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnPlatformThreadingInterface(nativePtr); + /// + /// No documentation. + /// + /// GetCurrentThreadIsLoopThread + /// GetCurrentThreadIsLoopThread + public System.Boolean CurrentThreadIsLoopThread + { + get => GetCurrentThreadIsLoopThread(); + } + + /// + /// No documentation. + /// + /// SetSignaledCallback + /// SetSignaledCallback + public Avalonia.Native.Interop.IAvnSignaledCallback SignaledCallback + { + set => SetSignaledCallback(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// bool IAvnPlatformThreadingInterface::GetCurrentThreadIsLoopThread() + /// IAvnPlatformThreadingInterface::GetCurrentThreadIsLoopThread + internal unsafe System.Boolean GetCurrentThreadIsLoopThread() + { + System.Boolean __result__; + System.Byte __result__native; + __result__native = Avalonia.Native.LocalInterop.CalliThisCallSystemByte(this._nativePointer, (*(void ***)this._nativePointer)[3]); + __result__ = __result__native != 0; + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// void IAvnPlatformThreadingInterface::SetSignaledCallback([In] IAvnSignaledCallback* cb) + /// IAvnPlatformThreadingInterface::SetSignaledCallback + internal unsafe void SetSignaledCallback(Avalonia.Native.Interop.IAvnSignaledCallback cb) + { + System.IntPtr cb_ = System.IntPtr.Zero; + cb_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cb); + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (void *)cb_, (*(void ***)this._nativePointer)[4]); + System.GC.KeepAlive(cb); + } + + /// + /// No documentation. + /// + /// No documentation. + /// IAvnLoopCancellation* IAvnPlatformThreadingInterface::CreateLoopCancellation() + /// IAvnPlatformThreadingInterface::CreateLoopCancellation + public unsafe Avalonia.Native.Interop.IAvnLoopCancellation CreateLoopCancellation() + { + Avalonia.Native.Interop.IAvnLoopCancellation __result__; + System.IntPtr __result__native = System.IntPtr.Zero; + __result__native = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, (*(void ***)this._nativePointer)[5]); + if (__result__native != System.IntPtr.Zero) + __result__ = new Avalonia.Native.Interop.IAvnLoopCancellation(__result__native); + else + __result__ = null; + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnPlatformThreadingInterface::RunLoop([In] IAvnLoopCancellation* cancel) + /// IAvnPlatformThreadingInterface::RunLoop + public unsafe void RunLoop(Avalonia.Native.Interop.IAvnLoopCancellation cancel) + { + System.IntPtr cancel_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + cancel_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cancel); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)cancel_, (*(void ***)this._nativePointer)[6]); + System.GC.KeepAlive(cancel); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// void IAvnPlatformThreadingInterface::Signal([In] int priority) + /// IAvnPlatformThreadingInterface::Signal + public unsafe void Signal(System.Int32 priority) + { + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, priority, (*(void ***)this._nativePointer)[7]); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// IUnknown* IAvnPlatformThreadingInterface::StartTimer([In] int priority,[In] int ms,[In] IAvnActionCallback* callback) + /// IAvnPlatformThreadingInterface::StartTimer + public unsafe SharpGen.Runtime.ComObject StartTimer(System.Int32 priority, System.Int32 ms, Avalonia.Native.Interop.IAvnActionCallback callback) + { + System.IntPtr callback_ = System.IntPtr.Zero; + SharpGen.Runtime.ComObject __result__; + System.IntPtr __result__native = System.IntPtr.Zero; + callback_ = SharpGen.Runtime.CppObject.ToCallbackPtr(callback); + __result__native = Avalonia.Native.LocalInterop.CalliThisCallSystemIntPtr(this._nativePointer, priority, ms, (void *)callback_, (*(void ***)this._nativePointer)[8]); + if (__result__native != System.IntPtr.Zero) + __result__ = new SharpGen.Runtime.ComObject(__result__native); + else + __result__ = null; + System.GC.KeepAlive(callback); + return __result__; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f03")] + public partial class IAvnPopup : Avalonia.Native.Interop.IAvnWindowBase + { + public IAvnPopup(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnPopup(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnPopup(nativePtr); + } + + class IAvnPredicateCallbackShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnPredicateCallbackVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnPredicateCallbackVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new EvaluateDelegate(Evaluate)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate System.Byte EvaluateDelegate(System.IntPtr thisObject); + private static unsafe System.Byte Evaluate(System.IntPtr thisObject) + { + try + { + System.Boolean __result__ = default (System.Boolean); + System.Byte __result__native; + IAvnPredicateCallback @this = (IAvnPredicateCallback)ToShadow(thisObject).Callback; + __result__ = @this.Evaluate(); + __result__native = (System.Byte)(__result__ ? 1 : 0); + return __result__native; + } + catch (System.Exception __exception__) + { + IAvnPredicateCallback @this = (IAvnPredicateCallback)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return default (System.Byte); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnPredicateCallbackShadow.IAvnPredicateCallbackVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f18"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnPredicateCallbackShadow))] + public partial interface IAvnPredicateCallback : SharpGen.Runtime.IUnknown + { + System.Boolean Evaluate(); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0e")] + public partial class IAvnScreens : SharpGen.Runtime.ComObject + { + public IAvnScreens(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnScreens(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnScreens(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnScreens::GetScreenCount([In] int* ret) + /// IAvnScreens::GetScreenCount + public unsafe System.Int32 GetScreenCount() + { + System.Int32 ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnScreens::GetScreen([In] int index,[In] AvnScreen* ret) + /// IAvnScreens::GetScreen + public unsafe Avalonia.Native.Interop.AvnScreen GetScreen(System.Int32 index) + { + Avalonia.Native.Interop.AvnScreen ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, index, &ret, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + return ret; + } + } + + class IAvnSignaledCallbackShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnSignaledCallbackVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnSignaledCallbackVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new SignaledDelegate(Signaled)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void SignaledDelegate(System.IntPtr thisObject, int arg0, System.Byte arg1); + private static unsafe void Signaled(System.IntPtr thisObject, int param0, System.Byte param1) + { + try + { + System.Int32 priority = default (System.Int32); + priority = (System.Int32)param0; + System.Boolean priorityContainsMeaningfulValue = default (System.Boolean); + System.Byte priorityContainsMeaningfulValue_ = (System.Byte)param1; + IAvnSignaledCallback @this = (IAvnSignaledCallback)ToShadow(thisObject).Callback; + priorityContainsMeaningfulValue = priorityContainsMeaningfulValue_ != 0; + @this.Signaled(priority, priorityContainsMeaningfulValue); + } + catch (System.Exception __exception__) + { + IAvnSignaledCallback @this = (IAvnSignaledCallback)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnSignaledCallbackShadow.IAvnSignaledCallbackVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f09"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnSignaledCallbackShadow))] + public partial interface IAvnSignaledCallback : SharpGen.Runtime.IUnknown + { + void Signaled(System.Int32 priority, System.Boolean priorityContainsMeaningfulValue); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f17")] + public partial class IAvnString : SharpGen.Runtime.ComObject + { + public IAvnString(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnString(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnString(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnString::Pointer([Out] void** retOut) + /// IAvnString::Pointer + public unsafe System.IntPtr Pointer() + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnString::Length([In] int* ret) + /// IAvnString::Length + public unsafe System.Int32 Length() + { + System.Int32 ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + return ret; + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f20")] + public partial class IAvnStringArray : SharpGen.Runtime.ComObject + { + public IAvnStringArray(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnStringArray(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnStringArray(nativePtr); + /// + /// No documentation. + /// + /// GetCount + /// GetCount + public System.UInt32 Count + { + get => GetCount(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// unsigned int IAvnStringArray::GetCount() + /// IAvnStringArray::GetCount + internal unsafe System.UInt32 GetCount() + { + System.UInt32 __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallSystemUInt32(this._nativePointer, (*(void ***)this._nativePointer)[3]); + return __result__; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnStringArray::Get([In] unsigned int index,[In] IAvnString** ppv) + /// IAvnStringArray::Get + public unsafe Avalonia.Native.Interop.IAvnString Get(System.UInt32 index) + { + Avalonia.Native.Interop.IAvnString vOut; + System.IntPtr vOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, index, &vOut_, (*(void ***)this._nativePointer)[4]); + if (vOut_ != System.IntPtr.Zero) + vOut = new Avalonia.Native.Interop.IAvnString(vOut_); + else + vOut = null; + __result__.CheckError(); + return vOut; + } + } + + class IAvnSystemDialogEventsShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnSystemDialogEventsVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnSystemDialogEventsVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 1) + { + AddMethod(new OnCompletedDelegate(OnCompleted)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void OnCompletedDelegate(System.IntPtr thisObject, int arg0, void *arg1); + private static unsafe void OnCompleted(System.IntPtr thisObject, int param0, void *param1) + { + try + { + System.Int32 numResults = default (System.Int32); + numResults = (System.Int32)param0; + System.IntPtr trFirstResultRef = default (System.IntPtr); + trFirstResultRef = (System.IntPtr)param1; + IAvnSystemDialogEvents @this = (IAvnSystemDialogEvents)ToShadow(thisObject).Callback; + @this.OnCompleted(numResults, trFirstResultRef); + } + catch (System.Exception __exception__) + { + IAvnSystemDialogEvents @this = (IAvnSystemDialogEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnSystemDialogEventsShadow.IAvnSystemDialogEventsVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0c"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnSystemDialogEventsShadow))] + public partial interface IAvnSystemDialogEvents : SharpGen.Runtime.IUnknown + { + void OnCompleted(System.Int32 numResults, System.IntPtr trFirstResultRef); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f0d")] + public partial class IAvnSystemDialogs : SharpGen.Runtime.ComObject + { + public IAvnSystemDialogs(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnSystemDialogs(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnSystemDialogs(nativePtr); + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// void IAvnSystemDialogs::SelectFolderDialog([In] IAvnWindow* parentWindowHandle,[In] IAvnSystemDialogEvents* events,[In] const char* title,[In] const char* initialPath) + /// IAvnSystemDialogs::SelectFolderDialog + public unsafe void SelectFolderDialog(Avalonia.Native.Interop.IAvnWindow arentWindowHandleRef, Avalonia.Native.Interop.IAvnSystemDialogEvents events, System.String title, System.String initialPath) + { + System.IntPtr arentWindowHandleRef_ = System.IntPtr.Zero; + System.IntPtr events_ = System.IntPtr.Zero; + System.IntPtr title_; + System.IntPtr initialPath_; + arentWindowHandleRef_ = SharpGen.Runtime.CppObject.ToCallbackPtr(arentWindowHandleRef); + events_ = SharpGen.Runtime.CppObject.ToCallbackPtr(events); + title_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(title); + initialPath_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(initialPath); + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (void *)arentWindowHandleRef_, (void *)events_, (void *)title_, (void *)initialPath_, (*(void ***)this._nativePointer)[3]); + System.GC.KeepAlive(arentWindowHandleRef); + System.GC.KeepAlive(events); + System.Runtime.InteropServices.Marshal.FreeHGlobal(title_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(initialPath_); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// void IAvnSystemDialogs::OpenFileDialog([In] IAvnWindow* parentWindowHandle,[In] IAvnSystemDialogEvents* events,[In] bool allowMultiple,[In] const char* title,[In] const char* initialDirectory,[In] const char* initialFile,[In] const char* filters) + /// IAvnSystemDialogs::OpenFileDialog + public unsafe void OpenFileDialog(Avalonia.Native.Interop.IAvnWindow arentWindowHandleRef, Avalonia.Native.Interop.IAvnSystemDialogEvents events, System.Boolean allowMultiple, System.String title, System.String initialDirectory, System.String initialFile, System.String filters) + { + System.IntPtr arentWindowHandleRef_ = System.IntPtr.Zero; + System.IntPtr events_ = System.IntPtr.Zero; + System.Byte allowMultiple_; + System.IntPtr title_; + System.IntPtr initialDirectory_; + System.IntPtr initialFile_; + System.IntPtr filters_; + arentWindowHandleRef_ = SharpGen.Runtime.CppObject.ToCallbackPtr(arentWindowHandleRef); + events_ = SharpGen.Runtime.CppObject.ToCallbackPtr(events); + allowMultiple_ = (System.Byte)(allowMultiple ? 1 : 0); + title_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(title); + initialDirectory_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(initialDirectory); + initialFile_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(initialFile); + filters_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(filters); + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (void *)arentWindowHandleRef_, (void *)events_, allowMultiple_, (void *)title_, (void *)initialDirectory_, (void *)initialFile_, (void *)filters_, (*(void ***)this._nativePointer)[4]); + System.GC.KeepAlive(arentWindowHandleRef); + System.GC.KeepAlive(events); + System.Runtime.InteropServices.Marshal.FreeHGlobal(title_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(initialDirectory_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(initialFile_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(filters_); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// void IAvnSystemDialogs::SaveFileDialog([In] IAvnWindow* parentWindowHandle,[In] IAvnSystemDialogEvents* events,[In] const char* title,[In] const char* initialDirectory,[In] const char* initialFile,[In] const char* filters) + /// IAvnSystemDialogs::SaveFileDialog + public unsafe void SaveFileDialog(Avalonia.Native.Interop.IAvnWindow arentWindowHandleRef, Avalonia.Native.Interop.IAvnSystemDialogEvents events, System.String title, System.String initialDirectory, System.String initialFile, System.String filters) + { + System.IntPtr arentWindowHandleRef_ = System.IntPtr.Zero; + System.IntPtr events_ = System.IntPtr.Zero; + System.IntPtr title_; + System.IntPtr initialDirectory_; + System.IntPtr initialFile_; + System.IntPtr filters_; + arentWindowHandleRef_ = SharpGen.Runtime.CppObject.ToCallbackPtr(arentWindowHandleRef); + events_ = SharpGen.Runtime.CppObject.ToCallbackPtr(events); + title_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(title); + initialDirectory_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(initialDirectory); + initialFile_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(initialFile); + filters_ = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(filters); + Avalonia.Native.LocalInterop.CalliThisCallvoid(this._nativePointer, (void *)arentWindowHandleRef_, (void *)events_, (void *)title_, (void *)initialDirectory_, (void *)initialFile_, (void *)filters_, (*(void ***)this._nativePointer)[5]); + System.GC.KeepAlive(arentWindowHandleRef); + System.GC.KeepAlive(events); + System.Runtime.InteropServices.Marshal.FreeHGlobal(title_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(initialDirectory_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(initialFile_); + System.Runtime.InteropServices.Marshal.FreeHGlobal(filters_); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f04")] + public partial class IAvnWindow : Avalonia.Native.Interop.IAvnWindowBase + { + public IAvnWindow(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnWindow(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnWindow(nativePtr); + /// + /// No documentation. + /// + /// SetEnabled + /// SetEnabled + public System.Boolean Enabled + { + set => SetEnabled(value); + } + + /// + /// No documentation. + /// + /// SetParent + /// SetParent + public Avalonia.Native.Interop.IAvnWindow Parent + { + set => SetParent(value); + } + + /// + /// No documentation. + /// + /// SetCanResize + /// SetCanResize + public System.Boolean CanResize + { + set => SetCanResize(value); + } + + /// + /// No documentation. + /// + /// SetDecorations + /// SetDecorations + public Avalonia.Native.Interop.SystemDecorations Decorations + { + set => SetDecorations(value); + } + + /// + /// No documentation. + /// + /// SetTitle + /// SetTitle + public System.IntPtr Title + { + set => SetTitle(value); + } + + /// + /// No documentation. + /// + /// SetTitleBarColor + /// SetTitleBarColor + public Avalonia.Native.Interop.AvnColor TitleBarColor + { + set => SetTitleBarColor(value); + } + + /// + /// No documentation. + /// + /// SetExtendClientArea + /// SetExtendClientArea + public System.Boolean ExtendClientArea + { + set => SetExtendClientArea(value); + } + + /// + /// No documentation. + /// + /// SetExtendClientAreaHints + /// SetExtendClientAreaHints + public Avalonia.Native.Interop.AvnExtendClientAreaChromeHints ExtendClientAreaHints + { + set => SetExtendClientAreaHints(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetEnabled([In] bool enable) + /// IAvnWindow::SetEnabled + internal unsafe void SetEnabled(System.Boolean enable) + { + System.Byte enable_; + SharpGen.Runtime.Result __result__; + enable_ = (System.Byte)(enable ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, enable_, (*(void ***)this._nativePointer)[30]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetParent([In] IAvnWindow* parent) + /// IAvnWindow::SetParent + internal unsafe void SetParent(Avalonia.Native.Interop.IAvnWindow arentRef) + { + System.IntPtr arentRef_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + arentRef_ = SharpGen.Runtime.CppObject.ToCallbackPtr(arentRef); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)arentRef_, (*(void ***)this._nativePointer)[31]); + System.GC.KeepAlive(arentRef); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetCanResize([In] bool value) + /// IAvnWindow::SetCanResize + internal unsafe void SetCanResize(System.Boolean value) + { + System.Byte value_; + SharpGen.Runtime.Result __result__; + value_ = (System.Byte)(value ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, value_, (*(void ***)this._nativePointer)[32]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetDecorations([In] SystemDecorations value) + /// IAvnWindow::SetDecorations + internal unsafe void SetDecorations(Avalonia.Native.Interop.SystemDecorations value) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)value), (*(void ***)this._nativePointer)[33]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetTitle([In] void* utf8Title) + /// IAvnWindow::SetTitle + internal unsafe void SetTitle(System.IntPtr utf8Title) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)utf8Title, (*(void ***)this._nativePointer)[34]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetTitleBarColor([In] AvnColor color) + /// IAvnWindow::SetTitleBarColor + internal unsafe void SetTitleBarColor(Avalonia.Native.Interop.AvnColor color) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, color, (*(void ***)this._nativePointer)[35]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetWindowState([In] AvnWindowState state) + /// IAvnWindow::SetWindowState + public unsafe void SetWindowState(Avalonia.Native.Interop.AvnWindowState state) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)state), (*(void ***)this._nativePointer)[36]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindow::GetWindowState([In] AvnWindowState* ret) + /// IAvnWindow::GetWindowState + public unsafe Avalonia.Native.Interop.AvnWindowState GetWindowState() + { + Avalonia.Native.Interop.AvnWindowState ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[37]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindow::TakeFocusFromChildren() + /// IAvnWindow::TakeFocusFromChildren + public unsafe void TakeFocusFromChildren() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[38]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetExtendClientArea([In] bool enable) + /// IAvnWindow::SetExtendClientArea + internal unsafe void SetExtendClientArea(System.Boolean enable) + { + System.Byte enable_; + SharpGen.Runtime.Result __result__; + enable_ = (System.Byte)(enable ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, enable_, (*(void ***)this._nativePointer)[39]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetExtendClientAreaHints([In] AvnExtendClientAreaChromeHints hints) + /// IAvnWindow::SetExtendClientAreaHints + internal unsafe void SetExtendClientAreaHints(Avalonia.Native.Interop.AvnExtendClientAreaChromeHints hints) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)hints), (*(void ***)this._nativePointer)[40]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindow::GetExtendTitleBarHeight([In] double* ret) + /// IAvnWindow::GetExtendTitleBarHeight + public unsafe System.Double GetExtendTitleBarHeight() + { + System.Double ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[41]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindow::SetExtendTitleBarHeight([In] double value) + /// IAvnWindow::SetExtendTitleBarHeight + public unsafe void SetExtendTitleBarHeight(System.Double value) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, value, (*(void ***)this._nativePointer)[42]); + __result__.CheckError(); + } + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f02")] + public partial class IAvnWindowBase : SharpGen.Runtime.ComObject + { + public IAvnWindowBase(System.IntPtr nativePtr): base (nativePtr) + { + } + + public static explicit operator IAvnWindowBase(System.IntPtr nativePtr) => nativePtr == System.IntPtr.Zero ? null : new IAvnWindowBase(nativePtr); + /// + /// No documentation. + /// + /// SetTopMost + /// SetTopMost + public System.Boolean TopMost + { + set => SetTopMost(value); + } + + /// + /// No documentation. + /// + /// SetCursor + /// SetCursor + public Avalonia.Native.Interop.IAvnCursor Cursor + { + set => SetCursor(value); + } + + /// + /// No documentation. + /// + /// SetMainMenu + /// SetMainMenu + public Avalonia.Native.Interop.IAvnMenu MainMenu + { + set => SetMainMenu(value); + } + + /// + /// No documentation. + /// + /// SetBlurEnabled + /// SetBlurEnabled + public System.Boolean BlurEnabled + { + set => SetBlurEnabled(value); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::Show() + /// IAvnWindowBase::Show + public unsafe void Show() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[3]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::Hide() + /// IAvnWindowBase::Hide + public unsafe void Hide() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[4]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::Close() + /// IAvnWindowBase::Close + public unsafe void Close() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[5]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::Activate() + /// IAvnWindowBase::Activate + public unsafe void Activate() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[6]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::GetClientSize([In] AvnSize* ret) + /// IAvnWindowBase::GetClientSize + public unsafe Avalonia.Native.Interop.AvnSize GetClientSize() + { + Avalonia.Native.Interop.AvnSize ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[7]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::GetScaling([In] double* ret) + /// IAvnWindowBase::GetScaling + public unsafe System.Double GetScaling() + { + System.Double ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[8]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetMinMaxSize([In] AvnSize minSize,[In] AvnSize maxSize) + /// IAvnWindowBase::SetMinMaxSize + public unsafe void SetMinMaxSize(Avalonia.Native.Interop.AvnSize minSize, Avalonia.Native.Interop.AvnSize maxSize) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, minSize, maxSize, (*(void ***)this._nativePointer)[9]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::Resize([In] double width,[In] double height) + /// IAvnWindowBase::Resize + public unsafe void Resize(System.Double width, System.Double height) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, width, height, (*(void ***)this._nativePointer)[10]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::Invalidate([In] AvnRect rect) + /// IAvnWindowBase::Invalidate + public unsafe void Invalidate(Avalonia.Native.Interop.AvnRect rect) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, rect, (*(void ***)this._nativePointer)[11]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::BeginMoveDrag() + /// IAvnWindowBase::BeginMoveDrag + public unsafe void BeginMoveDrag() + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (*(void ***)this._nativePointer)[12]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::BeginResizeDrag([In] AvnWindowEdge edge) + /// IAvnWindowBase::BeginResizeDrag + public unsafe void BeginResizeDrag(Avalonia.Native.Interop.AvnWindowEdge edge) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, unchecked ((System.Int32)edge), (*(void ***)this._nativePointer)[13]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::GetPosition([In] AvnPoint* ret) + /// IAvnWindowBase::GetPosition + public unsafe Avalonia.Native.Interop.AvnPoint GetPosition() + { + Avalonia.Native.Interop.AvnPoint ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret, (*(void ***)this._nativePointer)[14]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetPosition([In] AvnPoint point) + /// IAvnWindowBase::SetPosition + public unsafe void SetPosition(Avalonia.Native.Interop.AvnPoint point) + { + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, point, (*(void ***)this._nativePointer)[15]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::PointToClient([In] AvnPoint point,[In] AvnPoint* ret) + /// IAvnWindowBase::PointToClient + public unsafe Avalonia.Native.Interop.AvnPoint PointToClient(Avalonia.Native.Interop.AvnPoint point) + { + Avalonia.Native.Interop.AvnPoint ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, point, &ret, (*(void ***)this._nativePointer)[16]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::PointToScreen([In] AvnPoint point,[In] AvnPoint* ret) + /// IAvnWindowBase::PointToScreen + public unsafe Avalonia.Native.Interop.AvnPoint PointToScreen(Avalonia.Native.Interop.AvnPoint point) + { + Avalonia.Native.Interop.AvnPoint ret; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, point, &ret, (*(void ***)this._nativePointer)[17]); + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::ThreadSafeSetSwRenderedFrame([In] AvnFramebuffer* fb,[In] IUnknown* dispose) + /// IAvnWindowBase::ThreadSafeSetSwRenderedFrame + public unsafe void ThreadSafeSetSwRenderedFrame(ref Avalonia.Native.Interop.AvnFramebuffer fb, SharpGen.Runtime.IUnknown dispose) + { + System.IntPtr dispose_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + dispose_ = SharpGen.Runtime.CppObject.ToCallbackPtr(dispose); + fixed (void *fb_ = &fb) + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, fb_, (void *)dispose_, (*(void ***)this._nativePointer)[18]); + System.GC.KeepAlive(dispose); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetTopMost([In] bool value) + /// IAvnWindowBase::SetTopMost + internal unsafe void SetTopMost(System.Boolean value) + { + System.Byte value_; + SharpGen.Runtime.Result __result__; + value_ = (System.Byte)(value ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, value_, (*(void ***)this._nativePointer)[19]); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetCursor([In] IAvnCursor* cursor) + /// IAvnWindowBase::SetCursor + internal unsafe void SetCursor(Avalonia.Native.Interop.IAvnCursor cursor) + { + System.IntPtr cursor_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + cursor_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cursor); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)cursor_, (*(void ***)this._nativePointer)[20]); + System.GC.KeepAlive(cursor); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::CreateGlRenderTarget([In] IAvnGlSurfaceRenderTarget** ret) + /// IAvnWindowBase::CreateGlRenderTarget + public unsafe Avalonia.Native.Interop.IAvnGlSurfaceRenderTarget CreateGlRenderTarget() + { + Avalonia.Native.Interop.IAvnGlSurfaceRenderTarget ret; + System.IntPtr ret_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &ret_, (*(void ***)this._nativePointer)[21]); + if (ret_ != System.IntPtr.Zero) + ret = new Avalonia.Native.Interop.IAvnGlSurfaceRenderTarget(ret_); + else + ret = null; + __result__.CheckError(); + return ret; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetMainMenu([In] IAvnMenu* menu) + /// IAvnWindowBase::SetMainMenu + internal unsafe void SetMainMenu(Avalonia.Native.Interop.IAvnMenu menu) + { + System.IntPtr menu_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + menu_ = SharpGen.Runtime.CppObject.ToCallbackPtr(menu); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, (void *)menu_, (*(void ***)this._nativePointer)[22]); + System.GC.KeepAlive(menu); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::ObtainNSWindowHandle([Out] void** retOut) + /// IAvnWindowBase::ObtainNSWindowHandle + public unsafe System.IntPtr ObtainNSWindowHandle() + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut, (*(void ***)this._nativePointer)[23]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::ObtainNSWindowHandleRetained([Out] void** retOut) + /// IAvnWindowBase::ObtainNSWindowHandleRetained + public unsafe System.IntPtr ObtainNSWindowHandleRetained() + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut, (*(void ***)this._nativePointer)[24]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::ObtainNSViewHandle([Out] void** retOut) + /// IAvnWindowBase::ObtainNSViewHandle + public unsafe System.IntPtr ObtainNSViewHandle() + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut, (*(void ***)this._nativePointer)[25]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::ObtainNSViewHandleRetained([Out] void** retOut) + /// IAvnWindowBase::ObtainNSViewHandleRetained + public unsafe System.IntPtr ObtainNSViewHandleRetained() + { + System.IntPtr retOut; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut, (*(void ***)this._nativePointer)[26]); + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// HRESULT IAvnWindowBase::CreateNativeControlHost([Out] IAvnNativeControlHost** retOut) + /// IAvnWindowBase::CreateNativeControlHost + public unsafe Avalonia.Native.Interop.IAvnNativeControlHost CreateNativeControlHost() + { + Avalonia.Native.Interop.IAvnNativeControlHost retOut; + System.IntPtr retOut_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, &retOut_, (*(void ***)this._nativePointer)[27]); + if (retOut_ != System.IntPtr.Zero) + retOut = new Avalonia.Native.Interop.IAvnNativeControlHost(retOut_); + else + retOut = null; + __result__.CheckError(); + return retOut; + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::BeginDragAndDropOperation([In] AvnDragDropEffects effects,[In] AvnPoint point,[In] IAvnClipboard* clipboard,[In] IAvnDndResultCallback* cb,[In] void* sourceHandle) + /// IAvnWindowBase::BeginDragAndDropOperation + public unsafe void BeginDragAndDropOperation(Avalonia.Native.Interop.AvnDragDropEffects effects, Avalonia.Native.Interop.AvnPoint point, Avalonia.Native.Interop.IAvnClipboard clipboard, Avalonia.Native.Interop.IAvnDndResultCallback cb, System.IntPtr sourceHandle) + { + System.IntPtr clipboard_ = System.IntPtr.Zero; + System.IntPtr cb_ = System.IntPtr.Zero; + SharpGen.Runtime.Result __result__; + clipboard_ = SharpGen.Runtime.CppObject.ToCallbackPtr(clipboard); + cb_ = SharpGen.Runtime.CppObject.ToCallbackPtr(cb); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint0(this._nativePointer, unchecked ((System.Int32)effects), point, (void *)clipboard_, (void *)cb_, (void *)sourceHandle, (*(void ***)this._nativePointer)[28]); + System.GC.KeepAlive(clipboard); + System.GC.KeepAlive(cb); + __result__.CheckError(); + } + + /// + /// No documentation. + /// + /// No documentation. + /// No documentation. + /// HRESULT IAvnWindowBase::SetBlurEnabled([In] bool enable) + /// IAvnWindowBase::SetBlurEnabled + internal unsafe void SetBlurEnabled(System.Boolean enable) + { + System.Byte enable_; + SharpGen.Runtime.Result __result__; + enable_ = (System.Byte)(enable ? 1 : 0); + __result__ = Avalonia.Native.LocalInterop.CalliThisCallint(this._nativePointer, enable_, (*(void ***)this._nativePointer)[29]); + __result__.CheckError(); + } + } + + class IAvnWindowBaseEventsShadow : SharpGen.Runtime.ComObjectShadow + { + protected unsafe class IAvnWindowBaseEventsVtbl : SharpGen.Runtime.ComObjectShadow.ComObjectVtbl + { + public IAvnWindowBaseEventsVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 13) + { + AddMethod(new PaintDelegate(Paint)); + AddMethod(new ClosedDelegate(Closed)); + AddMethod(new ActivatedDelegate(Activated)); + AddMethod(new DeactivatedDelegate(Deactivated)); + AddMethod(new ResizedDelegate(Resized)); + AddMethod(new PositionChangedDelegate(PositionChanged)); + AddMethod(new RawMouseEventDelegate(RawMouseEvent)); + AddMethod(new RawKeyEventDelegate(RawKeyEvent)); + AddMethod(new RawTextInputEventDelegate(RawTextInputEvent)); + AddMethod(new ScalingChangedDelegate(ScalingChanged)); + AddMethod(new RunRenderPriorityJobsDelegate(RunRenderPriorityJobs)); + AddMethod(new LostFocusDelegate(LostFocus)); + AddMethod(new DragEventDelegate(DragEvent)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate int PaintDelegate(System.IntPtr thisObject); + private static unsafe int Paint(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.Paint(); + return SharpGen.Runtime.Result.Ok.Code; + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return SharpGen.Runtime.Result.GetResultFromException(__exception__).Code; + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void ClosedDelegate(System.IntPtr thisObject); + private static unsafe void Closed(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.Closed(); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void ActivatedDelegate(System.IntPtr thisObject); + private static unsafe void Activated(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.Activated(); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void DeactivatedDelegate(System.IntPtr thisObject); + private static unsafe void Deactivated(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.Deactivated(); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void ResizedDelegate(System.IntPtr thisObject, void *arg0); + private static unsafe void Resized(System.IntPtr thisObject, void *param0) + { + try + { + Avalonia.Native.Interop.AvnSize size = System.Runtime.CompilerServices.Unsafe.AsRef(param0); + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.Resized(size); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void PositionChangedDelegate(System.IntPtr thisObject, Avalonia.Native.Interop.AvnPoint arg0); + private static unsafe void PositionChanged(System.IntPtr thisObject, Avalonia.Native.Interop.AvnPoint param0) + { + try + { + Avalonia.Native.Interop.AvnPoint position = default (Avalonia.Native.Interop.AvnPoint); + position = (Avalonia.Native.Interop.AvnPoint)param0; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.PositionChanged(position); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void RawMouseEventDelegate(System.IntPtr thisObject, int arg0, System.UInt32 arg1, int arg2, Avalonia.Native.Interop.AvnPoint arg3, Avalonia.Native.Interop.AvnVector arg4); + private static unsafe void RawMouseEvent(System.IntPtr thisObject, int param0, System.UInt32 param1, int param2, Avalonia.Native.Interop.AvnPoint param3, Avalonia.Native.Interop.AvnVector param4) + { + try + { + Avalonia.Native.Interop.AvnRawMouseEventType type = default (Avalonia.Native.Interop.AvnRawMouseEventType); + type = (Avalonia.Native.Interop.AvnRawMouseEventType)param0; + System.UInt32 timeStamp = default (System.UInt32); + timeStamp = (System.UInt32)param1; + Avalonia.Native.Interop.AvnInputModifiers modifiers = default (Avalonia.Native.Interop.AvnInputModifiers); + modifiers = (Avalonia.Native.Interop.AvnInputModifiers)param2; + Avalonia.Native.Interop.AvnPoint point = default (Avalonia.Native.Interop.AvnPoint); + point = (Avalonia.Native.Interop.AvnPoint)param3; + Avalonia.Native.Interop.AvnVector delta = default (Avalonia.Native.Interop.AvnVector); + delta = (Avalonia.Native.Interop.AvnVector)param4; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.RawMouseEvent(type, timeStamp, modifiers, point, delta); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate System.Byte RawKeyEventDelegate(System.IntPtr thisObject, int arg0, System.UInt32 arg1, int arg2, System.UInt32 arg3); + private static unsafe System.Byte RawKeyEvent(System.IntPtr thisObject, int param0, System.UInt32 param1, int param2, System.UInt32 param3) + { + try + { + System.Boolean __result__ = default (System.Boolean); + System.Byte __result__native; + Avalonia.Native.Interop.AvnRawKeyEventType type = default (Avalonia.Native.Interop.AvnRawKeyEventType); + type = (Avalonia.Native.Interop.AvnRawKeyEventType)param0; + System.UInt32 timeStamp = default (System.UInt32); + timeStamp = (System.UInt32)param1; + Avalonia.Native.Interop.AvnInputModifiers modifiers = default (Avalonia.Native.Interop.AvnInputModifiers); + modifiers = (Avalonia.Native.Interop.AvnInputModifiers)param2; + System.UInt32 key = default (System.UInt32); + key = (System.UInt32)param3; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + __result__ = @this.RawKeyEvent(type, timeStamp, modifiers, key); + __result__native = (System.Byte)(__result__ ? 1 : 0); + return __result__native; + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return default (System.Byte); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate System.Byte RawTextInputEventDelegate(System.IntPtr thisObject, System.UInt32 arg0, void *arg1); + private static unsafe System.Byte RawTextInputEvent(System.IntPtr thisObject, System.UInt32 param0, void *param1) + { + try + { + System.Boolean __result__ = default (System.Boolean); + System.Byte __result__native; + System.UInt32 timeStamp = default (System.UInt32); + timeStamp = (System.UInt32)param0; + System.String text = default (System.String); + System.IntPtr text_ = (System.IntPtr)param1; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + text = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(text_); + __result__ = @this.RawTextInputEvent(timeStamp, text); + __result__native = (System.Byte)(__result__ ? 1 : 0); + return __result__native; + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return default (System.Byte); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void ScalingChangedDelegate(System.IntPtr thisObject, double arg0); + private static unsafe void ScalingChanged(System.IntPtr thisObject, double param0) + { + try + { + System.Double scaling = default (System.Double); + scaling = (System.Double)param0; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.ScalingChanged(scaling); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void RunRenderPriorityJobsDelegate(System.IntPtr thisObject); + private static unsafe void RunRenderPriorityJobs(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.RunRenderPriorityJobs(); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void LostFocusDelegate(System.IntPtr thisObject); + private static unsafe void LostFocus(System.IntPtr thisObject) + { + try + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + @this.LostFocus(); + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate Avalonia.Native.Interop.AvnDragDropEffects DragEventDelegate(System.IntPtr thisObject, int arg0, Avalonia.Native.Interop.AvnPoint arg1, int arg2, int arg3, void *arg4, void *arg5); + private static unsafe Avalonia.Native.Interop.AvnDragDropEffects DragEvent(System.IntPtr thisObject, int param0, Avalonia.Native.Interop.AvnPoint param1, int param2, int param3, void *param4, void *param5) + { + try + { + Avalonia.Native.Interop.AvnDragDropEffects __result__ = default (Avalonia.Native.Interop.AvnDragDropEffects); + Avalonia.Native.Interop.AvnDragEventType type = default (Avalonia.Native.Interop.AvnDragEventType); + type = (Avalonia.Native.Interop.AvnDragEventType)param0; + Avalonia.Native.Interop.AvnPoint position = default (Avalonia.Native.Interop.AvnPoint); + position = (Avalonia.Native.Interop.AvnPoint)param1; + Avalonia.Native.Interop.AvnInputModifiers modifiers = default (Avalonia.Native.Interop.AvnInputModifiers); + modifiers = (Avalonia.Native.Interop.AvnInputModifiers)param2; + Avalonia.Native.Interop.AvnDragDropEffects effects = default (Avalonia.Native.Interop.AvnDragDropEffects); + effects = (Avalonia.Native.Interop.AvnDragDropEffects)param3; + Avalonia.Native.Interop.IAvnClipboard clipboard = default (Avalonia.Native.Interop.IAvnClipboard); + System.IntPtr clipboard_ = (System.IntPtr)param4; + System.IntPtr dataObjectHandle = default (System.IntPtr); + dataObjectHandle = (System.IntPtr)param5; + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + if (clipboard_ != System.IntPtr.Zero) + clipboard = new Avalonia.Native.Interop.IAvnClipboard(clipboard_); + else + clipboard = null; + __result__ = @this.DragEvent(type, position, modifiers, effects, clipboard, dataObjectHandle); + return __result__; + } + catch (System.Exception __exception__) + { + IAvnWindowBaseEvents @this = (IAvnWindowBaseEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return default (Avalonia.Native.Interop.AvnDragDropEffects); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnWindowBaseEventsShadow.IAvnWindowBaseEventsVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f05"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnWindowBaseEventsShadow))] + public partial interface IAvnWindowBaseEvents : SharpGen.Runtime.IUnknown + { + void Paint(); + void Closed(); + void Activated(); + void Deactivated(); + void Resized(Avalonia.Native.Interop.AvnSize size); + void PositionChanged(Avalonia.Native.Interop.AvnPoint position); + void RawMouseEvent(Avalonia.Native.Interop.AvnRawMouseEventType type, System.UInt32 timeStamp, Avalonia.Native.Interop.AvnInputModifiers modifiers, Avalonia.Native.Interop.AvnPoint point, Avalonia.Native.Interop.AvnVector delta); + System.Boolean RawKeyEvent(Avalonia.Native.Interop.AvnRawKeyEventType type, System.UInt32 timeStamp, Avalonia.Native.Interop.AvnInputModifiers modifiers, System.UInt32 key); + System.Boolean RawTextInputEvent(System.UInt32 timeStamp, System.String text); + void ScalingChanged(System.Double scaling); + void RunRenderPriorityJobs(); + void LostFocus(); + Avalonia.Native.Interop.AvnDragDropEffects DragEvent(Avalonia.Native.Interop.AvnDragEventType type, Avalonia.Native.Interop.AvnPoint position, Avalonia.Native.Interop.AvnInputModifiers modifiers, Avalonia.Native.Interop.AvnDragDropEffects effects, Avalonia.Native.Interop.IAvnClipboard clipboard, System.IntPtr dataObjectHandle); + } + + class IAvnWindowEventsShadow : Avalonia.Native.Interop.IAvnWindowBaseEventsShadow + { + protected unsafe class IAvnWindowEventsVtbl : Avalonia.Native.Interop.IAvnWindowBaseEventsShadow.IAvnWindowBaseEventsVtbl + { + public IAvnWindowEventsVtbl(int numberOfCallbackMethods): base (numberOfCallbackMethods + 3) + { + AddMethod(new ClosingDelegate(Closing)); + AddMethod(new WindowStateChangedDelegate(WindowStateChanged)); + AddMethod(new GotInputWhenDisabledDelegate(GotInputWhenDisabled)); + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate System.Byte ClosingDelegate(System.IntPtr thisObject); + private static unsafe System.Byte Closing(System.IntPtr thisObject) + { + try + { + System.Boolean __result__ = default (System.Boolean); + System.Byte __result__native; + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + __result__ = @this.Closing(); + __result__native = (System.Byte)(__result__ ? 1 : 0); + return __result__native; + } + catch (System.Exception __exception__) + { + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + return default (System.Byte); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void WindowStateChangedDelegate(System.IntPtr thisObject, int arg0); + private static unsafe void WindowStateChanged(System.IntPtr thisObject, int param0) + { + try + { + Avalonia.Native.Interop.AvnWindowState state = default (Avalonia.Native.Interop.AvnWindowState); + state = (Avalonia.Native.Interop.AvnWindowState)param0; + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + @this.WindowStateChanged(state); + } + catch (System.Exception __exception__) + { + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + + [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.ThisCall)] + private delegate void GotInputWhenDisabledDelegate(System.IntPtr thisObject); + private static unsafe void GotInputWhenDisabled(System.IntPtr thisObject) + { + try + { + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + @this.GotInputWhenDisabled(); + } + catch (System.Exception __exception__) + { + IAvnWindowEvents @this = (IAvnWindowEvents)ToShadow(thisObject).Callback; + (@this as SharpGen.Runtime.IExceptionCallback)?.RaiseException(__exception__); + } + } + } + + protected override SharpGen.Runtime.CppObjectVtbl Vtbl + { + get; + } + + = new Avalonia.Native.Interop.IAvnWindowEventsShadow.IAvnWindowEventsVtbl(0); + } + + [System.Runtime.InteropServices.GuidAttribute("2e2cda0a-9ae5-4f1b-8e20-081a04279f06"), SharpGen.Runtime.ShadowAttribute(typeof (Avalonia.Native.Interop.IAvnWindowEventsShadow))] + public partial interface IAvnWindowEvents : Avalonia.Native.Interop.IAvnWindowBaseEvents + { + System.Boolean Closing(); + void WindowStateChanged(Avalonia.Native.Interop.AvnWindowState state); + void GotInputWhenDisabled(); + } +} \ No newline at end of file diff --git a/src/Avalonia.Native/Generated/LocalInterop.cs b/src/Avalonia.Native/Generated/LocalInterop.cs new file mode 100644 index 0000000000..41e69a6bdc --- /dev/null +++ b/src/Avalonia.Native/Generated/LocalInterop.cs @@ -0,0 +1,202 @@ +// + +namespace Avalonia.Native +{ + internal static partial class LocalInterop + { + public static unsafe int CalliThisCallint(void *thisObject, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid0(void *thisObject, int param0, System.UInt32 param1, int param2, Avalonia.Native.Interop.AvnPoint param3, Avalonia.Native.Interop.AvnVector param4, void *methodPtr) + { + throw null; + } + + public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, int param0, System.UInt32 param1, int param2, System.UInt32 param3, void *methodPtr) + { + throw null; + } + + public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, System.UInt32 param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, double param0, void *methodPtr) + { + throw null; + } + + public static unsafe Avalonia.Native.Interop.AvnDragDropEffects CalliThisCallAvaloniaNativeInteropAvnDragDropEffects0(void *thisObject, int param0, Avalonia.Native.Interop.AvnPoint param1, int param2, int param3, void *param4, void *param5, void *methodPtr) + { + throw null; + } + + public static unsafe System.Byte CalliThisCallSystemByte(void *thisObject, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, int param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnSize param0, Avalonia.Native.Interop.AvnSize param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, double param0, double param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnRect param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, int param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnPoint param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, System.Byte param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, int param0, Avalonia.Native.Interop.AvnPoint param1, void *param2, void *param3, void *param4, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint0(void *thisObject, Avalonia.Native.Interop.AvnColor param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, double param0, void *methodPtr) + { + throw null; + } + + public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, void *methodPtr) + { + throw null; + } + + public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, int param0, int param1, void *param2, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, int param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, void *param2, void *param3, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, System.Byte param2, void *param3, void *param4, void *param5, void *param6, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, void *param0, void *param1, void *param2, void *param3, void *param4, void *param5, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, int param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, int param2, void *methodPtr) + { + throw null; + } + + public static unsafe System.IntPtr CalliThisCallSystemIntPtr(void *thisObject, void *param0, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, void *param0, int param1, void *methodPtr) + { + throw null; + } + + public static unsafe System.UInt32 CalliThisCallSystemUInt32(void *thisObject, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, System.UInt32 param0, void *param1, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, float param0, float param1, float param2, float param3, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, float param0, float param1, void *methodPtr) + { + throw null; + } + + public static unsafe int CalliThisCallint(void *thisObject, void *param0, void *param1, void *param2, void *methodPtr) + { + throw null; + } + + public static unsafe void CalliThisCallvoid(void *thisObject, int param0, System.Byte param1, void *methodPtr) + { + throw null; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Native/Generated/Structures.cs b/src/Avalonia.Native/Generated/Structures.cs new file mode 100644 index 0000000000..fc871a2516 --- /dev/null +++ b/src/Avalonia.Native/Generated/Structures.cs @@ -0,0 +1,246 @@ +// + +namespace Avalonia.Native.Interop +{ + /// + /// No documentation. + /// + /// AvnColor + /// AvnColor + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnColor + { + /// + /// No documentation. + /// + /// Alpha + /// Alpha + public System.Byte Alpha; + /// + /// No documentation. + /// + /// Red + /// Red + public System.Byte Red; + /// + /// No documentation. + /// + /// Green + /// Green + public System.Byte Green; + /// + /// No documentation. + /// + /// Blue + /// Blue + public System.Byte Blue; + } + + /// + /// No documentation. + /// + /// AvnFramebuffer + /// AvnFramebuffer + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnFramebuffer + { + /// + /// No documentation. + /// + /// Data + /// Data + public System.IntPtr Data; + /// + /// No documentation. + /// + /// Width + /// Width + public System.Int32 Width; + /// + /// No documentation. + /// + /// Height + /// Height + public System.Int32 Height; + /// + /// No documentation. + /// + /// Stride + /// Stride + public System.Int32 Stride; + /// + /// No documentation. + /// + /// Dpi + /// Dpi + public Avalonia.Native.Interop.AvnVector Dpi; + /// + /// No documentation. + /// + /// PixelFormat + /// PixelFormat + public Avalonia.Native.Interop.AvnPixelFormat PixelFormat; + } + + /// + /// No documentation. + /// + /// AvnPixelSize + /// AvnPixelSize + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnPixelSize + { + /// + /// No documentation. + /// + /// Width + /// Width + public System.Int32 Width; + /// + /// No documentation. + /// + /// Height + /// Height + public System.Int32 Height; + } + + /// + /// No documentation. + /// + /// AvnPoint + /// AvnPoint + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnPoint + { + /// + /// No documentation. + /// + /// X + /// X + public System.Double X; + /// + /// No documentation. + /// + /// Y + /// Y + public System.Double Y; + } + + /// + /// No documentation. + /// + /// AvnRect + /// AvnRect + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnRect + { + /// + /// No documentation. + /// + /// X + /// X + public System.Double X; + /// + /// No documentation. + /// + /// Y + /// Y + public System.Double Y; + /// + /// No documentation. + /// + /// Width + /// Width + public System.Double Width; + /// + /// No documentation. + /// + /// Height + /// Height + public System.Double Height; + } + + /// + /// No documentation. + /// + /// AvnScreen + /// AvnScreen + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnScreen + { + /// + /// No documentation. + /// + /// Bounds + /// Bounds + public Avalonia.Native.Interop.AvnRect Bounds; + /// + /// No documentation. + /// + /// WorkingArea + /// WorkingArea + public Avalonia.Native.Interop.AvnRect WorkingArea; + /// + /// No documentation. + /// + /// PixelDensity + /// PixelDensity + public System.Single PixelDensity; + /// + /// No documentation. + /// + /// Primary + /// Primary + public bool Primary + { + get => 0 != _Primary; + set => _Primary = (System.Byte)(value ? 1 : 0); + } + + internal System.Byte _Primary; + } + + /// + /// No documentation. + /// + /// AvnSize + /// AvnSize + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnSize + { + /// + /// No documentation. + /// + /// Width + /// Width + public System.Double Width; + /// + /// No documentation. + /// + /// Height + /// Height + public System.Double Height; + } + + /// + /// No documentation. + /// + /// AvnVector + /// AvnVector + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 0, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] + public partial struct AvnVector + { + /// + /// No documentation. + /// + /// X + /// X + public System.Double X; + /// + /// No documentation. + /// + /// Y + /// Y + public System.Double Y; + } +} \ No newline at end of file diff --git a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs deleted file mode 100644 index 8aa9b1a122..0000000000 --- a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Avalonia.Controls.Primitives.PopupPositioning; -using Avalonia.Platform; - -namespace Avalonia.Native -{ - class OsxManagedPopupPositionerPopupImplHelper : ManagedPopupPositionerPopupImplHelper - { - public OsxManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize) : base(parent, moveResize) - { - - } - - public override double Scaling => 1; - } -} diff --git a/src/Avalonia.Native/OsxUnicodeKeys.cs b/src/Avalonia.Native/OsxUnicodeKeys.cs index b4056c8cd8..fd9acfa714 100644 --- a/src/Avalonia.Native/OsxUnicodeKeys.cs +++ b/src/Avalonia.Native/OsxUnicodeKeys.cs @@ -140,6 +140,11 @@ namespace Avalonia.Native.Interop } else { + if (key >= Key.D0 && key <= Key.D9) + { + return key.ToString().Replace("D", ""); + } + return key.ToString().ToLower(); } } diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index b0da5fdc43..2d246e08d2 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -26,7 +26,7 @@ namespace Avalonia.Native var context = _opts.UseGpu ? glFeature?.DeferredContext : null; Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context); } - PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize)); + PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); } private void MoveResize(PixelPoint position, Size size, double scaling) diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index 0d593bc2f6..f28be52dd5 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -20,21 +20,26 @@ namespace Avalonia.Native { get { - var count = ScreenCount; - var result = new Screen[count]; - - for(int i = 0; i < count; i++) + if (_native != null) { - var screen = _native.GetScreen(i); + var count = ScreenCount; + var result = new Screen[count]; + + for (int i = 0; i < count; i++) + { + var screen = _native.GetScreen(i); + + result[i] = new Screen( + screen.PixelDensity, + screen.Bounds.ToAvaloniaPixelRect(), + screen.WorkingArea.ToAvaloniaPixelRect(), + screen.Primary); + } - result[i] = new Screen( - screen.PixelDensity, - screen.Bounds.ToAvaloniaPixelRect(), - screen.WorkingArea.ToAvaloniaPixelRect(), - screen.Primary); + return result; } - return result; + return Array.Empty(); } } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index f916e95d7c..56cf544d9d 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -81,7 +81,7 @@ namespace Avalonia.Native _glSurface = new GlPlatformSurface(window, _glContext); Screen = new ScreenImpl(screens); _savedLogicalSize = ClientSize; - _savedScaling = Scaling; + _savedScaling = RenderScaling; _nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost()); var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity) @@ -94,8 +94,13 @@ namespace Avalonia.Native { get { - var s = _native.GetClientSize(); - return new Size(s.Width, s.Height); + if (_native != null) + { + var s = _native.GetClientSize(); + return new Size(s.Width, s.Height); + } + + return default; } } @@ -144,7 +149,6 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Closed() { var n = _parent._native; - _parent._native = null; try { _parent?.Closed?.Invoke(); @@ -153,6 +157,7 @@ namespace Avalonia.Native { n?.Dispose(); } + _parent._mouse.Dispose(); } @@ -300,7 +305,15 @@ namespace Avalonia.Native public IRenderer CreateRenderer(IRenderRoot root) { if (_deferredRendering) - return new DeferredRenderer(root, AvaloniaLocator.Current.GetService()); + { + var loop = AvaloniaLocator.Current.GetService(); + var customRendererFactory = AvaloniaLocator.Current.GetService(); + + if (customRendererFactory != null) + return customRendererFactory.Create(root, loop); + return new DeferredRenderer(root, loop); + } + return new ImmediateRenderer(root); } @@ -343,12 +356,12 @@ namespace Avalonia.Native public Point PointToClient(PixelPoint point) { - return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint(); + return _native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default; } public PixelPoint PointToScreen(Point point) { - return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint(); + return _native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default; } public void Hide() @@ -369,7 +382,9 @@ namespace Avalonia.Native _native.SetTopMost(value); } - public double Scaling => _native?.GetScaling() ?? 1; + public double RenderScaling => _native?.GetScaling() ?? 1; + + public double DesktopScaling => 1; public Action Deactivated { get; set; } public Action Activated { get; set; } @@ -432,7 +447,7 @@ namespace Avalonia.Native TransparencyLevel = transparencyLevel; - _native.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur); + _native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur); TransparencyLevelChanged?.Invoke(TransparencyLevel); } } diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 72132eed93..d761e60c07 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -9,6 +9,5 @@ - - + diff --git a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs index 4881c77034..fe4e9dd94a 100644 --- a/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs +++ b/src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs @@ -48,6 +48,10 @@ namespace Avalonia.ReactiveUI if (itemsControl.ItemTemplate != null) return true; + if (itemsControl.DataTemplates != null && + itemsControl.DataTemplates.Count > 0) + return true; + itemsControl.ItemTemplate = DefaultItemTemplate; return true; } diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 64145b9c3c..d01a495108 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -8,4 +8,5 @@ + diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs index 1af8012a20..421633cd58 100644 --- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs +++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs @@ -65,8 +65,15 @@ namespace Avalonia.ReactiveUI { this.WhenActivated(disposables => { - this.WhenAnyObservable(x => x.Router.CurrentViewModel) - .DistinctUntilChanged() + var routerRemoved = this + .WhenAnyValue(x => x.Router) + .Where(router => router == null) + .Cast(); + + this.WhenAnyValue(x => x.Router) + .Where(router => router != null) + .SelectMany(router => router.CurrentViewModel) + .Merge(routerRemoved) .Subscribe(NavigateToViewModel) .DisposeWith(disposables); }); @@ -92,6 +99,13 @@ namespace Avalonia.ReactiveUI /// ViewModel to which the user navigates. private void NavigateToViewModel(object viewModel) { + if (Router == null) + { + this.Log().Warn("Router property is null. Falling back to default content."); + Content = DefaultContent; + return; + } + if (viewModel == null) { this.Log().Info("ViewModel is null. Falling back to default content."); @@ -110,8 +124,8 @@ namespace Avalonia.ReactiveUI this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}."); viewInstance.ViewModel = viewModel; - if (viewInstance is IStyledElement styled) - styled.DataContext = viewModel; + if (viewInstance is IDataContextProvider provider) + provider.DataContext = viewModel; Content = viewInstance; } } diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 6684772bfe..6c241bbc75 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -4,8 +4,10 @@ AVALONIA_REMOTE_PROTOCOL;$(DefineConstants) true Key.snk + Avalonia.Remote.Protocol + \ No newline at end of file diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index b4f6c2c942..d9f0cef3da 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -8,4 +8,5 @@ + diff --git a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs index e177993d13..929f7142bb 100644 --- a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs +++ b/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs @@ -164,7 +164,7 @@ namespace Avalonia.Styling private void ConvertAndPublishNext(object? value) { - _value = value is T v ? v : BindingValue.FromUntyped(value); + _value = BindingValue.FromUntyped(value); if (_isActive) { diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml index ffe3e92202..44dfb9ea48 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml @@ -58,6 +58,11 @@ + + + + + 1,1,1,1 0.5 diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index c0e5f47eed..9ed3207235 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -61,6 +61,11 @@ + + + + + 1 0.5 diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml index 788b60892b..66d0f17ede 100644 --- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml +++ b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml @@ -19,7 +19,7 @@ MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{TemplateBinding MaxDropDownHeight}" PlacementTarget="{TemplateBinding}" - StaysOpen="False"> + IsLightDismissEnabled="True"> + diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/ComboBox.xaml index ae5b902ae8..cced76e850 100644 --- a/src/Avalonia.Themes.Default/ComboBox.xaml +++ b/src/Avalonia.Themes.Default/ComboBox.xaml @@ -1,4 +1,5 @@ - + @@ -24,6 +25,7 @@ + + + IsLightDismissEnabled="True"> diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/DatePicker.xaml new file mode 100644 index 0000000000..da878c88e2 --- /dev/null +++ b/src/Avalonia.Themes.Default/DatePicker.xaml @@ -0,0 +1,334 @@ + + + + + 0,0,0,4 + 40 + 40 + 41 + 296 + 456 + 0,3,0,6 + 9,3,0,6 + 0,3,0,6 + 9,3,0,6 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 97cff5d94b..e5b654b490 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -37,6 +37,7 @@ + @@ -54,4 +55,7 @@ + + + diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml index d63f785e77..08d8b4c995 100644 --- a/src/Avalonia.Themes.Default/Expander.xaml +++ b/src/Avalonia.Themes.Default/Expander.xaml @@ -86,7 +86,7 @@ - - diff --git a/src/Avalonia.Themes.Default/SplitView.xaml b/src/Avalonia.Themes.Default/SplitView.xaml new file mode 100644 index 0000000000..2fb59cdbc4 --- /dev/null +++ b/src/Avalonia.Themes.Default/SplitView.xaml @@ -0,0 +1,219 @@ + + + + 320 + 48 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml index 92482a564c..6e344ce58e 100644 --- a/src/Avalonia.Themes.Default/TabItem.xaml +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -2,7 +2,7 @@ - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/TimePicker.xaml b/src/Avalonia.Themes.Default/TimePicker.xaml new file mode 100644 index 0000000000..c76f900cfe --- /dev/null +++ b/src/Avalonia.Themes.Default/TimePicker.xaml @@ -0,0 +1,283 @@ + + + + + 40 + 1 + 1 + 0,0,0,4 + 40 + 41 + 242 + 456 + 0,3,0,6 + 0,3,0,6 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/TitleBar.xaml b/src/Avalonia.Themes.Default/TitleBar.xaml index 45798d3fa1..7f8ed24076 100644 --- a/src/Avalonia.Themes.Default/TitleBar.xaml +++ b/src/Avalonia.Themes.Default/TitleBar.xaml @@ -5,7 +5,7 @@ - - @@ -264,10 +289,6 @@ - - diff --git a/src/Avalonia.Themes.Default/Window.xaml b/src/Avalonia.Themes.Default/Window.xaml index 739887fb35..3b378dbcbe 100644 --- a/src/Avalonia.Themes.Default/Window.xaml +++ b/src/Avalonia.Themes.Default/Window.xaml @@ -1,23 +1,24 @@ - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/CalendarButton.xaml index 87082ef21e..ca538e4b0a 100644 --- a/src/Avalonia.Themes.Fluent/CalendarButton.xaml +++ b/src/Avalonia.Themes.Fluent/CalendarButton.xaml @@ -67,7 +67,7 @@ - + diff --git a/src/Avalonia.Themes.Fluent/DatePicker.xaml b/src/Avalonia.Themes.Fluent/DatePicker.xaml index 8b669a30ed..6fbfa1bbf7 100644 --- a/src/Avalonia.Themes.Fluent/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/DatePicker.xaml @@ -190,7 +190,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Expander.xaml b/src/Avalonia.Themes.Fluent/Expander.xaml index e9eb939798..97ca62c493 100644 --- a/src/Avalonia.Themes.Fluent/Expander.xaml +++ b/src/Avalonia.Themes.Fluent/Expander.xaml @@ -86,10 +86,10 @@ @@ -145,6 +148,8 @@ @@ -155,9 +160,9 @@ + IsLightDismissEnabled="True" + IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}" + OverlayInputPassThroughElement="{Binding $parent[Menu]}"> + + - + @@ -203,7 +213,9 @@ + - - + - - + - - - - @@ -101,12 +114,12 @@ - - @@ -115,12 +128,12 @@ - - @@ -129,23 +142,152 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/TimePicker.xaml b/src/Avalonia.Themes.Fluent/TimePicker.xaml index f82e194a73..84352213e1 100644 --- a/src/Avalonia.Themes.Fluent/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/TimePicker.xaml @@ -123,7 +123,7 @@ diff --git a/src/Avalonia.Themes.Fluent/TitleBar.xaml b/src/Avalonia.Themes.Fluent/TitleBar.xaml index 45798d3fa1..4dba5b4ba4 100644 --- a/src/Avalonia.Themes.Fluent/TitleBar.xaml +++ b/src/Avalonia.Themes.Fluent/TitleBar.xaml @@ -15,7 +15,7 @@ - + diff --git a/src/Avalonia.Themes.Fluent/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/ToggleButton.xaml index f2f07d3e2a..49e2280a6d 100644 --- a/src/Avalonia.Themes.Fluent/ToggleButton.xaml +++ b/src/Avalonia.Themes.Fluent/ToggleButton.xaml @@ -39,67 +39,67 @@ - - - - - - - - - - -