diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 515f56167f..7f43256ddc 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -74,6 +74,7 @@ namespace Avalonia.Native .Bind().ToSingleton() .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs())) .Bind().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface())); } diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs new file mode 100644 index 0000000000..59d2731248 --- /dev/null +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -0,0 +1,85 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Native.Interop; +using Avalonia.Platform; + +namespace Avalonia.Native +{ + public class SystemDialogs : ISystemDialogImpl + { + IAvnSystemDialogs _native; + + public SystemDialogs(IAvnSystemDialogs native) + { + _native = native; + } + + public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + { + var events = new SystemDialogEvents(); + + if(dialog is OpenFileDialog ofd) + { + _native.OpenFileDialog(events, ofd.AllowMultiple, + ofd.Title, + ofd.InitialDirectory, + ofd.InitialFileName, + string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + } + else + { + _native.SaveFileDialog(events, + dialog.Title, + dialog.InitialDirectory, + dialog.InitialFileName, + string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + } + + return events.Task; + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + { + var events = new SystemDialogEvents(); + + _native.SelectFolderDialog(events, dialog.Title, dialog.InitialDirectory); + + return (await events.Task).FirstOrDefault(); + } + } + + public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents + { + private TaskCompletionSource _tcs; + + public SystemDialogEvents() + { + _tcs = new TaskCompletionSource(); + } + + public Task Task => _tcs.Task; + + public void OnCompleted(int numResults, IntPtr trFirstResultRef) + { + string[] results = new string[numResults]; + + unsafe + { + var ptr = (IntPtr*)trFirstResultRef.ToPointer(); + + for (int i = 0; i < numResults; i++) + { + results[i] = Marshal.PtrToStringAnsi(*ptr); + + ptr++; + } + } + + _tcs.SetResult(results); + } + } +} diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 27aa24536a..82404c1b8d 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -7,6 +7,8 @@ struct IAvnWindow; struct IAvnPopup; struct IAvnMacOptions; struct IAvnPlatformThreadingInterface; +struct IAvnSystemDialogEvents; +struct IAvnSystemDialogs; struct AvnSize { @@ -62,6 +64,7 @@ public: virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0; virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0; virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0; + virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0; }; AVNCOM(IAvnWindowBase, 02) : virtual IUnknown @@ -143,4 +146,29 @@ AVNCOM(IAvnPlatformThreadingInterface, 0b) : virtual IUnknown virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; }; +AVNCOM(IAvnSystemDialogEvents, 0c) : virtual IUnknown +{ + virtual void OnCompleted (int numResults, void* ptrFirstResult) = 0; +}; + +AVNCOM(IAvnSystemDialogs, 0d) : virtual IUnknown +{ + virtual void SelectFolderDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialPath) = 0; + + virtual void OpenFileDialog (IAvnSystemDialogEvents* events, + bool allowMultiple, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) = 0; + + virtual void SaveFileDialog (IAvnSystemDialogEvents* events, + const char* title, + const char* initialDirectory, + const char* intialFile, + const char* filters) = 0; +}; + extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();