3 changed files with 128 additions and 0 deletions
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus; |
|||
|
|||
[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] |
|||
namespace Avalonia.FreeDesktop |
|||
{ |
|||
[DBusInterface("org.freedesktop.portal.FileChooser")] |
|||
public interface IFileChooser : IDBusObject |
|||
{ |
|||
Task<ObjectPath> OpenFileAsync(string ParentWindow, string Title, IDictionary<string, object> Options); |
|||
Task<ObjectPath> SaveFileAsync(string ParentWindow, string Title, IDictionary<string, object> Options); |
|||
Task<ObjectPath> SaveFilesAsync(string ParentWindow, string Title, IDictionary<string, object> Options); |
|||
Task<T> GetAsync<T>(string prop); |
|||
Task<FileChooserProperties> GetAllAsync(); |
|||
Task SetAsync(string prop, object val); |
|||
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler); |
|||
} |
|||
|
|||
[Dictionary] |
|||
public class FileChooserProperties |
|||
{ |
|||
public uint Version { get; set; } |
|||
} |
|||
|
|||
public static class FileChooserExtensions |
|||
{ |
|||
public static Task<uint> GetVersionAsync(this IFileChooser o) => o.GetAsync<uint>("version"); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading.Tasks; |
|||
using Tmds.DBus; |
|||
|
|||
[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] |
|||
namespace Avalonia.FreeDesktop |
|||
{ |
|||
[DBusInterface("org.freedesktop.portal.Request")] |
|||
internal interface IRequest : IDBusObject |
|||
{ |
|||
Task CloseAsync(); |
|||
Task<IDisposable> WatchResponseAsync(Action<(uint response, IDictionary<string, object> results)> handler, Action<Exception> onError = null); |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Platform; |
|||
using Tmds.DBus; |
|||
|
|||
namespace Avalonia.FreeDesktop |
|||
{ |
|||
public class DBusSystemDialog : ISystemDialogImpl |
|||
{ |
|||
private readonly IFileChooser _fileChooser; |
|||
|
|||
public DBusSystemDialog() |
|||
{ |
|||
_fileChooser = DBusHelper.Connection.CreateProxy<IFileChooser>("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); |
|||
} |
|||
|
|||
public async Task<string[]> ShowFileDialogAsync(FileDialog dialog, Window parent) |
|||
{ |
|||
var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; |
|||
ObjectPath objectPath; |
|||
var options = new Dictionary<string, object>(); |
|||
if (dialog.Filters is not null) |
|||
options.Add("filters", ParseFilters(dialog)); |
|||
|
|||
switch (dialog) |
|||
{ |
|||
case OpenFileDialog openFileDialog: |
|||
options.Add("multiple", openFileDialog.AllowMultiple); |
|||
objectPath = await _fileChooser.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); |
|||
break; |
|||
case SaveFileDialog saveFileDialog: |
|||
if (saveFileDialog.InitialFileName is not null) |
|||
options.Add("current_name", saveFileDialog.InitialFileName); |
|||
if (saveFileDialog.Directory is not null) |
|||
options.Add("current_folder", Encoding.UTF8.GetBytes(saveFileDialog.Directory)); |
|||
objectPath = await _fileChooser.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); |
|||
break; |
|||
} |
|||
|
|||
var request = DBusHelper.Connection.CreateProxy<IRequest>("org.freedesktop.portal.Request", objectPath); |
|||
var tsc = new TaskCompletionSource<string[]>(); |
|||
using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); |
|||
var uris = await tsc.Task; |
|||
for (var i = 0; i < uris.Length; i++) |
|||
uris[i] = new Uri(uris[i]).AbsolutePath; |
|||
return uris; |
|||
} |
|||
|
|||
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) |
|||
{ |
|||
var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; |
|||
var options = new Dictionary<string, object> |
|||
{ |
|||
{ "directory", true } |
|||
}; |
|||
var objectPath = await _fileChooser.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); |
|||
var request = DBusHelper.Connection.CreateProxy<IRequest>("org.freedesktop.portal.Request", objectPath); |
|||
var tsc = new TaskCompletionSource<string[]>(); |
|||
using var disposable = await request.WatchResponseAsync(x => tsc.TrySetResult(x.results["uris"] as string[])); |
|||
var uris = await tsc.Task; |
|||
return uris.Length != 1 ? string.Empty : new Uri(uris[0]).AbsolutePath; |
|||
} |
|||
|
|||
private static (string name, (uint style, string extension)[])[] ParseFilters(FileDialog dialog) |
|||
{ |
|||
var filters = new (string name, (uint style, string extension)[])[dialog.Filters!.Count]; |
|||
for (var i = 0; i < filters.Length; i++) |
|||
{ |
|||
var extensions = dialog.Filters[i].Extensions.Select(static x => (0u, x)).ToArray(); |
|||
filters[i] = (dialog.Filters[i].Name, extensions); |
|||
} |
|||
|
|||
return filters; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue