22 changed files with 843 additions and 1 deletions
@ -0,0 +1,107 @@ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.IO; |
|||
using System.Security; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage.FileIO; |
|||
|
|||
[Unstable] |
|||
public class BclStorageFile : IStorageBookmarkFile |
|||
{ |
|||
private readonly FileInfo _fileInfo; |
|||
|
|||
public BclStorageFile(FileInfo fileInfo) |
|||
{ |
|||
_fileInfo = fileInfo ?? throw new ArgumentNullException(nameof(fileInfo)); |
|||
} |
|||
|
|||
public bool CanOpenRead => true; |
|||
|
|||
public bool CanOpenWrite => true; |
|||
|
|||
public string Name => _fileInfo.Name; |
|||
|
|||
public virtual bool CanBookmark => true; |
|||
|
|||
public Task<StorageItemProperties> GetBasicPropertiesAsync() |
|||
{ |
|||
var props = new StorageItemProperties(); |
|||
if (_fileInfo.Exists) |
|||
{ |
|||
props = new StorageItemProperties( |
|||
(ulong)_fileInfo.Length, |
|||
_fileInfo.CreationTimeUtc, |
|||
_fileInfo.LastAccessTimeUtc); |
|||
} |
|||
return Task.FromResult(props); |
|||
} |
|||
|
|||
public Task<IStorageFolder?> GetParentAsync() |
|||
{ |
|||
if (_fileInfo.Directory is { } directory) |
|||
{ |
|||
return Task.FromResult<IStorageFolder?>(new BclStorageFolder(directory)); |
|||
} |
|||
return Task.FromResult<IStorageFolder?>(null); |
|||
} |
|||
|
|||
public Task<Stream> OpenRead() |
|||
{ |
|||
return Task.FromResult<Stream>(_fileInfo.OpenRead()); |
|||
} |
|||
|
|||
public Task<Stream> OpenWrite() |
|||
{ |
|||
return Task.FromResult<Stream>(_fileInfo.OpenWrite()); |
|||
} |
|||
|
|||
public virtual Task<string?> SaveBookmark() |
|||
{ |
|||
return Task.FromResult<string?>(_fileInfo.FullName); |
|||
} |
|||
|
|||
public Task ReleaseBookmark() |
|||
{ |
|||
// No-op
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public bool TryGetUri([NotNullWhen(true)] out Uri? uri) |
|||
{ |
|||
try |
|||
{ |
|||
if (_fileInfo.Directory is not null) |
|||
{ |
|||
uri = Path.IsPathRooted(_fileInfo.FullName) ? |
|||
new Uri(new Uri("file://"), _fileInfo.FullName) : |
|||
new Uri(_fileInfo.FullName, UriKind.Relative); |
|||
return true; |
|||
} |
|||
|
|||
uri = null; |
|||
return false; |
|||
} |
|||
catch (SecurityException) |
|||
{ |
|||
uri = null; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
|
|||
~BclStorageFile() |
|||
{ |
|||
Dispose(disposing: false); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
Dispose(disposing: true); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.IO; |
|||
using System.Security; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage.FileIO; |
|||
|
|||
[Unstable] |
|||
public class BclStorageFolder : IStorageBookmarkFolder |
|||
{ |
|||
private readonly DirectoryInfo _directoryInfo; |
|||
|
|||
public BclStorageFolder(DirectoryInfo directoryInfo) |
|||
{ |
|||
_directoryInfo = directoryInfo ?? throw new ArgumentNullException(nameof(directoryInfo)); |
|||
if (!_directoryInfo.Exists) |
|||
{ |
|||
throw new ArgumentException("Directory must exist", nameof(directoryInfo)); |
|||
} |
|||
} |
|||
|
|||
public string Name => _directoryInfo.Name; |
|||
|
|||
public bool CanBookmark => true; |
|||
|
|||
public Task<StorageItemProperties> GetBasicPropertiesAsync() |
|||
{ |
|||
var props = new StorageItemProperties( |
|||
null, |
|||
_directoryInfo.CreationTimeUtc, |
|||
_directoryInfo.LastAccessTimeUtc); |
|||
return Task.FromResult(props); |
|||
} |
|||
|
|||
public Task<IStorageFolder?> GetParentAsync() |
|||
{ |
|||
if (_directoryInfo.Parent is { } directory) |
|||
{ |
|||
return Task.FromResult<IStorageFolder?>(new BclStorageFolder(directory)); |
|||
} |
|||
return Task.FromResult<IStorageFolder?>(null); |
|||
} |
|||
|
|||
public virtual Task<string?> SaveBookmark() |
|||
{ |
|||
return Task.FromResult<string?>(_directoryInfo.FullName); |
|||
} |
|||
|
|||
public Task ReleaseBookmark() |
|||
{ |
|||
// No-op
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public bool TryGetUri([NotNullWhen(true)] out Uri? uri) |
|||
{ |
|||
try |
|||
{ |
|||
uri = Path.IsPathRooted(_directoryInfo.FullName) ? |
|||
new Uri(new Uri("file://"), _directoryInfo.FullName) : |
|||
new Uri(_directoryInfo.FullName, UriKind.Relative); |
|||
|
|||
return true; |
|||
} |
|||
catch (SecurityException) |
|||
{ |
|||
uri = null; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
|
|||
~BclStorageFolder() |
|||
{ |
|||
Dispose(disposing: false); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
Dispose(disposing: true); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage.FileIO; |
|||
|
|||
[Unstable] |
|||
public abstract class BclStorageProvider : IStorageProvider |
|||
{ |
|||
public abstract bool CanOpen { get; } |
|||
public abstract Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options); |
|||
|
|||
public abstract bool CanSave { get; } |
|||
public abstract Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options); |
|||
|
|||
public abstract bool CanPickFolder { get; } |
|||
public abstract Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options); |
|||
|
|||
public virtual Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark) |
|||
{ |
|||
var file = new FileInfo(bookmark); |
|||
return file.Exists |
|||
? Task.FromResult<IStorageBookmarkFile?>(new BclStorageFile(file)) |
|||
: Task.FromResult<IStorageBookmarkFile?>(null); |
|||
} |
|||
|
|||
public virtual Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark) |
|||
{ |
|||
var folder = new DirectoryInfo(bookmark); |
|||
return folder.Exists |
|||
? Task.FromResult<IStorageBookmarkFolder?>(new BclStorageFolder(folder)) |
|||
: Task.FromResult<IStorageBookmarkFolder?>(null); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage.FileIO; |
|||
|
|||
[Unstable] |
|||
public static class StorageProviderHelpers |
|||
{ |
|||
public static string NameWithExtension(string path, string? defaultExtension, FilePickerFileType? filter) |
|||
{ |
|||
var name = Path.GetFileName(path); |
|||
if (name != null && !Path.HasExtension(name)) |
|||
{ |
|||
if (filter?.Patterns?.Count > 0) |
|||
{ |
|||
if (defaultExtension != null |
|||
&& filter.Patterns.Contains(defaultExtension)) |
|||
{ |
|||
return Path.ChangeExtension(path, defaultExtension.TrimStart('.')); |
|||
} |
|||
|
|||
var ext = filter.Patterns.FirstOrDefault(x => x != "*.*"); |
|||
ext = ext?.Split(new[] { "*." }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); |
|||
if (ext != null) |
|||
{ |
|||
return Path.ChangeExtension(path, ext); |
|||
} |
|||
} |
|||
|
|||
if (defaultExtension != null) |
|||
{ |
|||
return Path.ChangeExtension(path, defaultExtension); |
|||
} |
|||
} |
|||
|
|||
return path; |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Represents a name mapped to the associated file types (extensions).
|
|||
/// </summary>
|
|||
public class FilePickerFileType |
|||
{ |
|||
public FilePickerFileType(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// File type name.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// List of extensions in GLOB format. I.e. "*.png" or "*.*".
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Used on Windows and Linux systems.
|
|||
/// </remarks>
|
|||
public IReadOnlyList<string>? Patterns { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// List of extensions in MIME format.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Used on Android, Browser and Linux systems.
|
|||
/// </remarks>
|
|||
public IReadOnlyList<string>? MimeTypes { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// List of extensions in Apple uniform format.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Used only on Apple devices.
|
|||
/// See https://developer.apple.com/documentation/uniformtypeidentifiers/system_declared_uniform_type_identifiers.
|
|||
/// </remarks>
|
|||
public IReadOnlyList<string>? AppleUniformTypeIdentifiers { get; set; } |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Dictionary of well known file types.
|
|||
/// </summary>
|
|||
public static class FilePickerFileTypes |
|||
{ |
|||
public static FilePickerFileType All { get; } = new("All") |
|||
{ |
|||
Patterns = new[] { "*.*" }, |
|||
MimeTypes = new[] { "*/*" } |
|||
}; |
|||
|
|||
public static FilePickerFileType TextPlain { get; } = new("Plain Text") |
|||
{ |
|||
Patterns = new[] { "*.txt" }, |
|||
AppleUniformTypeIdentifiers = new[] { "public.plain-text" }, |
|||
MimeTypes = new[] { "text/plain" } |
|||
}; |
|||
|
|||
public static FilePickerFileType ImageAll { get; } = new("All Images") |
|||
{ |
|||
Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp" }, |
|||
AppleUniformTypeIdentifiers = new[] { "public.image" }, |
|||
MimeTypes = new[] { "image/*" } |
|||
}; |
|||
|
|||
public static FilePickerFileType ImageJpg { get; } = new("JPEG image") |
|||
{ |
|||
Patterns = new[] { "*.jpg", "*.jpeg" }, |
|||
AppleUniformTypeIdentifiers = new[] { "public.jpeg" }, |
|||
MimeTypes = new[] { "image/jpeg" } |
|||
}; |
|||
|
|||
public static FilePickerFileType ImagePng { get; } = new("PNG image") |
|||
{ |
|||
Patterns = new[] { "*.png" }, |
|||
AppleUniformTypeIdentifiers = new[] { "public.png" }, |
|||
MimeTypes = new[] { "image/png" } |
|||
}; |
|||
|
|||
public static FilePickerFileType Pdf { get; } = new("PDF document") |
|||
{ |
|||
Patterns = new[] { "*.pdf" }, |
|||
AppleUniformTypeIdentifiers = new[] { "com.adobe.pdf" }, |
|||
MimeTypes = new[] { "application/pdf" } |
|||
}; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Options class for <see cref="IStorageProvider.OpenFilePickerAsync"/> method.
|
|||
/// </summary>
|
|||
public class FilePickerOpenOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the text that appears in the title bar of a file dialog.
|
|||
/// </summary>
|
|||
public string? Title { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the initial location where the file open picker looks for files to present to the user.
|
|||
/// </summary>
|
|||
public IStorageFolder? SuggestedStartLocation { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets an option indicating whether open picker allows users to select multiple files.
|
|||
/// </summary>
|
|||
public bool AllowMultiple { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the collection of file types that the file open picker displays.
|
|||
/// </summary>
|
|||
public IReadOnlyList<FilePickerFileType>? FileTypeFilter { get; set; } |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Options class for <see cref="IStorageProvider.SaveFilePickerAsync"/> method.
|
|||
/// </summary>
|
|||
public class FilePickerSaveOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the text that appears in the title bar of a file dialog.
|
|||
/// </summary>
|
|||
public string? Title { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the file name that the file save picker suggests to the user.
|
|||
/// </summary>
|
|||
public string? SuggestedFileName { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the default extension to be used to save the file.
|
|||
/// </summary>
|
|||
public string? DefaultExtension { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the initial location where the file open picker looks for files to present to the user.
|
|||
/// </summary>
|
|||
public IStorageFolder? SuggestedStartLocation { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the collection of valid file types that the user can choose to assign to a file.
|
|||
/// </summary>
|
|||
public IReadOnlyList<FilePickerFileType>? FileTypeChoices { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether file open picker displays a warning if the user specifies the name of a file that already exists.
|
|||
/// </summary>
|
|||
public bool? ShowOverwritePrompt { get; set; } |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Options class for <see cref="IStorageProvider.OpenFolderPickerAsync"/> method.
|
|||
/// </summary>
|
|||
public class FolderPickerOpenOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the text that appears in the title bar of a folder dialog.
|
|||
/// </summary>
|
|||
public string? Title { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets an option indicating whether open picker allows users to select multiple folders.
|
|||
/// </summary>
|
|||
public bool AllowMultiple { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the initial location where the file open picker looks for files to present to the user.
|
|||
/// </summary>
|
|||
public IStorageFolder? SuggestedStartLocation { get; set; } |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
[NotClientImplementable] |
|||
public interface IStorageBookmarkItem : IStorageItem |
|||
{ |
|||
Task ReleaseBookmark(); |
|||
} |
|||
|
|||
[NotClientImplementable] |
|||
public interface IStorageBookmarkFile : IStorageFile, IStorageBookmarkItem |
|||
{ |
|||
} |
|||
|
|||
[NotClientImplementable] |
|||
public interface IStorageBookmarkFolder : IStorageFolder, IStorageBookmarkItem |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Represents a file. Provides information about the file and its contents, and ways to manipulate them.
|
|||
/// </summary>
|
|||
[NotClientImplementable] |
|||
public interface IStorageFile : IStorageItem |
|||
{ |
|||
/// <summary>
|
|||
/// Returns true, if file is readable.
|
|||
/// </summary>
|
|||
bool CanOpenRead { get; } |
|||
|
|||
/// <summary>
|
|||
/// Opens a stream for read access.
|
|||
/// </summary>
|
|||
Task<Stream> OpenRead(); |
|||
|
|||
/// <summary>
|
|||
/// Returns true, if file is writeable.
|
|||
/// </summary>
|
|||
bool CanOpenWrite { get; } |
|||
|
|||
/// <summary>
|
|||
/// Opens stream for writing to the file.
|
|||
/// </summary>
|
|||
Task<Stream> OpenWrite(); |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Manipulates folders and their contents, and provides information about them.
|
|||
/// </summary>
|
|||
[NotClientImplementable] |
|||
public interface IStorageFolder : IStorageItem |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Manipulates storage items (files and folders) and their contents, and provides information about them
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This interface inherits <see cref="IDisposable"/> . It's recommended to dispose <see cref="IStorageItem"/> when it's not used anymore.
|
|||
/// </remarks>
|
|||
[NotClientImplementable] |
|||
public interface IStorageItem : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the name of the item including the file name extension if there is one.
|
|||
/// </summary>
|
|||
string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the full file-system path of the item, if the item has a path.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Android backend might return file path with "content:" scheme.
|
|||
/// Browser and iOS backends might return relative uris.
|
|||
/// </remarks>
|
|||
bool TryGetUri([NotNullWhen(true)] out Uri? uri); |
|||
|
|||
/// <summary>
|
|||
/// Gets the basic properties of the current item.
|
|||
/// </summary>
|
|||
Task<StorageItemProperties> GetBasicPropertiesAsync(); |
|||
|
|||
/// <summary>
|
|||
/// Returns true is item can be bookmarked and reused later.
|
|||
/// </summary>
|
|||
bool CanBookmark { get; } |
|||
|
|||
/// <summary>
|
|||
/// Saves items to a bookmark.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// Returns identifier of a bookmark. Can be null if OS denied request.
|
|||
/// </returns>
|
|||
Task<string?> SaveBookmark(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the parent folder of the current storage item.
|
|||
/// </summary>
|
|||
Task<IStorageFolder?> GetParentAsync(); |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
[NotClientImplementable] |
|||
public interface IStorageProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Returns true if it's possible to open file picker on the current platform.
|
|||
/// </summary>
|
|||
bool CanOpen { get; } |
|||
|
|||
/// <summary>
|
|||
/// Opens file picker dialog.
|
|||
/// </summary>
|
|||
/// <returns>Array of selected <see cref="IStorageFile"/> or empty collection if user canceled the dialog.</returns>
|
|||
Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options); |
|||
|
|||
/// <summary>
|
|||
/// Returns true if it's possible to open save file picker on the current platform.
|
|||
/// </summary>
|
|||
bool CanSave { get; } |
|||
|
|||
/// <summary>
|
|||
/// Opens save file picker dialog.
|
|||
/// </summary>
|
|||
/// <returns>Saved <see cref="IStorageFile"/> or null if user canceled the dialog.</returns>
|
|||
Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options); |
|||
|
|||
/// <summary>
|
|||
/// Returns true if it's possible to open folder picker on the current platform.
|
|||
/// </summary>
|
|||
bool CanPickFolder { get; } |
|||
|
|||
/// <summary>
|
|||
/// Opens folder picker dialog.
|
|||
/// </summary>
|
|||
/// <returns>Array of selected <see cref="IStorageFolder"/> or empty collection if user canceled the dialog.</returns>
|
|||
Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options); |
|||
|
|||
/// <summary>
|
|||
/// Open <see cref="IStorageBookmarkFile"/> from the bookmark ID.
|
|||
/// </summary>
|
|||
/// <param name="bookmark">Bookmark ID.</param>
|
|||
/// <returns>Bookmarked file or null if OS denied request.</returns>
|
|||
Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark); |
|||
|
|||
/// <summary>
|
|||
/// Open <see cref="IStorageBookmarkFolder"/> from the bookmark ID.
|
|||
/// </summary>
|
|||
/// <param name="bookmark">Bookmark ID.</param>
|
|||
/// <returns>Bookmarked folder or null if OS denied request.</returns>
|
|||
Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark); |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Platform.Storage; |
|||
|
|||
/// <summary>
|
|||
/// Provides access to the content-related properties of an item (like a file or folder).
|
|||
/// </summary>
|
|||
public class StorageItemProperties |
|||
{ |
|||
public StorageItemProperties( |
|||
ulong? size = null, |
|||
DateTimeOffset? dateCreated = null, |
|||
DateTimeOffset? dateModified = null) |
|||
{ |
|||
Size = size; |
|||
DateCreated = dateCreated; |
|||
DateModified = dateModified; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the file in bytes.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Can be null if property is not available.
|
|||
/// </remarks>
|
|||
public ulong? Size { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the date and time that the current folder was created.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Can be null if property is not available.
|
|||
/// </remarks>
|
|||
public DateTimeOffset? DateCreated { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the date and time of the last time the file was modified.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Can be null if property is not available.
|
|||
/// </remarks>
|
|||
public DateTimeOffset? DateModified { get; } |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
#nullable enable |
|||
using Avalonia.Platform.Storage; |
|||
|
|||
namespace Avalonia.Controls.Platform; |
|||
|
|||
/// <summary>
|
|||
/// Factory allows to register custom storage provider instead of native implementation.
|
|||
/// </summary>
|
|||
public interface IStorageProviderFactory |
|||
{ |
|||
IStorageProvider CreateProvider(TopLevel topLevel); |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Controls.Platform |
|||
{ |
|||
/// <summary>
|
|||
/// Defines a platform-specific system dialog implementation.
|
|||
/// </summary>
|
|||
[Obsolete] |
|||
internal class SystemDialogImpl : ISystemDialogImpl |
|||
{ |
|||
public async Task<string[]?> ShowFileDialogAsync(FileDialog dialog, Window parent) |
|||
{ |
|||
if (dialog is OpenFileDialog openDialog) |
|||
{ |
|||
var filePicker = parent.StorageProvider; |
|||
if (!filePicker.CanOpen) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var options = openDialog.ToFilePickerOpenOptions(); |
|||
|
|||
var files = await filePicker.OpenFilePickerAsync(options); |
|||
return files |
|||
.Select(file => file.TryGetUri(out var fullPath) |
|||
? fullPath.LocalPath |
|||
: file.Name) |
|||
.ToArray(); |
|||
} |
|||
else if (dialog is SaveFileDialog saveDialog) |
|||
{ |
|||
var filePicker = parent.StorageProvider; |
|||
if (!filePicker.CanSave) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var options = saveDialog.ToFilePickerSaveOptions(); |
|||
|
|||
var file = await filePicker.SaveFilePickerAsync(options); |
|||
if (file is null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var filePath = file.TryGetUri(out var fullPath) |
|||
? fullPath.LocalPath |
|||
: file.Name; |
|||
return new[] { filePath }; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public async Task<string?> ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) |
|||
{ |
|||
var filePicker = parent.StorageProvider; |
|||
if (!filePicker.CanPickFolder) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var options = dialog.ToFolderPickerOpenOptions(); |
|||
|
|||
var folders = await filePicker.OpenFolderPickerAsync(options); |
|||
return folders |
|||
.Select(f => f.TryGetUri(out var uri) ? uri.LocalPath : null) |
|||
.FirstOrDefault(u => u is not null); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Avalonia.Metadata; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Platform.Storage; |
|||
|
|||
namespace Avalonia.Controls.Platform; |
|||
|
|||
[Unstable] |
|||
public interface ITopLevelImplWithStorageProvider : ITopLevelImpl |
|||
{ |
|||
public IStorageProvider StorageProvider { get; } |
|||
} |
|||
Loading…
Reference in new issue