committed by
GitHub
131 changed files with 2070 additions and 2324 deletions
File diff suppressed because it is too large
@ -1,4 +1,5 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"> |
|||
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application> |
|||
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application> |
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
|||
</manifest> |
|||
|
|||
@ -0,0 +1 @@ |
|||
DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend |
|||
@ -0,0 +1 @@ |
|||
DOTNET_DiagnosticPorts=10.0.2.2:9001,suspend |
|||
@ -1,37 +0,0 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Animation; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Media |
|||
{ |
|||
public class GeometryCollection : Animatable, IList<Geometry>, IReadOnlyList<Geometry> |
|||
{ |
|||
private List<Geometry> _inner; |
|||
|
|||
public GeometryCollection() => _inner = new List<Geometry>(); |
|||
public GeometryCollection(IEnumerable<Geometry> collection) => _inner = new List<Geometry>(collection); |
|||
public GeometryCollection(int capacity) => _inner = new List<Geometry>(capacity); |
|||
|
|||
public Geometry this[int index] |
|||
{ |
|||
get => _inner[index]; |
|||
set => _inner[index] = value; |
|||
} |
|||
|
|||
public int Count => _inner.Count; |
|||
public bool IsReadOnly => false; |
|||
|
|||
public void Add(Geometry item) => _inner.Add(item); |
|||
public void Clear() => _inner.Clear(); |
|||
public bool Contains(Geometry item) => _inner.Contains(item); |
|||
public void CopyTo(Geometry[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex); |
|||
public IEnumerator<Geometry> GetEnumerator() => _inner.GetEnumerator(); |
|||
public int IndexOf(Geometry item) => _inner.IndexOf(item); |
|||
public void Insert(int index, Geometry item) => _inner.Insert(index, item); |
|||
public bool Remove(Geometry item) => _inner.Remove(item); |
|||
public void RemoveAt(int index) => _inner.RemoveAt(index); |
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.Collections; |
|||
|
|||
namespace Avalonia.Media |
|||
{ |
|||
public sealed class DrawingCollection : AvaloniaList<Drawing> |
|||
{ |
|||
public DrawingCollection() |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
} |
|||
|
|||
public DrawingCollection(IEnumerable<Drawing> items) : base(items) |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Collections; |
|||
|
|||
#nullable enable |
|||
|
|||
namespace Avalonia.Media |
|||
{ |
|||
public sealed class GeometryCollection : AvaloniaList<Geometry> |
|||
{ |
|||
public GeometryCollection() |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
|
|||
this.ForEachItem( |
|||
x => |
|||
{ |
|||
Parent?.Invalidate(); |
|||
}, |
|||
x => |
|||
{ |
|||
Parent?.Invalidate(); |
|||
}, |
|||
() => throw new NotSupportedException()); |
|||
} |
|||
|
|||
public GeometryCollection(IEnumerable<Geometry> items) : base(items) |
|||
{ |
|||
ResetBehavior = ResetBehavior.Remove; |
|||
|
|||
this.ForEachItem( |
|||
x => |
|||
{ |
|||
Parent?.Invalidate(); |
|||
}, |
|||
x => |
|||
{ |
|||
Parent?.Invalidate(); |
|||
}, |
|||
() => throw new NotSupportedException()); |
|||
} |
|||
|
|||
public GeometryGroup? Parent { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Media |
|||
{ |
|||
internal class PlatformGeometry : Geometry |
|||
{ |
|||
private readonly IGeometryImpl _geometryImpl; |
|||
|
|||
public PlatformGeometry(IGeometryImpl geometryImpl) |
|||
{ |
|||
_geometryImpl = geometryImpl; |
|||
} |
|||
|
|||
public override Geometry Clone() |
|||
{ |
|||
return new PlatformGeometry(_geometryImpl); |
|||
} |
|||
|
|||
protected override IGeometryImpl? CreateDefiningGeometry() |
|||
{ |
|||
return _geometryImpl; |
|||
} |
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace Avalonia.PlatformSupport.Internal; |
|||
namespace Avalonia.Platform.Internal; |
|||
|
|||
internal static class Constants |
|||
{ |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace Avalonia.PlatformSupport.Internal; |
|||
namespace Avalonia.Platform.Internal; |
|||
|
|||
internal class SlicedStream : Stream |
|||
{ |
|||
@ -0,0 +1,155 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
|
|||
namespace Avalonia.Platform.Internal; |
|||
|
|||
internal class UnmanagedBlob : IUnmanagedBlob |
|||
{ |
|||
private IntPtr _address; |
|||
private readonly object _lock = new object(); |
|||
#if DEBUG
|
|||
private static readonly List<string> Backtraces = new List<string>(); |
|||
private static Thread? GCThread; |
|||
private readonly string _backtrace; |
|||
private static readonly object _btlock = new object(); |
|||
|
|||
class GCThreadDetector |
|||
{ |
|||
~GCThreadDetector() |
|||
{ |
|||
GCThread = Thread.CurrentThread; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
static void Spawn() => new GCThreadDetector(); |
|||
|
|||
static UnmanagedBlob() |
|||
{ |
|||
Spawn(); |
|||
GC.WaitForPendingFinalizers(); |
|||
} |
|||
#endif
|
|||
|
|||
public UnmanagedBlob(int size) |
|||
{ |
|||
try |
|||
{ |
|||
if (size <= 0) |
|||
throw new ArgumentException("Positive number required", nameof(size)); |
|||
_address = Alloc(size); |
|||
GC.AddMemoryPressure(size); |
|||
Size = size; |
|||
} |
|||
catch |
|||
{ |
|||
GC.SuppressFinalize(this); |
|||
throw; |
|||
} |
|||
#if DEBUG
|
|||
_backtrace = Environment.StackTrace; |
|||
lock (_btlock) |
|||
Backtraces.Add(_backtrace); |
|||
#endif
|
|||
} |
|||
|
|||
void DoDispose() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
#if DEBUG
|
|||
lock (_btlock) |
|||
Backtraces.Remove(_backtrace); |
|||
#endif
|
|||
Free(_address, Size); |
|||
GC.RemoveMemoryPressure(Size); |
|||
IsDisposed = true; |
|||
_address = IntPtr.Zero; |
|||
Size = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
#if DEBUG
|
|||
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " |
|||
+ Environment.StackTrace |
|||
+ "\n\nBlob created by " + _backtrace); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
DoDispose(); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~UnmanagedBlob() |
|||
{ |
|||
#if DEBUG
|
|||
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); |
|||
#endif
|
|||
DoDispose(); |
|||
} |
|||
|
|||
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; |
|||
public int Size { get; private set; } |
|||
public bool IsDisposed { get; private set; } |
|||
|
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern int munmap(IntPtr addr, IntPtr length); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern long sysconf(int name); |
|||
|
|||
private bool? _useMmap; |
|||
private bool UseMmap |
|||
=> _useMmap ?? ((_useMmap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value); |
|||
|
|||
// Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available.
|
|||
private IntPtr Alloc(int size) |
|||
{ |
|||
if (!UseMmap) |
|||
{ |
|||
return Marshal.AllocHGlobal(size); |
|||
} |
|||
else |
|||
{ |
|||
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); |
|||
if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to allocate memory: " + errno); |
|||
} |
|||
return rv; |
|||
} |
|||
} |
|||
|
|||
private void Free(IntPtr ptr, int len) |
|||
{ |
|||
if (!UseMmap) |
|||
{ |
|||
Marshal.FreeHGlobal(ptr); |
|||
} |
|||
else |
|||
{ |
|||
if (munmap(ptr, new IntPtr(len)) == -1) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to free memory: " + errno); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Avalonia.Platform.Internal; |
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
public class StandardRuntimePlatform : IRuntimePlatform |
|||
{ |
|||
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
|||
{ |
|||
return new Timer(_ => tick(), null, interval, interval); |
|||
} |
|||
|
|||
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); |
|||
|
|||
private static readonly Lazy<RuntimePlatformInfo> Info = new(() => |
|||
{ |
|||
OperatingSystemType os; |
|||
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
|||
os = OperatingSystemType.OSX; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|||
os = OperatingSystemType.Linux; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
os = OperatingSystemType.WinNT; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android"))) |
|||
os = OperatingSystemType.Android; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS"))) |
|||
os = OperatingSystemType.iOS; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser"))) |
|||
os = OperatingSystemType.Browser; |
|||
else |
|||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|||
|
|||
// Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
|
|||
var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); |
|||
var isMonoRuntime = Type.GetType("Mono.Runtime") != null; |
|||
var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); |
|||
|
|||
return new RuntimePlatformInfo |
|||
{ |
|||
IsCoreClr = isCoreClr, |
|||
IsDotNetFramework = isFramework, |
|||
IsMono = isMonoRuntime, |
|||
|
|||
IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT, |
|||
IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS, |
|||
IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android, |
|||
IsBrowser = os == OperatingSystemType.Browser, |
|||
OperatingSystem = os, |
|||
}; |
|||
}); |
|||
|
|||
|
|||
public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; |
|||
} |
|||
} |
|||
@ -1,30 +1,33 @@ |
|||
using System.Reflection; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Platform.Internal; |
|||
using Avalonia.Platform.Interop; |
|||
|
|||
namespace Avalonia.PlatformSupport |
|||
namespace Avalonia.Platform |
|||
{ |
|||
public static class StandardRuntimePlatformServices |
|||
{ |
|||
public static void Register(Assembly? assembly = null) |
|||
{ |
|||
var standardPlatform = new StandardRuntimePlatform(); |
|||
var os = standardPlatform.GetRuntimeInfo().OperatingSystem; |
|||
|
|||
AssetLoader.RegisterResUriParsers(); |
|||
AvaloniaLocator.CurrentMutable |
|||
.Bind<IRuntimePlatform>().ToConstant(standardPlatform) |
|||
.Bind<IAssetLoader>().ToConstant(new AssetLoader(assembly)) |
|||
.Bind<IDynamicLibraryLoader>().ToConstant( |
|||
os switch |
|||
#if NET6_0_OR_GREATER
|
|||
new Net6Loader() |
|||
#else
|
|||
standardPlatform.GetRuntimeInfo().OperatingSystem switch |
|||
{ |
|||
OperatingSystemType.WinNT => new Win32Loader(), |
|||
OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(), |
|||
OperatingSystemType.OSX => new UnixLoader(), |
|||
OperatingSystemType.Linux => new UnixLoader(), |
|||
OperatingSystemType.Android => new UnixLoader(), |
|||
// iOS, WASM, ...
|
|||
_ => (IDynamicLibraryLoader)new NotSupportedLoader() |
|||
_ => new NotSupportedLoader() |
|||
} |
|||
#endif
|
|||
); |
|||
} |
|||
} |
|||
@ -1,5 +1,5 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.PlatformSupport; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia |
|||
{ |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
using Avalonia.Metadata; |
|||
|
|||
namespace Avalonia.Controls.Templates; |
|||
|
|||
public interface ITypedDataTemplate : IDataTemplate |
|||
{ |
|||
[DataType] |
|||
Type? DataType { get; } |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net6.0;net461;netstandard2.0</TargetFrameworks> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="../Avalonia.Base/Avalonia.Base.csproj" /> |
|||
<ProjectReference Include="../Avalonia.Controls/Avalonia.Controls.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" Condition="'$(TargetFramework)' == 'net461'" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\NetCore.props" /> |
|||
<Import Project="..\..\build\NetFX.props" /> |
|||
<Import Project="..\..\build\NullableEnable.props" /> |
|||
|
|||
<ItemGroup> |
|||
<InternalsVisibleTo Include="$(AssemblyName).UnitTests, PublicKey=$(AvaloniaPublicKey)" /> |
|||
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -1,218 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.PlatformSupport |
|||
{ |
|||
public class StandardRuntimePlatform : IRuntimePlatform |
|||
{ |
|||
public IDisposable StartSystemTimer(TimeSpan interval, Action tick) |
|||
{ |
|||
return new Timer(_ => tick(), null, interval, interval); |
|||
} |
|||
|
|||
public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size); |
|||
|
|||
private class UnmanagedBlob : IUnmanagedBlob |
|||
{ |
|||
private readonly StandardRuntimePlatform _plat; |
|||
private IntPtr _address; |
|||
private readonly object _lock = new object(); |
|||
#if DEBUG
|
|||
private static readonly List<string> Backtraces = new List<string>(); |
|||
private static Thread? GCThread; |
|||
private readonly string _backtrace; |
|||
private static readonly object _btlock = new object(); |
|||
|
|||
class GCThreadDetector |
|||
{ |
|||
~GCThreadDetector() |
|||
{ |
|||
GCThread = Thread.CurrentThread; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.NoInlining)] |
|||
static void Spawn() => new GCThreadDetector(); |
|||
|
|||
static UnmanagedBlob() |
|||
{ |
|||
Spawn(); |
|||
GC.WaitForPendingFinalizers(); |
|||
} |
|||
#endif
|
|||
|
|||
public UnmanagedBlob(StandardRuntimePlatform plat, int size) |
|||
{ |
|||
try |
|||
{ |
|||
if (size <= 0) |
|||
throw new ArgumentException("Positive number required", nameof(size)); |
|||
_plat = plat; |
|||
_address = plat.Alloc(size); |
|||
GC.AddMemoryPressure(size); |
|||
Size = size; |
|||
} |
|||
catch |
|||
{ |
|||
GC.SuppressFinalize(this); |
|||
throw; |
|||
} |
|||
#if DEBUG
|
|||
_backtrace = Environment.StackTrace; |
|||
lock (_btlock) |
|||
Backtraces.Add(_backtrace); |
|||
#endif
|
|||
} |
|||
|
|||
void DoDispose() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
#if DEBUG
|
|||
lock (_btlock) |
|||
Backtraces.Remove(_backtrace); |
|||
#endif
|
|||
_plat?.Free(_address, Size); |
|||
GC.RemoveMemoryPressure(Size); |
|||
IsDisposed = true; |
|||
_address = IntPtr.Zero; |
|||
Size = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
#if DEBUG
|
|||
if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (!IsDisposed) |
|||
{ |
|||
Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " |
|||
+ Environment.StackTrace |
|||
+ "\n\nBlob created by " + _backtrace); |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
DoDispose(); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~UnmanagedBlob() |
|||
{ |
|||
#if DEBUG
|
|||
Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); |
|||
#endif
|
|||
DoDispose(); |
|||
} |
|||
|
|||
public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; |
|||
public int Size { get; private set; } |
|||
public bool IsDisposed { get; private set; } |
|||
} |
|||
|
|||
#if NET461 || NETCOREAPP2_0_OR_GREATER
|
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern int munmap(IntPtr addr, IntPtr length); |
|||
[DllImport("libc", SetLastError = true)] |
|||
private static extern long sysconf(int name); |
|||
|
|||
private bool? _useMmap; |
|||
private bool UseMmap |
|||
=> _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value; |
|||
|
|||
IntPtr Alloc(int size) |
|||
{ |
|||
if (UseMmap) |
|||
{ |
|||
var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); |
|||
if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to allocate memory: " + errno); |
|||
} |
|||
return rv; |
|||
} |
|||
else |
|||
return Marshal.AllocHGlobal(size); |
|||
} |
|||
|
|||
void Free(IntPtr ptr, int len) |
|||
{ |
|||
if (UseMmap) |
|||
{ |
|||
if (munmap(ptr, new IntPtr(len)) == -1) |
|||
{ |
|||
var errno = Marshal.GetLastWin32Error(); |
|||
throw new Exception("Unable to free memory: " + errno); |
|||
} |
|||
} |
|||
else |
|||
Marshal.FreeHGlobal(ptr); |
|||
} |
|||
#else
|
|||
IntPtr Alloc(int size) => Marshal.AllocHGlobal(size); |
|||
void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr); |
|||
#endif
|
|||
|
|||
private static readonly Lazy<RuntimePlatformInfo> Info = new Lazy<RuntimePlatformInfo>(() => |
|||
{ |
|||
OperatingSystemType os; |
|||
|
|||
#if NET5_0_OR_GREATER
|
|||
if (OperatingSystem.IsWindows()) |
|||
os = OperatingSystemType.WinNT; |
|||
else if (OperatingSystem.IsMacOS()) |
|||
os = OperatingSystemType.OSX; |
|||
else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) |
|||
os = OperatingSystemType.Linux; |
|||
else if (OperatingSystem.IsAndroid()) |
|||
os = OperatingSystemType.Android; |
|||
else if (OperatingSystem.IsIOS()) |
|||
os = OperatingSystemType.iOS; |
|||
else if (OperatingSystem.IsBrowser()) |
|||
os = OperatingSystemType.Browser; |
|||
else |
|||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|||
#else
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) |
|||
os = OperatingSystemType.OSX; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|||
os = OperatingSystemType.Linux; |
|||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
os = OperatingSystemType.WinNT; |
|||
else |
|||
throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); |
|||
#endif
|
|||
|
|||
return new RuntimePlatformInfo |
|||
{ |
|||
#if NETCOREAPP
|
|||
IsCoreClr = true, |
|||
#elif NETFRAMEWORK
|
|||
IsDotNetFramework = true, |
|||
#endif
|
|||
IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT, |
|||
IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser, |
|||
IsMobile = os == OperatingSystemType.Android || os == OperatingSystemType.iOS, |
|||
IsUnix = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.Android, |
|||
IsBrowser = os == OperatingSystemType.Browser, |
|||
OperatingSystem = os, |
|||
}; |
|||
}); |
|||
|
|||
|
|||
public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using XamlX; |
|||
using XamlX.Ast; |
|||
using XamlX.Transform; |
|||
using XamlX.Transform.Transformers; |
|||
using XamlX.TypeSystem; |
|||
|
|||
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers |
|||
{ |
|||
internal class XDataTypeTransformer : IXamlAstTransformer |
|||
{ |
|||
private const string DataTypePropertyName = "DataType"; |
|||
|
|||
/// <summary>
|
|||
/// Converts x:DataType directives to regular DataType assignments if property with Avalonia.Metadata.DataTypeAttribute exists.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
|||
{ |
|||
if (node is XamlAstObjectNode on) |
|||
{ |
|||
for (var c = 0; c < on.Children.Count; c++) |
|||
{ |
|||
var ch = on.Children[c]; |
|||
if (ch is XamlAstXmlDirective { Namespace: XamlNamespaces.Xaml2006, Name: DataTypePropertyName } d) |
|||
{ |
|||
if (on.Children.OfType<XamlAstXamlPropertyValueNode>() |
|||
.Any(p => ((XamlAstNamePropertyReference)p.Property)?.Name == DataTypePropertyName)) |
|||
{ |
|||
// Break iteration if any DataType property was already set by user code.
|
|||
break; |
|||
} |
|||
|
|||
var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; |
|||
|
|||
var clrType = (on.Type as XamlAstClrTypeReference)?.Type; |
|||
if (clrType is null) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
// Technically it's possible to map "x:DataType" to a property with [DataType] attribute regardless of its name,
|
|||
// but we go explicitly strict here and check the name as well.
|
|||
var (declaringType, dataTypeProperty) = GetAllProperties(clrType) |
|||
.FirstOrDefault(t => t.property.Name == DataTypePropertyName && t.property.CustomAttributes |
|||
.Any(a => a.Type == templateDataTypeAttribute)); |
|||
|
|||
if (dataTypeProperty is not null) |
|||
{ |
|||
on.Children[c] = new XamlAstXamlPropertyValueNode(d, |
|||
new XamlAstNamePropertyReference(d, |
|||
new XamlAstClrTypeReference(ch, declaringType, false), dataTypeProperty.Name, |
|||
on.Type), |
|||
d.Values); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return node; |
|||
} |
|||
|
|||
private static IEnumerable<(IXamlType declaringType, IXamlProperty property)> GetAllProperties(IXamlType t) |
|||
{ |
|||
foreach (var p in t.Properties) |
|||
yield return (t, p); |
|||
if(t.BaseType!=null) |
|||
foreach (var tuple in GetAllProperties(t.BaseType)) |
|||
yield return tuple; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue