From 1624e88ba3aee39c040ea5c4c2bab205de97d031 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 25 Apr 2020 21:57:33 +0200 Subject: [PATCH 1/2] Remove Window.ShowDialog variant that takes IWindowImpl. --- .../Platform/ISystemDialogImpl.cs | 5 ++-- src/Avalonia.Controls/SystemDialog.cs | 6 ++-- src/Avalonia.Controls/Window.cs | 28 +++++++------------ src/Avalonia.DesignerSupport/Remote/Stubs.cs | 4 +-- .../ManagedFileDialogExtensions.cs | 12 +++----- src/Avalonia.Native/SystemDialogs.cs | 20 +++++++++---- .../NativeDialogs/GtkNativeFileDialogs.cs | 8 +++--- .../Avalonia.Win32/SystemDialogImpl.cs | 9 +++--- .../WindowTests.cs | 8 +++--- 9 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs index 6141b6eb19..affec6301b 100644 --- a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs +++ b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Avalonia.Platform; namespace Avalonia.Controls.Platform { @@ -14,8 +13,8 @@ namespace Avalonia.Controls.Platform /// The details of the file dialog to show. /// The parent window. /// A task returning the selected filenames. - Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent); + Task ShowFileDialogAsync(FileDialog dialog, Window parent); - Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent); + Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent); } } diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 6ccaa3c742..e74b950f23 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls if(parent == null) throw new ArgumentNullException(nameof(parent)); return ((await AvaloniaLocator.Current.GetService() - .ShowFileDialogAsync(this, parent?.PlatformImpl)) ?? + .ShowFileDialogAsync(this, parent)) ?? Array.Empty()).FirstOrDefault(); } } @@ -45,7 +45,7 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - return AvaloniaLocator.Current.GetService().ShowFileDialogAsync(this, parent?.PlatformImpl); + return AvaloniaLocator.Current.GetService().ShowFileDialogAsync(this, parent); } } @@ -61,7 +61,7 @@ namespace Avalonia.Controls { if(parent == null) throw new ArgumentNullException(nameof(parent)); - return AvaloniaLocator.Current.GetService().ShowFolderDialogAsync(this, parent?.PlatformImpl); + return AvaloniaLocator.Current.GetService().ShowFolderDialogAsync(this, parent); } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ee596432f7..dd00b850fe 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -484,22 +484,12 @@ namespace Avalonia.Controls /// . /// A task that can be used to retrieve the result of the dialog when it closes. /// - public Task ShowDialog(Window owner) => ShowDialog(owner.PlatformImpl); - - /// - /// Shows the window as a dialog. - /// - /// - /// The type of the result produced by the dialog. - /// - /// The dialog's owner window. - /// . - /// A task that can be used to retrieve the result of the dialog when it closes. - /// - public Task ShowDialog(IWindowImpl owner) + public Task ShowDialog(Window owner) { if (owner == null) + { throw new ArgumentNullException(nameof(owner)); + } if (IsVisible) { @@ -516,23 +506,25 @@ namespace Avalonia.Controls using (BeginAutoSizing()) { - - PlatformImpl?.ShowDialog(owner); + PlatformImpl?.ShowDialog(owner.PlatformImpl); Renderer?.Start(); + Observable.FromEventPattern( - x => this.Closed += x, - x => this.Closed -= x) + x => Closed += x, + x => Closed -= x) .Take(1) .Subscribe(_ => { owner.Activate(); result.SetResult((TResult)(_dialogResult ?? default(TResult))); }); + OnOpened(EventArgs.Empty); } - SetWindowStartupLocation(owner); + SetWindowStartupLocation(owner.PlatformImpl); + return result.Task; } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 7bf1d236bd..82950ce53b 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -166,10 +166,10 @@ namespace Avalonia.DesignerSupport.Remote class SystemDialogsStub : ISystemDialogImpl { - public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => + public Task ShowFileDialogAsync(FileDialog dialog, Window parent) => Task.FromResult((string[])null); - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) => + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) => Task.FromResult((string)null); } diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs index 771d2b1b5e..41f526a513 100644 --- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs +++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs @@ -1,19 +1,15 @@ -using System; using System.Linq; using System.Threading.Tasks; -using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Platform; -using Avalonia.Dialogs; -using Avalonia.Platform; namespace Avalonia.Dialogs { public static class ManagedFileDialogExtensions { - class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() + private class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() { - async Task Show(SystemDialog d, IWindowImpl parent) + async Task Show(SystemDialog d, Window parent) { var model = new ManagedFileChooserViewModel((FileSystemDialog)d); @@ -39,12 +35,12 @@ namespace Avalonia.Dialogs return result; } - public async Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) { return await Show(dialog, parent); } - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { return (await Show(dialog, parent))?.FirstOrDefault(); } diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index de355fdf71..d8c2c34f16 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Native.Interop; -using Avalonia.Platform; namespace Avalonia.Native { @@ -18,13 +17,15 @@ namespace Avalonia.Native _native = native; } - public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + public Task ShowFileDialogAsync(FileDialog dialog, Window parent) { var events = new SystemDialogEvents(); + var nativeParent = GetNativeWindow(parent); + if (dialog is OpenFileDialog ofd) { - _native.OpenFileDialog((parent as WindowImpl)?.Native, + _native.OpenFileDialog(nativeParent, events, ofd.AllowMultiple, ofd.Title ?? "", ofd.InitialDirectory ?? "", @@ -33,7 +34,7 @@ namespace Avalonia.Native } else { - _native.SaveFileDialog((parent as WindowImpl)?.Native, + _native.SaveFileDialog(nativeParent, events, dialog.Title ?? "", dialog.InitialDirectory ?? "", @@ -44,14 +45,21 @@ namespace Avalonia.Native return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; }); } - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { var events = new SystemDialogEvents(); - _native.SelectFolderDialog((parent as WindowImpl)?.Native, events, dialog.Title ?? "", dialog.InitialDirectory ?? ""); + var nativeParent = GetNativeWindow(parent); + + _native.SelectFolderDialog(nativeParent, events, dialog.Title ?? "", dialog.InitialDirectory ?? ""); return events.Task.ContinueWith(t => { events.Dispose(); return t.Result.FirstOrDefault(); }); } + + private IAvnWindow GetNativeWindow(Window window) + { + return (window?.PlatformImpl as WindowImpl)?.Native; + } } public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs index 5c9a0c992a..0c80611ada 100644 --- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs +++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs @@ -102,23 +102,23 @@ namespace Avalonia.X11.NativeDialogs return tcs.Task; } - public async Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) { await EnsureInitialized(); return await await RunOnGlibThread( - () => ShowDialog(dialog.Title, parent, + () => ShowDialog(dialog.Title, parent?.PlatformImpl, dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, (dialog as OpenFileDialog)?.AllowMultiple ?? false, Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory, string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters)); } - public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { await EnsureInitialized(); return await await RunOnGlibThread(async () => { - var res = await ShowDialog(dialog.Title, parent, + var res = await ShowDialog(dialog.Title, parent?.PlatformImpl, GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, null); return res?.FirstOrDefault(); }); diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs index c452290afc..8bdd4b7bfa 100644 --- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs +++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs @@ -5,7 +5,6 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Platform; -using Avalonia.Platform; using Avalonia.Win32.Interop; namespace Avalonia.Win32 @@ -16,9 +15,9 @@ namespace Avalonia.Win32 private const UnmanagedMethods.FOS DefaultDialogOptions = UnmanagedMethods.FOS.FOS_FORCEFILESYSTEM | UnmanagedMethods.FOS.FOS_NOVALIDATE | UnmanagedMethods.FOS.FOS_NOTESTFILECREATE | UnmanagedMethods.FOS.FOS_DONTADDTORECENT; - public unsafe Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + public unsafe Task ShowFileDialogAsync(FileDialog dialog, Window parent) { - var hWnd = parent?.Handle?.Handle ?? IntPtr.Zero; + var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero; return Task.Factory.StartNew(() => { var result = Array.Empty(); @@ -98,13 +97,13 @@ namespace Avalonia.Win32 }); } - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { return Task.Factory.StartNew(() => { string result = string.Empty; - var hWnd = parent?.Handle?.Handle ?? IntPtr.Zero; + var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero; Guid clsid = UnmanagedMethods.ShellIds.OpenFileDialog; Guid iid = UnmanagedMethods.ShellIds.IFileDialog; diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index cf2920998a..29944c6f85 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var parent = Mock.Of(); + var parent = Mock.Of(); var renderer = new Mock(); var target = new Window(CreateImpl(renderer)); @@ -171,7 +171,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var parent = Mock.Of(); + var parent = Mock.Of(); var target = new Window(); var raised = false; @@ -203,7 +203,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var parent = new Mock(); + var parent = new Mock(); var windowImpl = new Mock(); windowImpl.SetupProperty(x => x.Closed); windowImpl.Setup(x => x.Scaling).Returns(1); @@ -242,7 +242,7 @@ namespace Avalonia.Controls.UnitTests { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var parent = new Mock(); + var parent = new Mock(); var windowImpl = new Mock(); windowImpl.SetupProperty(x => x.Closed); windowImpl.Setup(x => x.Scaling).Returns(1); From b3075f98fdbb5b9f3823a34fcc3c73a9a48bf660 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 25 Apr 2020 22:25:22 +0200 Subject: [PATCH 2/2] Cache PlatformImpl before invoking async code. --- src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs index 0c80611ada..07da0048d0 100644 --- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs +++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs @@ -105,8 +105,11 @@ namespace Avalonia.X11.NativeDialogs public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) { await EnsureInitialized(); + + var platformImpl = parent?.PlatformImpl; + return await await RunOnGlibThread( - () => ShowDialog(dialog.Title, parent?.PlatformImpl, + () => ShowDialog(dialog.Title, platformImpl, dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, (dialog as OpenFileDialog)?.AllowMultiple ?? false, Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory, @@ -116,9 +119,12 @@ namespace Avalonia.X11.NativeDialogs public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) { await EnsureInitialized(); + + var platformImpl = parent?.PlatformImpl; + return await await RunOnGlibThread(async () => { - var res = await ShowDialog(dialog.Title, parent?.PlatformImpl, + var res = await ShowDialog(dialog.Title, platformImpl, GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, null); return res?.FirstOrDefault(); });