Browse Source

Impl `SuggestedFileType` for Save/OpenFilePicker (#20026)

* new SuggestedFileType API

* Add impl for Win32 SuggestedFileType

* Impl suggestedFileType for GTK

* DBus impl

* Impl API for mac

* Add sample for suggested file filter

---------

Co-authored-by: Emmanuel Hansen <emmausssss@gmail.com>
pull/20055/head
Jumar Macato 11 months ago
committed by GitHub
parent
commit
80223ca7d4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      samples/ControlCatalog/Pages/DialogsPage.xaml
  2. 72
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  3. 9
      src/Avalonia.Base/Platform/Storage/FilePickerOpenOptions.cs
  4. 9
      src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs
  5. 31
      src/Avalonia.FreeDesktop/DBusSystemDialog.cs
  6. 20
      src/Avalonia.Native/StorageProviderApi.cs
  7. 2
      src/Avalonia.X11/NativeDialogs/Gtk.cs
  8. 16
      src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
  9. 17
      src/Windows/Avalonia.Win32/Win32StorageProvider.cs

6
samples/ControlCatalog/Pages/DialogsPage.xaml

@ -31,6 +31,12 @@
<ComboBoxItem>TXT mime only</ComboBoxItem> <ComboBoxItem>TXT mime only</ComboBoxItem>
<ComboBoxItem>TXT apple type id only</ComboBoxItem> <ComboBoxItem>TXT apple type id only</ComboBoxItem>
</ComboBox> </ComboBox>
<StackPanel Orientation="Horizontal" Spacing="8">
<CheckBox Name="UseSuggestedFilter">Use SuggestedFileType</CheckBox>
<ComboBox Name="SuggestedFilterSelector" MinWidth="160">
<ComboBoxItem>First filter</ComboBoxItem>
</ComboBox>
</StackPanel>
<Expander Header="FilePicker API"> <Expander Header="FilePicker API">
<StackPanel Spacing="4"> <StackPanel Spacing="4">
<CheckBox Name="ForceManaged">Force managed dialog</CheckBox> <CheckBox Name="ForceManaged">Force managed dialog</CheckBox>

72
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -37,6 +37,8 @@ namespace ControlCatalog.Pages
var openedFileContent = OpenedFileContent; var openedFileContent = OpenedFileContent;
var openMultiple = OpenMultiple; var openMultiple = OpenMultiple;
var currentFolderBox = CurrentFolderBox; var currentFolderBox = CurrentFolderBox;
var useSuggestedFilter = UseSuggestedFilter;
var suggestedFilterSelector = SuggestedFilterSelector;
currentFolderBox.TextChanged += async (sender, args) => currentFolderBox.TextChanged += async (sender, args) =>
{ {
@ -76,7 +78,7 @@ namespace ControlCatalog.Pages
}).ToList() ?? new List<FileDialogFilter>(); }).ToList() ?? new List<FileDialogFilter>();
} }
List<FilePickerFileType>? GetFileTypes() List<FilePickerFileType>? BuildFileTypes()
{ {
var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content
?? "None"; ?? "None";
@ -115,6 +117,64 @@ namespace ControlCatalog.Pages
}; };
} }
List<FilePickerFileType>? GetFileTypes()
{
var types = BuildFileTypes();
UpdateSuggestedFilterSelector(types);
return types;
}
void UpdateSuggestedFilterSelector(IReadOnlyList<FilePickerFileType>? types)
{
var previouslySelected = (suggestedFilterSelector.SelectedItem as ComboBoxItem)?.Tag as FilePickerFileType;
suggestedFilterSelector.Items.Clear();
suggestedFilterSelector.Items.Add(new ComboBoxItem { Content = "First filter", Tag = null });
var desiredIndex = 0;
if (types is { Count: > 0 })
{
for (var i = 0; i < types.Count; i++)
{
var type = types[i];
var item = new ComboBoxItem { Content = type.Name, Tag = type };
suggestedFilterSelector.Items.Add(item);
if (previouslySelected is not null && ReferenceEquals(previouslySelected, type))
{
desiredIndex = i + 1;
}
}
}
suggestedFilterSelector.SelectedIndex = desiredIndex;
}
FilePickerFileType? GetSuggestedFileType(IReadOnlyList<FilePickerFileType>? types)
{
if (useSuggestedFilter.IsChecked == true && types is { Count: > 0 })
{
if (suggestedFilterSelector.SelectedItem is ComboBoxItem { Tag: FilePickerFileType selectedType }
&& types.Any(t => ReferenceEquals(t, selectedType)))
{
return selectedType;
}
return types.FirstOrDefault();
}
return null;
}
void UpdateSuggestedFilterSelectorState() =>
suggestedFilterSelector.IsEnabled = useSuggestedFilter.IsChecked == true;
useSuggestedFilter.Checked += (_, _) => UpdateSuggestedFilterSelectorState();
useSuggestedFilter.Unchecked += (_, _) => UpdateSuggestedFilterSelectorState();
UpdateSuggestedFilterSelectorState();
FilterSelector.SelectionChanged += (_, _) => UpdateSuggestedFilterSelector(BuildFileTypes());
UpdateSuggestedFilterSelector(BuildFileTypes());
OpenFile.Click += async delegate OpenFile.Click += async delegate
{ {
// Almost guaranteed to exist // Almost guaranteed to exist
@ -229,10 +289,12 @@ namespace ControlCatalog.Pages
OpenFilePicker.Click += async delegate OpenFilePicker.Click += async delegate
{ {
var fileTypes = GetFileTypes();
var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions() var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions()
{ {
Title = "Open file", Title = "Open file",
FileTypeFilter = GetFileTypes(), FileTypeFilter = fileTypes,
SuggestedFileType = GetSuggestedFileType(fileTypes),
SuggestedFileName = "FileName", SuggestedFileName = "FileName",
SuggestedStartLocation = lastSelectedDirectory, SuggestedStartLocation = lastSelectedDirectory,
AllowMultiple = openMultiple.IsChecked == true AllowMultiple = openMultiple.IsChecked == true
@ -243,10 +305,12 @@ namespace ControlCatalog.Pages
SaveFilePicker.Click += async delegate SaveFilePicker.Click += async delegate
{ {
var fileTypes = GetFileTypes(); var fileTypes = GetFileTypes();
var suggestedType = GetSuggestedFileType(fileTypes);
var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions() var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions()
{ {
Title = "Save file", Title = "Save file",
FileTypeChoices = fileTypes, FileTypeChoices = fileTypes,
SuggestedFileType = suggestedType,
SuggestedStartLocation = lastSelectedDirectory, SuggestedStartLocation = lastSelectedDirectory,
SuggestedFileName = "FileName", SuggestedFileName = "FileName",
ShowOverwritePrompt = true ShowOverwritePrompt = true
@ -278,10 +342,12 @@ namespace ControlCatalog.Pages
}; };
SaveFilePickerWithResult.Click += async delegate SaveFilePickerWithResult.Click += async delegate
{ {
var saveFileTypes = new[] { FilePickerFileTypes.Json, FilePickerFileTypes.Xml };
var result = await GetStorageProvider().SaveFilePickerWithResultAsync(new FilePickerSaveOptions() var result = await GetStorageProvider().SaveFilePickerWithResultAsync(new FilePickerSaveOptions()
{ {
Title = "Save file", Title = "Save file",
FileTypeChoices = [FilePickerFileTypes.Json, FilePickerFileTypes.Xml], FileTypeChoices = saveFileTypes,
SuggestedFileType = GetSuggestedFileType(saveFileTypes),
SuggestedStartLocation = lastSelectedDirectory, SuggestedStartLocation = lastSelectedDirectory,
SuggestedFileName = "FileName", SuggestedFileName = "FileName",
ShowOverwritePrompt = true ShowOverwritePrompt = true

9
src/Avalonia.Base/Platform/Storage/FilePickerOpenOptions.cs

@ -7,6 +7,15 @@ namespace Avalonia.Platform.Storage;
/// </summary> /// </summary>
public class FilePickerOpenOptions : PickerOptions public class FilePickerOpenOptions : PickerOptions
{ {
/// <summary>
/// Gets or sets the file type that should be preselected when the dialog is opened.
/// </summary>
/// <remarks>
/// This value should reference one of the items in <see cref="FileTypeChoices"/>.
/// If not set, the first file type in <see cref="FileTypeChoices"/> may be selected by default.
/// </remarks>
public FilePickerFileType? SuggestedFileType { get; set; }
/// <summary> /// <summary>
/// Gets or sets an option indicating whether open picker allows users to select multiple files. /// Gets or sets an option indicating whether open picker allows users to select multiple files.
/// </summary> /// </summary>

9
src/Avalonia.Base/Platform/Storage/FilePickerSaveOptions.cs

@ -7,6 +7,15 @@ namespace Avalonia.Platform.Storage;
/// </summary> /// </summary>
public class FilePickerSaveOptions : PickerOptions public class FilePickerSaveOptions : PickerOptions
{ {
/// <summary>
/// Gets or sets the file type that should be preselected when the dialog is opened.
/// </summary>
/// <remarks>
/// This value should reference one of the items in <see cref="FileTypeChoices"/>.
/// If not set, the first file type in <see cref="FileTypeChoices"/> may be selected by default.
/// </remarks>
public FilePickerFileType? SuggestedFileType { get; set; }
/// <summary> /// <summary>
/// Gets or sets the default extension to be used to save the file. /// Gets or sets the default extension to be used to save the file.
/// </summary> /// </summary>

31
src/Avalonia.FreeDesktop/DBusSystemDialog.cs

@ -63,8 +63,13 @@ namespace Avalonia.FreeDesktop
ObjectPath objectPath; ObjectPath objectPath;
var chooserOptions = new Dictionary<string, VariantValue>(); var chooserOptions = new Dictionary<string, VariantValue>();
if (TryParseFilters(options.FileTypeFilter, out var filters)) if (TryParseFilters(options.FileTypeFilter, options.SuggestedFileType, out var filters,
out var currentFilter))
{
chooserOptions.Add("filters", filters); chooserOptions.Add("filters", filters);
if (currentFilter is { } filter)
chooserOptions.Add("current_filter", filter);
}
if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath) if (options.SuggestedStartLocation?.TryGetLocalPath() is { } folderPath)
chooserOptions.Add("current_folder", VariantValue.Array(Encoding.UTF8.GetBytes(folderPath + "\0"))); chooserOptions.Add("current_folder", VariantValue.Array(Encoding.UTF8.GetBytes(folderPath + "\0")));
@ -106,8 +111,13 @@ namespace Avalonia.FreeDesktop
var parentWindow = $"x11:{_handle.Handle:X}"; var parentWindow = $"x11:{_handle.Handle:X}";
ObjectPath objectPath; ObjectPath objectPath;
var chooserOptions = new Dictionary<string, VariantValue>(); var chooserOptions = new Dictionary<string, VariantValue>();
if (TryParseFilters(options.FileTypeChoices, out var filters)) if (TryParseFilters(options.FileTypeChoices, options.SuggestedFileType, out var filters,
out var currentFilter))
{
chooserOptions.Add("filters", filters); chooserOptions.Add("filters", filters);
if (currentFilter is { } filter)
chooserOptions.Add("current_filter", filter);
}
if (options.SuggestedFileName is { } currentName) if (options.SuggestedFileName is { } currentName)
chooserOptions.Add("current_name", VariantValue.String(currentName)); chooserOptions.Add("current_name", VariantValue.String(currentName));
@ -203,7 +213,10 @@ namespace Avalonia.FreeDesktop
.Select(static path => new BclStorageFolder(new DirectoryInfo(path))).ToList(); .Select(static path => new BclStorageFolder(new DirectoryInfo(path))).ToList();
} }
private static bool TryParseFilters(IReadOnlyList<FilePickerFileType>? fileTypes, out VariantValue result) private static bool TryParseFilters(IReadOnlyList<FilePickerFileType>? fileTypes,
FilePickerFileType? suggestedFileType,
out VariantValue result,
out VariantValue? currentFilter)
{ {
const uint GlobStyle = 0u; const uint GlobStyle = 0u;
const uint MimeStyle = 1u; const uint MimeStyle = 1u;
@ -212,10 +225,12 @@ namespace Avalonia.FreeDesktop
if (fileTypes is null) if (fileTypes is null)
{ {
result = default; result = default;
currentFilter = null;
return false; return false;
} }
var filters = new Array<Struct<string, Array<Struct<uint, string>>>>(); var filters = new Array<Struct<string, Array<Struct<uint, string>>>>();
currentFilter = null;
foreach (var fileType in fileTypes) foreach (var fileType in fileTypes)
{ {
@ -228,7 +243,15 @@ namespace Avalonia.FreeDesktop
else else
continue; continue;
filters.Add(Struct.Create(fileType.Name, new Array<Struct<uint, string>>(extensions))); var filterStruct = Struct.Create(fileType.Name, new Array<Struct<uint, string>>(extensions));
filters.Add(filterStruct);
if (suggestedFileType is not null && ReferenceEquals(fileType, suggestedFileType))
{
currentFilter = VariantValue.Struct(
VariantValue.String(filterStruct.Item1),
filterStruct.Item2.AsVariantValue());
}
} }
result = filters.AsVariantValue(); result = filters.AsVariantValue();

20
src/Avalonia.Native/StorageProviderApi.cs

@ -155,7 +155,7 @@ internal class StorageProviderApi(IAvnStorageProvider native, bool sandboxEnable
public async Task<IReadOnlyList<IStorageFile>> OpenFileDialog(TopLevelImpl? topLevel, FilePickerOpenOptions options) public async Task<IReadOnlyList<IStorageFile>> OpenFileDialog(TopLevelImpl? topLevel, FilePickerOpenOptions options)
{ {
using var fileTypes = new FilePickerFileTypesWrapper(options.FileTypeFilter, null); using var fileTypes = new FilePickerFileTypesWrapper(options.FileTypeFilter, null, options.SuggestedFileType);
var suggestedDirectory = options.SuggestedStartLocation?.Path.AbsoluteUri ?? string.Empty; var suggestedDirectory = options.SuggestedStartLocation?.Path.AbsoluteUri ?? string.Empty;
var (items, _) = await OpenDialogAsync(events => var (items, _) = await OpenDialogAsync(events =>
@ -174,7 +174,7 @@ internal class StorageProviderApi(IAvnStorageProvider native, bool sandboxEnable
public async Task<(IStorageFile? file, FilePickerFileType? selectedType)> SaveFileDialog(TopLevelImpl? topLevel, FilePickerSaveOptions options) public async Task<(IStorageFile? file, FilePickerFileType? selectedType)> SaveFileDialog(TopLevelImpl? topLevel, FilePickerSaveOptions options)
{ {
using var fileTypes = new FilePickerFileTypesWrapper(options.FileTypeChoices, options.DefaultExtension); using var fileTypes = new FilePickerFileTypesWrapper(options.FileTypeChoices, options.DefaultExtension, options.SuggestedFileType);
var suggestedDirectory = options.SuggestedStartLocation?.Path.AbsoluteUri ?? string.Empty; var suggestedDirectory = options.SuggestedStartLocation?.Path.AbsoluteUri ?? string.Empty;
var (items, selectedFilterIndex) = await OpenDialogAsync(events => var (items, selectedFilterIndex) = await OpenDialogAsync(events =>
@ -237,15 +237,25 @@ internal class StorageProviderApi(IAvnStorageProvider native, bool sandboxEnable
internal class FilePickerFileTypesWrapper( internal class FilePickerFileTypesWrapper(
IReadOnlyList<FilePickerFileType>? types, IReadOnlyList<FilePickerFileType>? types,
string? defaultExtension) string? defaultExtension,
FilePickerFileType? suggestedType)
: NativeCallbackBase, IAvnFilePickerFileTypes : NativeCallbackBase, IAvnFilePickerFileTypes
{ {
private readonly List<IDisposable> _disposables = new(); private readonly List<IDisposable> _disposables = new();
public int Count => types?.Count ?? 0; public int Count => types?.Count ?? 0;
public int IsDefaultType(int index) => (defaultExtension is not null && public int IsDefaultType(int index)
types![index].TryGetExtensions()?.Any(defaultExtension.EndsWith) == true).AsComBool(); {
if (types is null)
return false.AsComBool();
if (suggestedType is not null && ReferenceEquals(types[index], suggestedType))
return true.AsComBool();
return (defaultExtension is not null &&
types[index].TryGetExtensions()?.Any(defaultExtension.EndsWith) == true).AsComBool();
}
public int IsAnyType(int index) => public int IsAnyType(int index) =>
(types![index].Patterns?.Contains("*.*") == true || types[index].MimeTypes?.Contains("*.*") == true) (types![index].Patterns?.Contains("*.*") == true || types[index].MimeTypes?.Contains("*.*") == true)

2
src/Avalonia.X11/NativeDialogs/Gtk.cs

@ -101,6 +101,8 @@ namespace Avalonia.X11.NativeDialogs
[DllImport(GtkName)] [DllImport(GtkName)]
public static extern IntPtr gtk_file_chooser_get_filter(IntPtr chooser); public static extern IntPtr gtk_file_chooser_get_filter(IntPtr chooser);
[DllImport(GtkName)]
public static extern void gtk_file_chooser_set_filter(IntPtr chooser, IntPtr filter);
[DllImport(GtkName)] [DllImport(GtkName)]
public static extern void gtk_widget_realize(IntPtr gtkWidget); public static extern void gtk_widget_realize(IntPtr gtkWidget);

16
src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs

@ -40,7 +40,7 @@ namespace Avalonia.X11.NativeDialogs
return await await RunOnGlibThread(async () => return await await RunOnGlibThread(async () =>
{ {
var (files, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Open, var (files, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Open,
options.AllowMultiple, options.SuggestedStartLocation, null, options.FileTypeFilter, null, false) options.AllowMultiple, options.SuggestedStartLocation, null, options.SuggestedFileType, options.FileTypeFilter, null, false)
.ConfigureAwait(false); .ConfigureAwait(false);
return files?.Where(f => File.Exists(f)).Select(f => new BclStorageFile(new FileInfo(f))).ToArray() ?? return files?.Where(f => File.Exists(f)).Select(f => new BclStorageFile(new FileInfo(f))).ToArray() ??
Array.Empty<IStorageFile>(); Array.Empty<IStorageFile>();
@ -53,7 +53,7 @@ namespace Avalonia.X11.NativeDialogs
{ {
var (folders, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.SelectFolder, var (folders, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.SelectFolder,
options.AllowMultiple, options.SuggestedStartLocation, null, options.AllowMultiple, options.SuggestedStartLocation, null,
null, null, false) null, null, null, false)
.ConfigureAwait(false); .ConfigureAwait(false);
return folders?.Select(f => new BclStorageFolder(new DirectoryInfo(f))).ToArray() ?? return folders?.Select(f => new BclStorageFolder(new DirectoryInfo(f))).ToArray() ??
Array.Empty<IStorageFolder>(); Array.Empty<IStorageFolder>();
@ -65,7 +65,7 @@ namespace Avalonia.X11.NativeDialogs
return await await RunOnGlibThread(async () => return await await RunOnGlibThread(async () =>
{ {
var (files, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Save, var (files, _) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Save,
false, options.SuggestedStartLocation, options.SuggestedFileName, options.FileTypeChoices, false, options.SuggestedStartLocation, options.SuggestedFileName,options.SuggestedFileType, options.FileTypeChoices,
options.DefaultExtension, options.ShowOverwritePrompt ?? false) options.DefaultExtension, options.ShowOverwritePrompt ?? false)
.ConfigureAwait(false); .ConfigureAwait(false);
return files?.FirstOrDefault() is { } file return files?.FirstOrDefault() is { } file
@ -79,7 +79,7 @@ namespace Avalonia.X11.NativeDialogs
return await await RunOnGlibThread(async () => return await await RunOnGlibThread(async () =>
{ {
var (files, selectedFilter) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Save, var (files, selectedFilter) = await ShowDialog(options.Title, _window, GtkFileChooserAction.Save,
false, options.SuggestedStartLocation, options.SuggestedFileName, options.FileTypeChoices, false, options.SuggestedStartLocation, options.SuggestedFileName, options.SuggestedFileType, options.FileTypeChoices,
options.DefaultExtension, options.ShowOverwritePrompt ?? false) options.DefaultExtension, options.ShowOverwritePrompt ?? false)
.ConfigureAwait(false); .ConfigureAwait(false);
var file = files?.FirstOrDefault() is { } path var file = files?.FirstOrDefault() is { } path
@ -92,7 +92,7 @@ namespace Avalonia.X11.NativeDialogs
private unsafe Task<(string[]? files, FilePickerFileType? selectedFilter)> ShowDialog(string? title, private unsafe Task<(string[]? files, FilePickerFileType? selectedFilter)> ShowDialog(string? title,
IWindowImpl parent, GtkFileChooserAction action, IWindowImpl parent, GtkFileChooserAction action,
bool multiSelect, IStorageFolder? initialFolder, string? initialFileName, bool multiSelect, IStorageFolder? initialFolder, string? initialFileName, FilePickerFileType? suggestedFileType,
IEnumerable<FilePickerFileType>? filters, string? defaultExtension, bool overwritePrompt) IEnumerable<FilePickerFileType>? filters, string? defaultExtension, bool overwritePrompt)
{ {
IntPtr dlg; IntPtr dlg;
@ -165,8 +165,14 @@ namespace Avalonia.X11.NativeDialogs
} }
gtk_file_chooser_add_filter(dlg, filter); gtk_file_chooser_add_filter(dlg, filter);
if (suggestedFileType != null && suggestedFileType == f)
{
gtk_file_chooser_set_filter(dlg, filter);
}
} }
} }
} }
disposables = new List<IDisposable> disposables = new List<IDisposable>

17
src/Windows/Avalonia.Win32/Win32StorageProvider.cs

@ -4,6 +4,7 @@ using System.IO;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Utils;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Platform.Storage.FileIO; using Avalonia.Platform.Storage.FileIO;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
@ -33,7 +34,7 @@ namespace Avalonia.Win32
var (folders, _) = await ShowFilePicker( var (folders, _) = await ShowFilePicker(
true, true, true, true,
options.AllowMultiple, false, options.AllowMultiple, false,
options.Title, options.SuggestedFileName, options.SuggestedStartLocation, null, null, options.Title, options.SuggestedFileName, null, options.SuggestedStartLocation, null, null,
f => new BclStorageFolder(new DirectoryInfo(f))) f => new BclStorageFolder(new DirectoryInfo(f)))
.ConfigureAwait(false); .ConfigureAwait(false);
return folders; return folders;
@ -44,7 +45,7 @@ namespace Avalonia.Win32
var (files, _) = await ShowFilePicker( var (files, _) = await ShowFilePicker(
true, false, true, false,
options.AllowMultiple, false, options.AllowMultiple, false,
options.Title, options.SuggestedFileName, options.SuggestedStartLocation, options.Title, options.SuggestedFileName, options.SuggestedFileType, options.SuggestedStartLocation,
null, options.FileTypeFilter, null, options.FileTypeFilter,
f => new BclStorageFile(new FileInfo(f))) f => new BclStorageFile(new FileInfo(f)))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -56,7 +57,7 @@ namespace Avalonia.Win32
var (files, _) = await ShowFilePicker( var (files, _) = await ShowFilePicker(
false, false, false, false,
false, options.ShowOverwritePrompt, false, options.ShowOverwritePrompt,
options.Title, options.SuggestedFileName, options.SuggestedStartLocation, options.Title, options.SuggestedFileName, options.SuggestedFileType, options.SuggestedStartLocation,
options.DefaultExtension, options.FileTypeChoices, options.DefaultExtension, options.FileTypeChoices,
f => new BclStorageFile(new FileInfo(f))) f => new BclStorageFile(new FileInfo(f)))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -68,7 +69,7 @@ namespace Avalonia.Win32
var (files, index) = await ShowFilePicker( var (files, index) = await ShowFilePicker(
false, false, false, false,
false, options.ShowOverwritePrompt, false, options.ShowOverwritePrompt,
options.Title, options.SuggestedFileName, options.SuggestedStartLocation, options.Title, options.SuggestedFileName, options.SuggestedFileType, options.SuggestedStartLocation,
options.DefaultExtension, options.FileTypeChoices, options.DefaultExtension, options.FileTypeChoices,
f => new BclStorageFile(new FileInfo(f))) f => new BclStorageFile(new FileInfo(f)))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -88,6 +89,7 @@ namespace Avalonia.Win32
bool? showOverwritePrompt, bool? showOverwritePrompt,
string? title, string? title,
string? suggestedFileName, string? suggestedFileName,
FilePickerFileType? suggestedFileType,
IStorageFolder? folder, IStorageFolder? folder,
string? defaultExtension, string? defaultExtension,
IReadOnlyList<FilePickerFileType>? filters, IReadOnlyList<FilePickerFileType>? filters,
@ -118,6 +120,7 @@ namespace Avalonia.Win32
{ {
options &= ~FILEOPENDIALOGOPTIONS.FOS_OVERWRITEPROMPT; options &= ~FILEOPENDIALOGOPTIONS.FOS_OVERWRITEPROMPT;
} }
frm.SetOptions(options); frm.SetOptions(options);
defaultExtension ??= string.Empty; defaultExtension ??= string.Empty;
@ -152,6 +155,12 @@ namespace Avalonia.Win32
} }
} }
if (suggestedFileType != null &&
filters?.IndexOf(suggestedFileType) is { } fi and > -1)
{
frm.SetFileTypeIndex((uint)(fi + 1));
}
if (folder?.TryGetLocalPath() is { } folderPath) if (folder?.TryGetLocalPath() is { } folderPath)
{ {
var riid = UnmanagedMethods.ShellIds.IShellItem; var riid = UnmanagedMethods.ShellIds.IShellItem;

Loading…
Cancel
Save