csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
8.6 KiB
222 lines
8.6 KiB
using System;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Metadata;
|
|
using Avalonia.Platform.Storage;
|
|
using Avalonia.Utilities;
|
|
|
|
namespace Avalonia.Input;
|
|
|
|
/// <summary>
|
|
/// Represents a format usable with the clipboard and drag-and-drop.
|
|
/// </summary>
|
|
public abstract class DataFormat : IEquatable<DataFormat>
|
|
{
|
|
private protected DataFormat(DataFormatKind kind, string identifier)
|
|
{
|
|
Kind = kind;
|
|
Identifier = identifier;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the kind of the data format.
|
|
/// </summary>
|
|
public DataFormatKind Kind { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the identifier of the data format.
|
|
/// </summary>
|
|
public string Identifier { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a data format representing plain text.
|
|
/// Its data type is <see cref="string"/>.
|
|
/// </summary>
|
|
public static DataFormat<string> Text { get; } = CreateUniversalFormat<string>("Text");
|
|
|
|
/// <summary>
|
|
/// Gets a data format representing a bitmap.
|
|
/// Its data type is <see cref="Media.Imaging.Bitmap"/>.
|
|
/// </summary>
|
|
public static DataFormat<Bitmap> Bitmap { get; } = CreateUniversalFormat<Bitmap>("Bitmap");
|
|
|
|
/// <summary>
|
|
/// Gets a data format representing a single file.
|
|
/// Its data type is <see cref="IStorageItem"/>.
|
|
/// </summary>
|
|
public static DataFormat<IStorageItem> File { get; } = CreateUniversalFormat<IStorageItem>("File");
|
|
|
|
/// <summary>
|
|
/// Creates a name for this format, usable by the underlying platform.
|
|
/// </summary>
|
|
/// <param name="applicationPrefix">The system prefix used to recognize the name as an application format.</param>
|
|
/// <returns>A system name for the format.</returns>
|
|
/// <remarks>
|
|
/// This method can only be called if <see cref="Kind"/> is
|
|
/// <see cref="DataFormatKind.Application"/> or <see cref="DataFormatKind.Platform"/>.
|
|
/// </remarks>
|
|
public string ToSystemName(string applicationPrefix)
|
|
{
|
|
ThrowHelper.ThrowIfNull(applicationPrefix);
|
|
|
|
return Kind switch
|
|
{
|
|
DataFormatKind.Application => applicationPrefix + Identifier,
|
|
DataFormatKind.Platform => Identifier,
|
|
_ => throw new InvalidOperationException($"Cannot get system name for universal format {Identifier}")
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool Equals(DataFormat? other)
|
|
{
|
|
if (other is null)
|
|
return false;
|
|
if (ReferenceEquals(this, other))
|
|
return true;
|
|
return Kind == other.Kind && Identifier == other.Identifier;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool Equals(object? obj)
|
|
=> Equals(obj as DataFormat);
|
|
|
|
/// <inheritdoc />
|
|
public override int GetHashCode()
|
|
=> ((int)Kind * 397) ^ Identifier.GetHashCode();
|
|
|
|
/// <summary>
|
|
/// Compares two instances of <see cref="DataFormat"/> for equality.
|
|
/// </summary>
|
|
/// <param name="left">The first instance.</param>
|
|
/// <param name="right">The second instance.</param>
|
|
/// <returns>true if the two instances are equal; otherwise false.</returns>
|
|
public static bool operator ==(DataFormat? left, DataFormat? right)
|
|
=> Equals(left, right);
|
|
|
|
/// <summary>
|
|
/// Compares two instances of <see cref="DataFormat"/> for inequality.
|
|
/// </summary>
|
|
/// <param name="left">The first instance.</param>
|
|
/// <param name="right">The second instance.</param>
|
|
/// <returns>true if the two instances are not equal; otherwise false.</returns>
|
|
public static bool operator !=(DataFormat? left, DataFormat? right)
|
|
=> !Equals(left, right);
|
|
|
|
private static DataFormat<T> CreateUniversalFormat<T>(string identifier) where T : class
|
|
=> new(DataFormatKind.Universal, identifier);
|
|
|
|
/// <summary>
|
|
/// Creates a new format specific to the application that returns an array of <see cref="byte"/>.
|
|
/// </summary>
|
|
/// <param name="identifier">
|
|
/// <para>
|
|
/// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying
|
|
/// platform directly. However, two different applications using the same identifier
|
|
/// with <see cref="CreateBytesApplicationFormat"/> or <see cref="CreateStringApplicationFormat"/>
|
|
/// are able to share data using this format.
|
|
/// </para>
|
|
/// <para>Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted.</para>
|
|
/// </param>
|
|
/// <returns>A new <see cref="DataFormat"/>.</returns>
|
|
public static DataFormat<byte[]> CreateBytesApplicationFormat(string identifier)
|
|
=> CreateApplicationFormat<byte[]>(identifier);
|
|
|
|
/// <summary>
|
|
/// Creates a new format specific to the application that returns a <see cref="string"/>.
|
|
/// </summary>
|
|
/// <param name="identifier">
|
|
/// <para>
|
|
/// The format identifier. To avoid conflicts with system identifiers, this value isn't passed to the underlying
|
|
/// platform directly. However, two different applications using the same identifier
|
|
/// with <see cref="CreateBytesApplicationFormat"/> or <see cref="CreateStringApplicationFormat"/>
|
|
/// are able to share data using this format.
|
|
/// </para>
|
|
/// <para>Only ASCII letters (A-Z, a-z), digits (0-9), the dot (.) and the hyphen (-) are accepted.</para>
|
|
/// </param>
|
|
/// <returns>A new <see cref="DataFormat"/>.</returns>
|
|
public static DataFormat<string> CreateStringApplicationFormat(string identifier)
|
|
=> CreateApplicationFormat<string>(identifier);
|
|
|
|
private static DataFormat<T> CreateApplicationFormat<T>(string identifier)
|
|
where T : class
|
|
{
|
|
if (!IsValidApplicationFormatIdentifier(identifier))
|
|
throw new ArgumentException("Invalid application identifier", nameof(identifier));
|
|
|
|
return new(DataFormatKind.Application, identifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new format for the current platform that returns an array of <see cref="byte"/>.
|
|
/// </summary>
|
|
/// <param name="identifier">
|
|
/// The format identifier. This value is not validated and is passed AS IS to the underlying platform.
|
|
/// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI).
|
|
/// </param>
|
|
/// <returns>A new <see cref="DataFormat"/>.</returns>
|
|
public static DataFormat<byte[]> CreateBytesPlatformFormat(string identifier)
|
|
=> CreatePlatformFormat<byte[]>(identifier);
|
|
|
|
/// <summary>
|
|
/// Creates a new format for the current platform that returns a <see cref="string"/>.
|
|
/// </summary>
|
|
/// <param name="identifier">
|
|
/// The format identifier. This value is not validated and is passed AS IS to the underlying platform.
|
|
/// Most systems use mime types, but macOS requires Uniform Type Identifiers (UTI).
|
|
/// </param>
|
|
/// <returns>A new <see cref="DataFormat"/>.</returns>
|
|
public static DataFormat<string> CreateStringPlatformFormat(string identifier)
|
|
=> CreatePlatformFormat<string>(identifier);
|
|
|
|
private static DataFormat<T> CreatePlatformFormat<T>(string identifier)
|
|
where T : class
|
|
{
|
|
ThrowHelper.ThrowIfNullOrEmpty(identifier);
|
|
|
|
return new(DataFormatKind.Platform, identifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a <see cref="DataFormat"/> from a name coming from the underlying platform.
|
|
/// </summary>
|
|
/// <param name="systemName">The name.</param>
|
|
/// <param name="applicationPrefix">The system prefix used to recognize the name as an application format.</param>
|
|
/// <returns>A <see cref="DataFormat"/> corresponding to <paramref name="systemName"/>.</returns>
|
|
[PrivateApi]
|
|
public static DataFormat<T> FromSystemName<T>(string systemName, string applicationPrefix)
|
|
where T : class
|
|
{
|
|
ThrowHelper.ThrowIfNull(systemName);
|
|
ThrowHelper.ThrowIfNull(applicationPrefix);
|
|
|
|
if (systemName.StartsWith(applicationPrefix, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var identifier = systemName.Substring(applicationPrefix.Length);
|
|
if (IsValidApplicationFormatIdentifier(identifier))
|
|
return new(DataFormatKind.Application, identifier);
|
|
}
|
|
|
|
return new(DataFormatKind.Platform, systemName);
|
|
}
|
|
|
|
private static bool IsValidApplicationFormatIdentifier(string identifier)
|
|
{
|
|
if (string.IsNullOrEmpty(identifier))
|
|
return false;
|
|
|
|
foreach (var c in identifier)
|
|
{
|
|
if (!IsValidChar(c))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
static bool IsValidChar(char c)
|
|
=> char.IsAsciiLetterOrDigit(c) || c == '.' || c == '-';
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override string ToString()
|
|
=> $"{Kind}: {Identifier}";
|
|
}
|
|
|