Browse Source

Add xdg-desktop-portal file chooser

pull/8217/head
affederaffe 4 years ago
parent
commit
960ea2260c
  1. 32
      src/Avalonia.FreeDesktop/DBusFileChooser.cs
  2. 16
      src/Avalonia.FreeDesktop/DBusRequest.cs
  3. 80
      src/Avalonia.FreeDesktop/DBusSystemDialog.cs

32
src/Avalonia.FreeDesktop/DBusFileChooser.cs

@ -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");
}
}

16
src/Avalonia.FreeDesktop/DBusRequest.cs

@ -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);
}
}

80
src/Avalonia.FreeDesktop/DBusSystemDialog.cs

@ -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…
Cancel
Save