From 01a4ba5aa25bbad9074bd06f1301c16bdd36a9fe Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 20 Apr 2023 22:18:19 -0400 Subject: [PATCH] Add PreferFileDialogPolyfill option --- .../Avalonia.Browser/BrowserAppBuilder.cs | 7 +++++++ .../Avalonia.Browser/Interop/StorageHelper.cs | 6 +++--- .../Storage/BrowserStorageProvider.cs | 9 ++++++--- .../webapp/modules/storage/storageProvider.ts | 18 ++++++++++++------ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs index 9bb471005b..77e8b1a5f6 100644 --- a/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs +++ b/src/Browser/Avalonia.Browser/BrowserAppBuilder.cs @@ -11,6 +11,13 @@ public class BrowserPlatformOptions /// If null, default path resolved depending on the backend (browser or blazor) is used. /// public Func? FrameworkAssetPathResolver { get; set; } + + /// + /// Avalonia uses "native-file-system-adapter" polyfill for the file dialogs. + /// If native implementation is available, by default it is used. + /// This property forces polyfill to be always used. + /// + public bool PreferFileDialogPolyfill { get; set; } } public static class BrowserAppBuilder diff --git a/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs index c56023c0f7..d95d4405ba 100644 --- a/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs @@ -9,15 +9,15 @@ internal static partial class StorageHelper public static partial bool HasNativeFilePicker(); [JSImport("StorageProvider.selectFolderDialog", AvaloniaModule.StorageModuleName)] - public static partial Task SelectFolderDialog(JSObject? startIn); + public static partial Task SelectFolderDialog(JSObject? startIn, bool preferPolyfill); [JSImport("StorageProvider.openFileDialog", AvaloniaModule.StorageModuleName)] public static partial Task OpenFileDialog(JSObject? startIn, bool multiple, - [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption); + [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption, bool preferPolyfill); [JSImport("StorageProvider.saveFileDialog", AvaloniaModule.StorageModuleName)] public static partial Task SaveFileDialog(JSObject? startIn, string? suggestedName, - [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption); + [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption, bool preferPolyfill); [JSImport("StorageItem.createWellKnownDirectory", AvaloniaModule.StorageModuleName)] public static partial JSObject CreateWellKnownDirectory(string wellKnownDirectory); diff --git a/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs index e6849f2fcc..f1e90eaf3c 100644 --- a/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs +++ b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs @@ -20,6 +20,9 @@ internal class BrowserStorageProvider : IStorageProvider public bool CanSave => StorageHelper.HasNativeFilePicker(); public bool CanPickFolder => true; + private bool PreferPolyfill => + AvaloniaLocator.Current.GetService()?.PreferFileDialogPolyfill ?? false; + public async Task> OpenFilePickerAsync(FilePickerOpenOptions options) { await AvaloniaModule.ImportStorage(); @@ -29,7 +32,7 @@ internal class BrowserStorageProvider : IStorageProvider try { - using var items = await StorageHelper.OpenFileDialog(startIn, options.AllowMultiple, types, excludeAll); + using var items = await StorageHelper.OpenFileDialog(startIn, options.AllowMultiple, types, excludeAll, PreferPolyfill); if (items is null) { return Array.Empty(); @@ -63,7 +66,7 @@ internal class BrowserStorageProvider : IStorageProvider try { - var item = await StorageHelper.SaveFileDialog(startIn, options.SuggestedFileName, types, excludeAll); + var item = await StorageHelper.SaveFileDialog(startIn, options.SuggestedFileName, types, excludeAll, PreferPolyfill); return item is not null ? new JSStorageFile(item) : null; } catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal)) @@ -89,7 +92,7 @@ internal class BrowserStorageProvider : IStorageProvider try { - var item = await StorageHelper.SelectFolderDialog(startIn); + var item = await StorageHelper.SelectFolderDialog(startIn, PreferPolyfill); return item is not null ? new[] { new JSStorageFolder(item) } : Array.Empty(); } catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal)) diff --git a/src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts b/src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts index ce9e164735..8c79f225ee 100644 --- a/src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts +++ b/src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts @@ -12,10 +12,12 @@ declare global { export class StorageProvider { public static async selectFolderDialog( - startIn: StorageItem | null): Promise { + startIn: StorageItem | null, + preferPolyfill: boolean): Promise { // 'Picker' API doesn't accept "null" as a parameter, so it should be set to undefined. const options = { - startIn: (startIn?.wellKnownType ?? startIn?.handle ?? undefined) + startIn: (startIn?.wellKnownType ?? startIn?.handle ?? undefined), + _preferPolyfill: preferPolyfill }; const handle = await showDirectoryPicker(options as any); @@ -24,12 +26,14 @@ export class StorageProvider { public static async openFileDialog( startIn: StorageItem | null, multiple: boolean, - types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean): Promise { + types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean, + preferPolyfill: boolean): Promise { const options = { startIn: (startIn?.wellKnownType ?? startIn?.handle ?? undefined), multiple, excludeAcceptAllOption, - types: (types ?? undefined) + types: (types ?? undefined), + _preferPolyfill: preferPolyfill }; const handles = await showOpenFilePicker(options); @@ -38,12 +42,14 @@ export class StorageProvider { public static async saveFileDialog( startIn: StorageItem | null, suggestedName: string | null, - types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean): Promise { + types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean, + preferPolyfill: boolean): Promise { const options = { startIn: (startIn?.wellKnownType ?? startIn?.handle ?? undefined), suggestedName: (suggestedName ?? undefined), excludeAcceptAllOption, - types: (types ?? undefined) + types: (types ?? undefined), + _preferPolyfill: preferPolyfill }; const handle = await showSaveFilePicker(options);