165 changed files with 1911 additions and 852 deletions
@ -0,0 +1,32 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<InternalsVisibleTo Include="AvaloniaUI.Xpf.WinApiShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" /> |
|||
<InternalsVisibleTo Include="System.Windows.Forms, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="System.Windows.Forms.Primitives, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationFramework-SystemData, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.Aero, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="System.Xaml, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework-SystemDrawing, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationFramework-SystemCore, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="WindowsFormsIntegration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.AeroLite, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.Aero2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="UIAutomationClient, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.Luna, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="UIAutomationTypes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.Royale, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="System.Windows.Input.Manipulations, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="System.Windows.Controls.Ribbon, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationFramework-SystemXml, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="ReachFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="System.Printing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework-SystemXmlLinq, PublicKey=00000000000000000400000000000000" /> |
|||
<InternalsVisibleTo Include="PresentationUI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="Atlantis, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" /> |
|||
<InternalsVisibleTo Include="WindowsBase, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationFramework.Classic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="UIAutomationProvider, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
<InternalsVisibleTo Include="PresentationCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,22 +1,17 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Presenters; |
|||
using Avalonia.Input.TextInput; |
|||
using Avalonia.Markup.Xaml; |
|||
using Avalonia.Win32.WinRT.Composition; |
|||
|
|||
namespace Sandbox |
|||
{ |
|||
public class MainWindow : Window |
|||
public partial class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
this.InitializeComponent(); |
|||
this.AttachDevTools(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
InitializeComponent(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Metadata; |
|||
|
|||
/// <summary>
|
|||
/// Defines how compiler should split avalonia list string value before parsing individual items.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] |
|||
public sealed class AvaloniaListAttribute : Attribute |
|||
{ |
|||
/// <summary>
|
|||
/// Separator used to split input string.
|
|||
/// Default value is ','.
|
|||
/// </summary>
|
|||
public string[]? Separators { get; init; } |
|||
|
|||
/// <summary>
|
|||
/// Split options used to split input string.
|
|||
/// Default value is RemoveEmptyEntries with TrimEntries.
|
|||
/// </summary>
|
|||
// StringSplitOptions.TrimEntries = 2, but only on net6 target.
|
|||
public StringSplitOptions SplitOptions { get; init; } = StringSplitOptions.RemoveEmptyEntries | (StringSplitOptions)2; |
|||
} |
|||
@ -1,281 +1,48 @@ |
|||
using System; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
#if !BUILDTASK
|
|||
using Avalonia.Platform.Internal; |
|||
using Avalonia.Utilities; |
|||
#endif
|
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
/// <summary>
|
|||
/// Loads assets compiled into the application binary.
|
|||
/// </summary>
|
|||
public class AssetLoader |
|||
namespace Avalonia.Platform; |
|||
|
|||
#if !BUILDTASK
|
|||
: IAssetLoader |
|||
/// <inheritdoc cref="IAssetLoader"/>
|
|||
#endif
|
|||
{ |
|||
public static class AssetLoader |
|||
{ |
|||
#if !BUILDTASK
|
|||
private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver(); |
|||
|
|||
private AssemblyDescriptor? _defaultResmAssembly; |
|||
|
|||
/// <remarks>
|
|||
/// Introduced for tests.
|
|||
/// </remarks>
|
|||
internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) => |
|||
s_assemblyDescriptorResolver = resolver; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="AssetLoader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="assembly">
|
|||
/// The default assembly from which to load resm: assets for which no assembly is specified.
|
|||
/// </param>
|
|||
public AssetLoader(Assembly? assembly = null) |
|||
{ |
|||
if (assembly == null) |
|||
assembly = Assembly.GetEntryAssembly(); |
|||
if (assembly != null) |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the default assembly from which to load assets for which no assembly is specified.
|
|||
/// </summary>
|
|||
/// <param name="assembly">The default assembly.</param>
|
|||
public void SetDefaultAssembly(Assembly assembly) |
|||
{ |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks if an asset with the specified URI exists.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>True if the asset could be found; otherwise false.</returns>
|
|||
public bool Exists(Uri uri, Uri? baseUri = null) |
|||
{ |
|||
return TryGetAsset(uri, baseUri, out _); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>A stream containing the asset contents.</returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI and returns the asset stream and the
|
|||
/// assembly containing the asset.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The stream containing the resource contents together with the assembly.
|
|||
/// </returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null) |
|||
{ |
|||
if (TryGetAsset(uri, baseUri, out var assetDescriptor)) |
|||
{ |
|||
return (assetDescriptor.GetStream(), assetDescriptor.Assembly); |
|||
} |
|||
|
|||
throw new FileNotFoundException($"The resource {uri} could not be found."); |
|||
} |
|||
|
|||
public Assembly? GetAssembly(Uri uri, Uri? baseUri) |
|||
{ |
|||
if (!uri.IsAbsoluteUri && baseUri != null) |
|||
{ |
|||
uri = new Uri(baseUri, uri); |
|||
} |
|||
|
|||
if (TryGetAssembly(uri, out var assemblyDescriptor)) |
|||
{ |
|||
return assemblyDescriptor.Assembly; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all assets of a folder and subfolders that match specified uri.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
|
|||
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
|
|||
public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri) |
|||
{ |
|||
if (uri.IsAbsoluteResm()) |
|||
{ |
|||
if (!TryGetAssembly(uri, out var assembly)) |
|||
{ |
|||
assembly = _defaultResmAssembly; |
|||
} |
|||
|
|||
return assembly?.Resources? |
|||
.Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath())) |
|||
.Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ?? |
|||
Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
uri = uri.EnsureAbsolute(baseUri); |
|||
|
|||
if (uri.IsAvares()) |
|||
{ |
|||
if (!TryGetResAsmAndPath(uri, out var assembly, out var path)) |
|||
{ |
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
private static IAssetLoader GetAssetLoader() => AvaloniaLocator.Current.GetRequiredService<IAssetLoader>(); |
|||
|
|||
if (assembly?.AvaloniaResources == null) |
|||
{ |
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
/// <inheritdoc cref="IAssetLoader.SetDefaultAssembly"/>
|
|||
public static void SetDefaultAssembly(Assembly assembly) => GetAssetLoader().SetDefaultAssembly(assembly); |
|||
|
|||
if (path.Length > 0 && path[path.Length - 1] != '/') |
|||
{ |
|||
path += '/'; |
|||
} |
|||
/// <inheritdoc cref="IAssetLoader.Exists"/>
|
|||
public static bool Exists(Uri uri, Uri? baseUri = null) => GetAssetLoader().Exists(uri, baseUri); |
|||
|
|||
return assembly.AvaloniaResources |
|||
.Where(r => r.Key.StartsWith(path, StringComparison.Ordinal)) |
|||
.Select(x => new Uri($"avares://{assembly.Name}{x.Key}")); |
|||
} |
|||
/// <inheritdoc cref="IAssetLoader.Open"/>
|
|||
public static Stream Open(Uri uri, Uri? baseUri = null) => GetAssetLoader().Open(uri, baseUri); |
|||
|
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
/// <inheritdoc cref="IAssetLoader.OpenAndGetAssembly"/>
|
|||
public static (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null) |
|||
=> GetAssetLoader().OpenAndGetAssembly(uri, baseUri); |
|||
|
|||
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor) |
|||
{ |
|||
assetDescriptor = null; |
|||
/// <inheritdoc cref="IAssetLoader.GetAssembly"/>
|
|||
public static Assembly? GetAssembly(Uri uri, Uri? baseUri = null) |
|||
=> GetAssetLoader().GetAssembly(uri, baseUri); |
|||
|
|||
if (uri.IsAbsoluteResm()) |
|||
{ |
|||
if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly)) |
|||
{ |
|||
assembly = _defaultResmAssembly; |
|||
} |
|||
|
|||
if (assembly?.Resources != null) |
|||
{ |
|||
var resourceKey = uri.AbsolutePath; |
|||
|
|||
if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
uri = uri.EnsureAbsolute(baseUri); |
|||
|
|||
if (uri.IsAvares()) |
|||
{ |
|||
if (TryGetResAsmAndPath(uri, out var assembly, out var path)) |
|||
{ |
|||
if (assembly.AvaloniaResources == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path) |
|||
{ |
|||
path = uri.GetUnescapeAbsolutePath(); |
|||
|
|||
if (TryLoadAssembly(uri.Authority, out assembly)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly) |
|||
{ |
|||
assembly = null; |
|||
|
|||
if (uri != null) |
|||
{ |
|||
if (!uri.IsAbsoluteUri) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (uri.IsResm()) |
|||
{ |
|||
var assemblyName = uri.GetAssemblyNameFromQuery(); |
|||
|
|||
if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly) |
|||
{ |
|||
assembly = null; |
|||
|
|||
try |
|||
{ |
|||
assembly = s_assemblyDescriptorResolver.GetAssembly(assemblyName); |
|||
|
|||
return true; |
|||
} |
|||
catch (Exception) { } |
|||
|
|||
return false; |
|||
} |
|||
/// <inheritdoc cref="IAssetLoader.GetAssets"/>
|
|||
public static IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri) |
|||
=> GetAssetLoader().GetAssets(uri, baseUri); |
|||
#endif
|
|||
|
|||
public static void RegisterResUriParsers() |
|||
{ |
|||
if (!UriParser.IsKnownScheme("avares")) |
|||
UriParser.Register(new GenericUriParser( |
|||
GenericUriParserOptions.GenericAuthority | |
|||
GenericUriParserOptions.NoUserInfo | |
|||
GenericUriParserOptions.NoPort | |
|||
GenericUriParserOptions.NoQuery | |
|||
GenericUriParserOptions.NoFragment), "avares", -1); |
|||
} |
|||
internal static void RegisterResUriParsers() |
|||
{ |
|||
if (!UriParser.IsKnownScheme("avares")) |
|||
UriParser.Register(new GenericUriParser( |
|||
GenericUriParserOptions.GenericAuthority | |
|||
GenericUriParserOptions.NoUserInfo | |
|||
GenericUriParserOptions.NoPort | |
|||
GenericUriParserOptions.NoQuery | |
|||
GenericUriParserOptions.NoFragment), "avares", -1); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,255 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Avalonia.Platform.Internal; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Platform; |
|||
|
|||
/// <summary>
|
|||
/// Loads assets compiled into the application binary.
|
|||
/// </summary>
|
|||
internal class StandardAssetLoader : IAssetLoader |
|||
{ |
|||
private readonly IAssemblyDescriptorResolver _assemblyDescriptorResolver; |
|||
private AssemblyDescriptor? _defaultResmAssembly; |
|||
|
|||
public StandardAssetLoader(IAssemblyDescriptorResolver resolver, Assembly? assembly = null) |
|||
{ |
|||
if (assembly == null) |
|||
assembly = Assembly.GetEntryAssembly(); |
|||
if (assembly != null) |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
_assemblyDescriptorResolver = resolver; |
|||
} |
|||
|
|||
public StandardAssetLoader(Assembly? assembly = null) : this(new AssemblyDescriptorResolver(), assembly) |
|||
{ |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the default assembly from which to load assets for which no assembly is specified.
|
|||
/// </summary>
|
|||
/// <param name="assembly">The default assembly.</param>
|
|||
public void SetDefaultAssembly(Assembly assembly) |
|||
{ |
|||
_defaultResmAssembly = new AssemblyDescriptor(assembly); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks if an asset with the specified URI exists.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>True if the asset could be found; otherwise false.</returns>
|
|||
public bool Exists(Uri uri, Uri? baseUri = null) |
|||
{ |
|||
return TryGetAsset(uri, baseUri, out _); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>A stream containing the asset contents.</returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public Stream Open(Uri uri, Uri? baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; |
|||
|
|||
/// <summary>
|
|||
/// Opens the asset with the requested URI and returns the asset stream and the
|
|||
/// assembly containing the asset.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">
|
|||
/// A base URI to use if <paramref name="uri"/> is relative.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The stream containing the resource contents together with the assembly.
|
|||
/// </returns>
|
|||
/// <exception cref="FileNotFoundException">
|
|||
/// The asset could not be found.
|
|||
/// </exception>
|
|||
public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri? baseUri = null) |
|||
{ |
|||
if (TryGetAsset(uri, baseUri, out var assetDescriptor)) |
|||
{ |
|||
return (assetDescriptor.GetStream(), assetDescriptor.Assembly); |
|||
} |
|||
|
|||
throw new FileNotFoundException($"The resource {uri} could not be found."); |
|||
} |
|||
|
|||
public Assembly? GetAssembly(Uri uri, Uri? baseUri) |
|||
{ |
|||
if (!uri.IsAbsoluteUri && baseUri != null) |
|||
{ |
|||
uri = new Uri(baseUri, uri); |
|||
} |
|||
|
|||
if (TryGetAssembly(uri, out var assemblyDescriptor)) |
|||
{ |
|||
return assemblyDescriptor.Assembly; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets all assets of a folder and subfolders that match specified uri.
|
|||
/// </summary>
|
|||
/// <param name="uri">The URI.</param>
|
|||
/// <param name="baseUri">Base URI that is used if <paramref name="uri"/> is relative.</param>
|
|||
/// <returns>All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset</returns>
|
|||
public IEnumerable<Uri> GetAssets(Uri uri, Uri? baseUri) |
|||
{ |
|||
if (uri.IsAbsoluteResm()) |
|||
{ |
|||
if (!TryGetAssembly(uri, out var assembly)) |
|||
{ |
|||
assembly = _defaultResmAssembly; |
|||
} |
|||
|
|||
return assembly?.Resources? |
|||
.Where(x => x.Key.Contains(uri.GetUnescapeAbsolutePath())) |
|||
.Select(x => new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ?? |
|||
Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
uri = uri.EnsureAbsolute(baseUri); |
|||
|
|||
if (uri.IsAvares()) |
|||
{ |
|||
if (!TryGetResAsmAndPath(uri, out var assembly, out var path)) |
|||
{ |
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
if (assembly?.AvaloniaResources == null) |
|||
{ |
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
if (path.Length > 0 && path[path.Length - 1] != '/') |
|||
{ |
|||
path += '/'; |
|||
} |
|||
|
|||
return assembly.AvaloniaResources |
|||
.Where(r => r.Key.StartsWith(path, StringComparison.Ordinal)) |
|||
.Select(x => new Uri($"avares://{assembly.Name}{x.Key}")); |
|||
} |
|||
|
|||
return Enumerable.Empty<Uri>(); |
|||
} |
|||
|
|||
private bool TryGetAsset(Uri uri, Uri? baseUri, [NotNullWhen(true)] out IAssetDescriptor? assetDescriptor) |
|||
{ |
|||
assetDescriptor = null; |
|||
|
|||
if (uri.IsAbsoluteResm()) |
|||
{ |
|||
if (!TryGetAssembly(uri, out var assembly) && !TryGetAssembly(baseUri, out assembly)) |
|||
{ |
|||
assembly = _defaultResmAssembly; |
|||
} |
|||
|
|||
if (assembly?.Resources != null) |
|||
{ |
|||
var resourceKey = uri.AbsolutePath; |
|||
|
|||
if (assembly.Resources.TryGetValue(resourceKey, out assetDescriptor)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
uri = uri.EnsureAbsolute(baseUri); |
|||
|
|||
if (uri.IsAvares()) |
|||
{ |
|||
if (TryGetResAsmAndPath(uri, out var assembly, out var path)) |
|||
{ |
|||
if (assembly.AvaloniaResources == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (assembly.AvaloniaResources.TryGetValue(path, out assetDescriptor)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool TryGetResAsmAndPath(Uri uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly, out string path) |
|||
{ |
|||
path = uri.GetUnescapeAbsolutePath(); |
|||
|
|||
if (TryLoadAssembly(uri.Authority, out assembly)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool TryGetAssembly(Uri? uri, [NotNullWhen(true)] out IAssemblyDescriptor? assembly) |
|||
{ |
|||
assembly = null; |
|||
|
|||
if (uri != null) |
|||
{ |
|||
if (!uri.IsAbsoluteUri) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (uri.IsAvares() && TryGetResAsmAndPath(uri, out assembly, out _)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (uri.IsResm()) |
|||
{ |
|||
var assemblyName = uri.GetAssemblyNameFromQuery(); |
|||
|
|||
if (assemblyName.Length > 0 && TryLoadAssembly(assemblyName, out assembly)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool TryLoadAssembly(string assemblyName, [NotNullWhen(true)] out IAssemblyDescriptor? assembly) |
|||
{ |
|||
assembly = null; |
|||
|
|||
try |
|||
{ |
|||
assembly = _assemblyDescriptorResolver.GetAssembly(assemblyName); |
|||
|
|||
return true; |
|||
} |
|||
catch (Exception) { } |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue