增加一些反射帮助类

This commit is contained in:
polarboy 2024-06-24 11:07:37 +08:00
parent 1c3bb2c837
commit dd9b6a046c
12 changed files with 512 additions and 12 deletions

View File

@ -0,0 +1,259 @@
using System.Reflection;
using System.Text;
namespace AtomUI.Reflection;
public static class ObjectExtension
{
public static bool TryGetProperty<T>(
this object source,
string name,
out T? result,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
PropertyInfo? property = source.GetType().GetProperty(name, flags);
if (property is not null && property.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static bool TryGetProperty<T>(
this object source,
Type declareType,
string name,
out T? result,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
PropertyInfo? property = declareType.GetProperty(name, flags);
if (property is not null && property.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static bool TryGetProperty<T>(this object source, PropertyInfo info, out T? result)
{
if (info.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static T? GetPropertyOrThrow<T>(this object source, string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
object? obj = source.GetType().GetPropertyInfoOrThrow(name, flags).GetValue(source);
if (obj is T propertyOrThrow) {
return propertyOrThrow;
}
if (obj == null) {
if (typeof(T).IsValueType) {
throw new Exception(name + " is a value type but the value is null.");
}
return default;
}
var stringBuilder = new StringBuilder(30, 3);
stringBuilder.Append(name);
stringBuilder.Append("'s type is ");
stringBuilder.Append(source.GetType().Name);
stringBuilder.Append(" but the value is ");
stringBuilder.Append(obj.GetType().Name);
stringBuilder.Append(".");
throw new Exception(stringBuilder.ToString());
}
public static T? GetPropertyOrThrow<T>(
this object source,
Type declareType,
string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
object? obj = declareType.GetPropertyInfoOrThrow(name, flags).GetValue(source);
if (obj is T propertyOrThrow) {
return propertyOrThrow;
}
if (obj == null) {
if (typeof(T).IsValueType) {
throw new Exception(name + " is a value type but the value is null.");
}
return default;
}
var stringHandler = new StringBuilder(30, 3);
stringHandler.Append(name);
stringHandler.Append("'s type is ");
stringHandler.Append(declareType.Name);
stringHandler.Append(" but the value is ");
stringHandler.Append(obj.GetType().Name);
stringHandler.Append(".");
throw new Exception(stringHandler.ToString());
}
public static bool TrySetProperty<T>(
this object source,
Type declareType,
string name,
T? value,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
PropertyInfo? property = declareType.GetProperty(name, flags);
if (property is null) {
return false;
}
property.SetValue(source, value);
return true;
}
public static bool TryGetField<T>(
this object source,
string name,
out T? result,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
FieldInfo? field = source.GetType().GetField(name, flags);
if (field is not null && field.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static bool TryGetField<T>(
this object source,
Type declareType,
string name,
out T? result,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
FieldInfo? field = declareType.GetField(name, flags);
if (field is not null && field.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static bool TryGetField<T>(this object source, FieldInfo info, out T? result)
{
if (info.GetValue(source) is T obj) {
result = obj;
return true;
}
result = default;
return false;
}
public static T? GetFieldOrThrow<T>(this object source, string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
object? obj = source.GetType().GetFieldInfoOrThrow(name, flags).GetValue(source);
if (obj is T fieldOrThrow) {
return fieldOrThrow;
}
if (obj == null) {
if (typeof(T).IsValueType) {
throw new Exception(name + " is a value type but the value is null.");
}
return default;
}
var stringBuilder = new StringBuilder(30, 3);
stringBuilder.Append(name);
stringBuilder.Append("'s type is ");
stringBuilder.Append(source.GetType().Name);
stringBuilder.Append(" but the value is ");
stringBuilder.Append(obj.GetType().Name);
stringBuilder.Append(".");
throw new Exception(stringBuilder.ToString());
}
public static T? GetFieldOrThrow<T>(
this object source,
Type declareType,
string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
object? obj = declareType.GetFieldInfoOrThrow(name, flags).GetValue(source);
if (obj is T fieldOrThrow) return fieldOrThrow;
if (obj == null) {
if (typeof(T).IsValueType) {
throw new Exception(name + " is a value type but the value is null.");
}
return default;
}
var stringBuilder = new StringBuilder(30, 3);
stringBuilder.Append(name);
stringBuilder.Append("'s type is ");
stringBuilder.Append(declareType);
stringBuilder.Append(" but the value is ");
stringBuilder.Append(obj.GetType().Name);
stringBuilder.Append(".");
throw new Exception(stringBuilder.ToString());
}
public static bool TrySetField<T>(
this object source,
Type declareType,
string name,
T? value,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
FieldInfo? field = declareType.GetField(name, flags);
if (field is null) {
return false;
}
field.SetValue(source, value);
return true;
}
public static bool TryInvokeMethod(
this object source,
Type declareType,
string name,
out object? result,
params object[] parameters)
{
MethodInfo? method = declareType.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic);
if (method is null) {
result = null;
return false;
}
result = method.Invoke(source, parameters);
return true;
}
public static object? InvokeMethodOrThrow(
this object source,
Type declareType,
string name,
params object[] parameters)
{
return declareType.GetMethodInfoOrThrow(name).Invoke(source, parameters);
}
}

View File

@ -0,0 +1,73 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace AtomUI.Reflection;
public static class TypeExtension
{
public static bool TryGetPropertyInfo(
this Type type,
string name,
[NotNullWhen(true)] out PropertyInfo? info,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
info = type.GetProperty(name, flags);
return info is not null;
}
public static bool TryGetFieldInfo(
this Type type,
string name,
[NotNullWhen(true)] out FieldInfo? info,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
info = type.GetField(name, flags);
return info is not null;
}
public static bool TryGetMethodInfo(
this Type type,
string name,
[NotNullWhen(true)] out MethodInfo? info,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
info = type.GetMethod(name, flags);
return info is not null;
}
public static PropertyInfo GetPropertyInfoOrThrow(
this Type type,
string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
PropertyInfo? info;
if (!type.TryGetPropertyInfo(name, out info, flags)) {
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
}
return info;
}
public static FieldInfo GetFieldInfoOrThrow(this Type type, string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
FieldInfo? info;
if (!type.TryGetFieldInfo(name, out info, flags)) {
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
}
return info;
}
public static MethodInfo GetMethodInfoOrThrow(this Type type, string name,
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic)
{
MethodInfo? info;
if (!type.TryGetMethodInfo(name, out info, flags)) {
throw new NotSupportedException($"Can not find the '{name}' from type '{type}'. We can not reflect it.");
}
return info;
}
}

View File

@ -168,7 +168,7 @@
public const string TagBorderlessBg = "TagBorderlessBg";
}
public static class TooltipResourceKey
public static class ToolTipResourceKey
{
public const string TooltipMaxWidth = "TooltipMaxWidth";
public const string TooltipColor = "TooltipColor";

View File

@ -2,7 +2,7 @@
using AvaloniaTooltip = Avalonia.Controls.ToolTip;
public partial class Tooltip : AvaloniaTooltip
public partial class ToolTip : AvaloniaTooltip
{
}

View File

@ -1,6 +1,6 @@
namespace AtomUI.Controls.Tooltip;
public partial class Tooltip
public partial class ToolTip
{
}

View File

@ -0,0 +1,8 @@
using Avalonia.Controls;
using Avalonia.Threading;
namespace AtomUI.Controls.Tooltip;
public class ToolTipService
{
}

View File

@ -1,6 +1,6 @@
namespace AtomUI.Controls.Tooltip;
public class TooltipService
public partial class ToolTip
{
}

View File

@ -4,11 +4,11 @@ using Avalonia.Media;
namespace AtomUI.Controls.Tooltip;
[ControlDesignToken]
internal class TooltipToken : AbstractControlDesignToken
internal class ToolTipToken : AbstractControlDesignToken
{
public const string ID = "Tooltip";
public TooltipToken()
public ToolTipToken()
: base(ID)
{
}

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls.Tooltip;
public partial class Tooltip
{
}

View File

@ -0,0 +1,14 @@
using Avalonia.Controls;
using Avalonia.Media;
namespace AtomUI.Controls.Utils;
public interface IShadowLayer
{
public void AttachToTarget(Control control);
public void SetShadowMaskGeometry(Geometry geometry);
public void SetShadows(BoxShadows shadows);
public void ShowShadows();
public void HideShadows();
}

View File

@ -0,0 +1,38 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace AtomUI.Controls.Utils;
public sealed class ShadowLayer : AvaloniaObject, IShadowLayer
{
public void AttachToTarget(Control control)
{
}
public void SetShadowMaskGeometry(Geometry geometry)
{
}
public void SetShadows(BoxShadows shadows)
{
}
public void ShowShadows()
{
}
public void HideShadows()
{
}
private void CreateShadowRenderControl()
{
}
}

View File

@ -0,0 +1,114 @@
using System.Reactive.Linq;
using System.Reflection;
using AtomUI.Reflection;
using Avalonia;
using Avalonia.Input;
using Avalonia.Input.Raw;
namespace AtomUI.Input;
/// <summary>
/// See <see cref="InputManager"/>.
/// </summary>
internal static class InputManagerEx
{
private static readonly PropertyInfo RootInfo;
private static readonly PropertyInfo KeyInfo;
private static readonly PropertyInfo ModifiersInfo;
private static readonly PropertyInfo PositionInfo;
private static readonly PropertyInfo TypeInfo;
static InputManagerEx()
{
// Input
RootInfo = typeof(RawInputEventArgs).GetPropertyInfoOrThrow("Root", BindingFlags.Public | BindingFlags.Instance);
// Key
KeyInfo = typeof(RawKeyEventArgs).GetPropertyInfoOrThrow("Key", BindingFlags.Public | BindingFlags.Instance);
ModifiersInfo = typeof(RawKeyEventArgs).GetPropertyInfoOrThrow("Modifiers", BindingFlags.Public | BindingFlags.Instance);
TypeInfo = typeof(RawKeyEventArgs).GetPropertyInfoOrThrow("Type", BindingFlags.Public | BindingFlags.Instance);
// Pointer
PositionInfo = typeof(RawPointerEventArgs).GetPropertyInfoOrThrow("Position", BindingFlags.Public | BindingFlags.Instance);
}
public static IInputRoot? Root(this RawInputEventArgs e) => RootInfo.GetValue(e) as IInputRoot;
public static Key Key(this RawKeyEventArgs e) => (Key)KeyInfo.GetValue(e)!;
public static RawInputModifiers Modifiers(this RawKeyEventArgs e) => (RawInputModifiers)ModifiersInfo.GetValue(e)!;
public static Point Position(this RawPointerEventArgs e) => (Point)PositionInfo.GetValue(e)!;
public static RawKeyEventType Type(this RawKeyEventArgs e) => (RawKeyEventType)TypeInfo.GetValue(e)!;
private static IObservable<TInputArgs> GetRawInputEventObservable<TInputArgs>(string processName)
where TInputArgs : RawInputEventArgs
{
// Avalonia.Base
var assembly = Assembly.GetAssembly(typeof(RawInputEventArgs));
// InputManager Type
var type = assembly?.GetType("Avalonia.Input.InputManager")
?? throw new NotSupportedException($"Can not find the type InputManager in assembly {assembly}.");
// InputManager.Instance
var manager = type.GetPropertyOrThrow<IInputManager>(type, "Instance", BindingFlags.Public | BindingFlags.Static)
?? throw new NotSupportedException($"Can not find the 'Instance' in type of InputManager.");
// InputManager.PreProcess | InputManager.Process | InputManager.PostProcess
var process = manager.GetPropertyOrThrow<IObservable<RawInputEventArgs>>(type, processName, BindingFlags.Public | BindingFlags.Instance)
?? throw new NotSupportedException($"Can not find the '{processName}' in InputManager.");
return process.OfType<TInputArgs>();
}
private static IDisposable SubscribeRawInputEventCore<TInputArgs, TEnumType>(string processName, Func<TEnumType, bool>? filter,
Action<TInputArgs> next)
where TInputArgs : RawInputEventArgs
where TEnumType : Enum
{
var observable = GetRawInputEventObservable<TInputArgs>(processName);
return observable
.Where(x => filter == null || filter(x.GetPropertyOrThrow<TEnumType>(typeof(TInputArgs), "Type", BindingFlags.Public | BindingFlags.Instance)!))
.Subscribe(next);
}
public static IDisposable SubscribeRawKeyEvent(Func<RawKeyEventType, bool>? filter,
Action<RawKeyEventArgs> next)
{
return SubscribeRawInputEventCore("Process", filter, next);
}
public static IDisposable SubscribePreRawKeyEvent(Func<RawKeyEventType, bool>? filter,
Action<RawKeyEventArgs> next)
{
return SubscribeRawInputEventCore("PreProcess", filter, next);
}
public static IDisposable SubscribePostRawKeyEvent(Func<RawKeyEventType, bool>? filter,
Action<RawKeyEventArgs> next)
{
return SubscribeRawInputEventCore("PostProcess", filter, next);
}
public static IDisposable SubscribeRawPointerEvent(Func<RawPointerEventType, bool>? filter,
Action<RawPointerEventArgs> next)
{
return SubscribeRawInputEventCore("Process", filter, next);
}
public static IDisposable SubscribePreRawPointerEvent(Func<RawPointerEventType, bool>? filter,
Action<RawPointerEventArgs> next)
{
return SubscribeRawInputEventCore("PreProcess", filter, next);
}
public static IDisposable SubscribePostRawPointerEvent(Func<RawPointerEventType, bool>? filter,
Action<RawPointerEventArgs> next)
{
return SubscribeRawInputEventCore("PostProcess", filter, next);
}
}