From 2c540873ec2f2dc4feaa10be8aef7d8d183e1a16 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Mon, 27 Oct 2025 11:43:09 +0800 Subject: [PATCH] Add Impl for SaveFilePickerWithResultAsync API (#19783) * Add new SaveFilePickerWithResultAsync API to the base * Add SaveFilePickerWithResultAsync stub impl * Control Catalog SaveFilePickerWithResultAsync sample with XML and JSON types * Make SaveFilePickerResult a struct * Add managed picker implementation * Make SaveFilePickerResult a readonly struct * Windows implementation of SaveFilePickerWithResultAsync * Test impl for dbus * Reuse the file type object (FTO) so StorageFile consumer can match exactly the right FTO when receiving the SaveFilePicker's result. * Add Gtk impl * Avalonia.Native: surface selected save dialog filters * macOS: report selected NSSavePanel filter * Modify the conditional in case there's duplicate descriptions of FTO's in DBusSystemDialog.cs * Instantiate FPFT as fallback * Pass the mime/pattern to instantiated FPFT * Update API diff * Fix review comments --------- Co-authored-by: Max Katz --- api/Avalonia.nupkg.xml | 40 ++++++++++- .../src/OSX/StorageProvider.mm | 14 +++- samples/ControlCatalog/Pages/DialogsPage.xaml | 1 + .../ControlCatalog/Pages/DialogsPage.xaml.cs | 55 +++++++++++++-- .../Storage/AndroidStorageProvider.cs | 6 ++ .../Storage/FallbackStorageProvider.cs | 6 +- .../Storage/FileIO/BclStorageProvider.cs | 1 + .../Storage/FileIO/StorageProviderHelpers.cs | 3 +- .../Platform/Storage/FilePickerFileType.cs | 12 ++-- .../Platform/Storage/FilePickerFileTypes.cs | 14 ++++ .../Platform/Storage/IStorageProvider.cs | 6 ++ .../Platform/Storage/NoopStorageProvider.cs | 5 ++ .../Platform/Storage/SaveFilePickerResult.cs | 22 ++++++ src/Avalonia.DesignerSupport/Remote/Stubs.cs | 5 ++ .../ManagedFileChooserFilterViewModel.cs | 8 ++- .../Internal/ManagedFileChooserViewModel.cs | 6 +- .../ManagedStorageProvider.cs | 13 ++++ src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 68 +++++++++++++------ src/Avalonia.Native/StorageProviderApi.cs | 57 +++++++++++----- src/Avalonia.Native/StorageProviderImpl.cs | 11 ++- src/Avalonia.Native/avn.idl | 1 + .../NativeDialogs/GtkNativeFileDialogs.cs | 65 +++++++++++++----- .../Storage/BrowserStorageProvider.cs | 6 ++ .../Avalonia.Win32/Win32StorageProvider.cs | 60 +++++++++------- .../Storage/IOSStorageProvider.cs | 6 ++ 25 files changed, 389 insertions(+), 102 deletions(-) create mode 100644 src/Avalonia.Base/Platform/Storage/SaveFilePickerResult.cs diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 5d8cdee507..fbf22358ed 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,6 +1,24 @@ - + + + CP0002 + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/net6.0/Avalonia.Dialogs.dll + current/Avalonia/lib/net6.0/Avalonia.Dialogs.dll + + + CP0002 + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/net8.0/Avalonia.Dialogs.dll + current/Avalonia/lib/net8.0/Avalonia.Dialogs.dll + + + CP0002 + M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Dialogs.dll + CP0006 M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer) @@ -31,6 +49,12 @@ baseline/Avalonia/lib/net6.0/Avalonia.Base.dll current/Avalonia/lib/net6.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) + baseline/Avalonia/lib/net6.0/Avalonia.Base.dll + current/Avalonia/lib/net6.0/Avalonia.Base.dll + CP0006 M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) @@ -79,6 +103,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0006 M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) @@ -127,6 +157,12 @@ baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + + CP0006 + M:Avalonia.Platform.Storage.IStorageProvider.SaveFilePickerWithResultAsync(Avalonia.Platform.Storage.FilePickerSaveOptions) + baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll + CP0006 M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64) @@ -145,4 +181,4 @@ baseline/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll current/Avalonia/lib/netstandard2.0/Avalonia.OpenGL.dll - + \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/StorageProvider.mm b/native/Avalonia.Native/src/OSX/StorageProvider.mm index 4a82eeaea7..570281fdfd 100644 --- a/native/Avalonia.Native/src/OSX/StorageProvider.mm +++ b/native/Avalonia.Native/src/OSX/StorageProvider.mm @@ -320,12 +320,22 @@ public: } auto handler = ^(NSModalResponse result) { + int selectedIndex = -1; + if (panel.accessoryView != nil) + { + auto popup = [panel.accessoryView viewWithTag:kFileTypePopupTag]; + if ([popup isKindOfClass:[NSPopUpButton class]]) + { + selectedIndex = (int)[(NSPopUpButton*)popup indexOfSelectedItem]; + } + } + if(result == NSFileHandlingPanelOKButton) { auto url = [panel URL]; auto urls = [NSArray arrayWithObject:url]; auto uriStrings = CreateAvnStringArray(urls); - events->OnCompleted(uriStrings); + events->OnCompletedWithFilter(uriStrings, selectedIndex); [panel orderOut:panel]; @@ -338,7 +348,7 @@ public: return; } - events->OnCompleted(nullptr); + events->OnCompletedWithFilter(nullptr, selectedIndex); }; diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 60d991e324..75d98827c1 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -38,6 +38,7 @@ + diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 5b7814fb5d..7baa86e8b8 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -276,6 +276,48 @@ namespace ControlCatalog.Pages await SetPickerResult(file is null ? null : new[] { file }); }; + this.Get