diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 6fc3977d4ef..ace4a71a562 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; }; BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; }; + ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -101,6 +102,7 @@ AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; }; BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = ""; }; BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = ""; }; + ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -108,6 +110,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */, 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */, 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */, AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, @@ -122,6 +125,7 @@ AB661C1C2148230E00291242 /* Frameworks */ = { isa = PBXGroup; children = ( + ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */, 522D5958258159C1006F7F7A /* Carbon.framework */, 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */, 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index 535b6c3b665..e133a5d31f1 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,6 @@ #include "common.h" #include "INSWindowHolder.h" +#import class SystemDialogs : public ComSingleObject { @@ -7,6 +8,7 @@ FORWARD_IUNKNOWN() virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, IAvnSystemDialogEvents* events, + bool allowMultiple, const char* title, const char* initialDirectory) override { @@ -14,6 +16,7 @@ virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, { auto panel = [NSOpenPanel openPanel]; + panel.allowsMultipleSelection = allowMultiple; panel.canChooseDirectories = true; panel.canCreateDirectories = true; panel.canChooseFiles = false; @@ -118,7 +121,15 @@ virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, { auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; - panel.allowedFileTypes = allowedTypes; + // Prefer allowedContentTypes if available + if (@available(macOS 11.0, *)) + { + panel.allowedContentTypes = ConvertToUTType(allowedTypes); + } + else + { + panel.allowedFileTypes = allowedTypes; + } } } @@ -207,7 +218,18 @@ virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, { auto allowedTypes = [filtersString componentsSeparatedByString:@";"]; - panel.allowedFileTypes = allowedTypes; + // Prefer allowedContentTypes if available + if (@available(macOS 11.0, *)) + { + panel.allowedContentTypes = ConvertToUTType(allowedTypes); + } + else + { + panel.allowedFileTypes = allowedTypes; + } + + panel.allowsOtherFileTypes = false; + panel.extensionHidden = false; } } @@ -250,6 +272,32 @@ virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, } } } + +private: + NSMutableArray* ConvertToUTType(NSArray* allowedTypes) + { + auto originalCount = [allowedTypes count]; + auto mapped = [[NSMutableArray alloc] init]; + + if (@available(macOS 11.0, *)) + { + for (int i = 0; i < originalCount; i++) + { + auto utTypeStr = allowedTypes[i]; + auto utType = [UTType typeWithIdentifier:utTypeStr]; + if (utType == nil) + { + utType = [UTType typeWithMIMEType:utTypeStr]; + } + if (utType != nil) + { + [mapped addObject:utType]; + } + } + } + + return mapped; + } }; diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 903c849834e..8358fb3cd42 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,6 @@  - netstandard2.0 + netstandard2.0;net6.0 true enable diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 7676de54a69..40591c2d24d 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -69,6 +69,9 @@ + + + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index d675324d9f7..47d11738bc4 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -24,11 +24,6 @@ public MainView() { IList tabItems = ((IList)sideBar.Items); tabItems.Add(new TabItem() - { - Header = "Dialogs", - Content = new DialogsPage() - }); - tabItems.Add(new TabItem() { Header = "Screens", Content = new ScreenPage() diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 8a835867b35..cc23ef796ab 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -1,29 +1,57 @@ - - - Use filters - - - - - + + - - + - - - - - - - + + + + + + + + + + + + + + Use filters + + + Force managed dialog + Open multiple + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index efa30c27416..f7b6db12556 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -1,13 +1,21 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Dialogs; using Avalonia.Layout; using Avalonia.Markup.Xaml; -#pragma warning disable 4014 +using Avalonia.Platform.Storage; +using Avalonia.Platform.Storage.FileIO; + +#pragma warning disable CS0618 // Type or member is obsolete +#nullable enable + namespace ControlCatalog.Pages { public class DialogsPage : UserControl @@ -18,13 +26,16 @@ public DialogsPage() var results = this.Get("PickerLastResults"); var resultsVisible = this.Get("PickerLastResultsVisible"); + var bookmarkContainer = this.Get("BookmarkContainer"); + var openedFileContent = this.Get("OpenedFileContent"); + var openMultiple = this.Get("OpenMultiple"); - string? lastSelectedDirectory = null; + IStorageFolder? lastSelectedDirectory = null; - List? GetFilters() + List GetFilters() { if (this.Get("UseFilters").IsChecked != true) - return null; + return new List(); return new List { new FileDialogFilter @@ -39,12 +50,23 @@ public DialogsPage() }; } + List? GetFileTypes() + { + if (this.Get("UseFilters").IsChecked != true) + return null; + return new List + { + FilePickerFileTypes.All, + FilePickerFileTypes.TextPlain + }; + } + this.Get