This commit is contained in:
CortexCore
2024-04-16 04:15:06 +08:00
parent b673a9438d
commit 0362b2c606
183 changed files with 5695 additions and 1453 deletions

View File

@@ -6,7 +6,9 @@
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:90b448749ba9be04ebf2eb5953b53caf"
"GUID:90b448749ba9be04ebf2eb5953b53caf",
"GUID:e0cd26848372d4e5c891c569017e11f1",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -0,0 +1,153 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BITKit.UX;
using Newtonsoft.Json;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
using Cursor = UnityEngine.UIElements.Cursor;
namespace BITKit.IData
{
public interface IUXDataBinder:IDisposable
{
Type TargetType { get; }
void OnTick(float deltaTime);
VisualElement CreateUI(object target,FieldInfo fieldInfo);
VisualElement VisualElement { get; }
}
public abstract class UXDataBinder<TDataType> : IUXDataBinder
{
public Type TargetType => typeof(TDataType);
public VisualElement VisualElement { get; private set; }
protected object target{ get; private set; }
protected TDataType currentValue { get; set; }
protected FieldInfo fieldInfo{ get; private set; }
protected string Name { get; private set; }
public virtual void OnTick(float deltaTime)
{
var newValue = (TDataType)fieldInfo.GetValue(target);
if(Equals(newValue,currentValue))return;
OnValueChanged(currentValue,newValue);
}
protected virtual void OnValueChanged(TDataType oldValue,TDataType newValue)
{
currentValue = newValue;
fieldInfo.SetValue(target,currentValue);
if (VisualElement is INotifyValueChanged<TDataType> notifyValueChanged)
notifyValueChanged.SetValueWithoutNotify(newValue);
}
public virtual void Dispose()
{
target = null;
fieldInfo = null;
VisualElement.RemoveFromHierarchy();
VisualElement = null;
}
public VisualElement CreateUI(object target, FieldInfo fieldInfo)
{
this.target = target;
this.fieldInfo = fieldInfo;
currentValue = (TDataType)fieldInfo.GetValue(target);
VisualElement = OnCreateUI();
var attribute = fieldInfo.GetCustomAttribute<ExportAttribute>();
if(attribute != null)
{
Name = attribute.Name;
VisualElement.GetType().GetProperty("label",ReflectionHelper.Flags)?.SetValue(VisualElement, Name);
}
if (fieldInfo.GetCustomAttribute<ReadOnlyAttribute>() is not null)
{
VisualElement.SetEnabled(false);
}
if(VisualElement is INotifyValueChanged<TDataType> notifyValueChanged)
{
notifyValueChanged.SetValueWithoutNotify(currentValue);
notifyValueChanged.RegisterValueChangedCallback(x =>
{
fieldInfo.SetValue(target,x.newValue);
currentValue = x.newValue;
});
}
return VisualElement;
}
protected abstract VisualElement OnCreateUI();
}
public sealed class UXIntBinder:UXDataBinder<int>
{
protected override VisualElement OnCreateUI() => new IntegerField();
}
public sealed class UXFloatBinder:UXDataBinder<float>
{
protected override VisualElement OnCreateUI() => new FloatField();
}
public sealed class UXVector3Binder:UXDataBinder<Vector3>
{
protected override VisualElement OnCreateUI() => new Vector3Field();
}
public sealed class UXVector2Binder:UXDataBinder<Vector2>
{
protected override VisualElement OnCreateUI() => new Vector2Field();
}
public sealed class UXStringBinder:UXDataBinder<string>
{
protected override VisualElement OnCreateUI()
{
if (this.fieldInfo.GetCustomAttribute<ReadOnlyAttribute>() is not null)
{
return new Label();
}
else
{
return new TextField()
{
isDelayed = true,
multiline = true
};
}
}
}
public sealed class UXFloat3Binder:UXDataBinder<float3>
{
protected override VisualElement OnCreateUI()
{
var field = new Vector3Field
{
label = Name
};
field.SetValueWithoutNotify(currentValue);
field.RegisterCallback<ChangeEvent<Vector3>>(x =>
{
fieldInfo.SetValue(target,(float3)x.newValue);
});
return field;
}
protected override void OnValueChanged(float3 oldValue, float3 newValue)
{
currentValue = newValue;
VisualElement.As<INotifyValueChanged<Vector3>>().SetValueWithoutNotify(newValue);
}
}
}

View File

@@ -1,9 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using BITKit.UX;
using Newtonsoft.Json;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.UIElements;
@@ -11,163 +16,126 @@ using UnityEngine.UIElements;
namespace BITKit.IData
{
public sealed class UXDataBindingHandler:IDisposable
{
internal unsafe UXDataBindingHandler(void* ptr, object value)
{
ValueObject = ptr;
SafeValueObject = value;
visualElement = value switch
{
Vector3 => new Vector3Field(),
float => new FloatField(),
int => new IntegerField(),
_ => throw new InvalidCastException("未支持的数据类型"),
};
switch (visualElement)
{
case INotifyValueChanged<int> intField:
intField.RegisterValueChangedCallback(evt =>
{
//UnsafeUtility.AsRef<int>(ValueObject) = evt.newValue;
//Unsafe.Write(ValueObject,evt.newValue);
UnsafeUtility.AsRef<int>(ValueObject) = evt.newValue;
});
break;
case INotifyValueChanged<float> floatField:
floatField.RegisterValueChangedCallback(evt =>
{
//Unsafe.Write(ValueObject,evt.newValue);
UnsafeUtility.AsRef<float>(ValueObject) = evt.newValue;
});
break;
case INotifyValueChanged<Vector3> vector3Field:
vector3Field.RegisterValueChangedCallback(evt =>
{
//Unsafe.Write(ValueObject,evt.newValue);
UnsafeUtility.AsRef<Vector3>(ValueObject) = evt.newValue;
});
break;
}
}
internal readonly unsafe void* ValueObject;
internal object SafeValueObject;
public VisualElement visualElement { get; }
public void Dispose()
{
visualElement.RemoveFromHierarchy();
}
public unsafe void OnValueChanged()
{
SafeValueObject = SafeValueObject switch
{
int => UnsafeUtility.AsRef<int>(ValueObject),
float => UnsafeUtility.AsRef<float>(ValueObject),
Vector3 => UnsafeUtility.AsRef<Vector3>(ValueObject),
_ => null
};
switch (visualElement)
{
case INotifyValueChanged<int> intField:
intField.SetValueWithoutNotify((int)SafeValueObject);
break;
case INotifyValueChanged<float> floatField:
floatField.SetValueWithoutNotify((float)SafeValueObject);
break;
case INotifyValueChanged<Vector3> vector3Field:
vector3Field.SetValueWithoutNotify((Vector3)SafeValueObject);
break;
}
}
}
public class UXDataBindingService : MonoBehaviour
{
private static readonly List<UXDataBindingHandler> Handlers = new();
public static unsafe UXDataBindingHandler CreateBinding(void* ptr, object value)
[RuntimeInitializeOnLoadMethod]
private static void Reload()
{
var handler = new UXDataBindingHandler(ptr, value);
Handlers.Add(handler);
return handler;
_Binders.Clear();
_Instances.Clear();
}
public static unsafe UXDataBindingHandler CreateBinding(TypedReference typedReference)
private static readonly Dictionary<Type,IUXDataBinder> _Binders = new();
private static readonly CacheList<IUXDataBinder> _Instances = new();
public static VisualElement Create(object target)
{
UXDataBindingHandler handler = null;
var value = TypedReference.ToObject(typedReference);
var visualElement = new VisualElement();
var fields = target.GetType().GetFields(ReflectionHelper.Flags);
foreach (var fieldInfo in fields.Where(x=>x.GetCustomAttribute<ExportAttribute>() is not null))
{
var handler = UXDataBindingService.Create(target,fieldInfo);
var ui = handler.CreateUI(target, fieldInfo);
visualElement.Add(ui);
}
foreach (var methodInfo in target.GetType().GetMethods(ReflectionHelper.Flags))
{
if (Attribute.IsDefined(methodInfo, typeof(ExportAttribute)) is false) continue;
var exportAttribute = methodInfo.GetCustomAttribute<ExportAttribute>();
var button = visualElement.Create<Button>();
button.text =string.IsNullOrEmpty(exportAttribute.Name) ? methodInfo.Name:exportAttribute.Name;
button.clicked += OnClicked;
continue;
void OnClicked()
{
try
{
methodInfo.Invoke(target, null);
}
catch (TargetInvocationException targetInvocationException)
{
if (targetInvocationException.InnerException is InGameException e is false) return;
switch (e)
{
case {InnerException:not null}:
Alert.Print(e.Message,e.InnerException.Message);
break;
default:
Alert.Print(e.Message,e.Source);
break;
}
}
}
}
return visualElement;
}
public static IUXDataBinder Create(object target,FieldInfo fieldInfo)
{
var value = fieldInfo.GetValue(target);
var type = value?.GetType() ?? fieldInfo.FieldType;
// if (value is IEnumerable)
// {
// //type = typeof(IEnumerable<>).MakeGenericType(fieldInfo.FieldType.GetElementType());
// //type = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments());
// }
switch (value)
{
case int:
{
ref var rawValue = ref __refvalue(typedReference, int);
handler = new UXDataBindingHandler(UnsafeUtility.AddressOf(ref rawValue), value);
}
case Array:
type = typeof(IEnumerable<>).MakeGenericType(fieldInfo.FieldType.GetElementType());
break;
case float:
{
ref var rawValue = ref __refvalue(typedReference, float);
handler = new UXDataBindingHandler(UnsafeUtility.AddressOf(ref rawValue), value);
}
break;
case Vector3:
{
ref var rawValue = ref __refvalue(typedReference, Vector3);
handler = new UXDataBindingHandler(UnsafeUtility.AddressOf(ref rawValue), value);
}
case IList:
type = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments());
break;
}
// var handler = (value) switch
// {
// case int=> new UXDataBindingHandler(UnsafeUtility.AddressOf(ref value), value),
// _ => throw new InvalidCastException("未支持的数据类型"),
// };
//var handler = new UXDataBindingHandler(&value, __refvalue(typedReference, object));
Handlers.Add(handler);
return handler;
if (_Binders.TryGetValue(type, out var binder) is false)
throw new NotImplementedException($"未支持的类型:{type.Name}");
var instance = (IUXDataBinder)Activator.CreateInstance(binder.GetType());
_Instances.Add(instance);
return instance;
}
[SerializeReference, SubclassSelector] private ITicker ticker;
[SerializeReference,SubclassSelector] private ITicker ticker;
[SerializeField,ReadOnly] private int bindingCount;
private void Start()
private async void Start()
{
ticker.Add(OnTick);
destroyCancellationToken.Register(() => { ticker.Remove(OnTick); });
}
var binders = await ReflectionHelper.GetTypes<IUXDataBinder>();
if(destroyCancellationToken.IsCancellationRequested)return;
private void OnTick(float obj)
{
foreach (var handler in Handlers.ToArray())
foreach (var x in binders)
{
unsafe
{
if (handler.visualElement.parent is null)
{
Handlers.Remove(handler);
continue;
}
object value = handler.SafeValueObject switch
{
int => UnsafeUtility.AsRef<int>(handler.ValueObject),
float => UnsafeUtility.AsRef<float>(handler.ValueObject),
Vector3 => UnsafeUtility.AsRef<Vector3>(handler.ValueObject),
_ => null
};
if (value != handler.SafeValueObject)
{
handler.OnValueChanged();
}
}
var instance = (IUXDataBinder)Activator.CreateInstance(x);
if (_Binders.TryAdd(instance.TargetType, instance)) ;
}
ticker.Add(OnTick);
destroyCancellationToken.Register(() =>
{
ticker.Remove(OnTick);
});
}
private static void OnTick(float obj)
{
var array = _Instances.ValueArray;
foreach (var x in array)
{
if (x.VisualElement.panel is null)
{
_Instances.Remove(x);
continue;
}
x.OnTick(obj);
}
bindingCount = Handlers.Count;
}
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Newtonsoft.Json;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.IData
{
public abstract class UXIEnumerableBindingHandler<TDataType> : IUXDataBinder
{
public Type TargetType =>typeof(IEnumerable<TDataType>);
public VisualElement VisualElement { get;private set; }
protected object target { get; private set; }
protected FieldInfo fieldInfo{ get; private set; }
protected VisualElement container { get; private set; }
protected Button addButton;
protected readonly List<TDataType> interfaceList=new();
public VisualElement CreateUI(object target, FieldInfo fieldInfo)
{
if(VisualElement is not null){throw new InvalidOperationException($"已经创建了Inspector<{VisualElement.GetType().Name}>");}
var exportAttribute = fieldInfo.GetCustomAttribute<ExportAttribute>();
this.fieldInfo = fieldInfo;
this.target = target;
// VisualElement = new Foldout();
//
// VisualElement.Create<Label>().text = exportAttribute switch
// {
// not null when string.IsNullOrEmpty(exportAttribute.Name) is false => exportAttribute.Name,
// _=>fieldInfo.Name
// };
VisualElement = new Foldout()
{
text = exportAttribute switch
{
not null when string.IsNullOrEmpty(exportAttribute.Name) is false => exportAttribute.Name,
_=>fieldInfo.Name
}
};
VisualElement.AddToClassList("--button");
container = VisualElement.Create<VisualElement>();
addButton = VisualElement.Create<Button>();
addButton.text = "+";
addButton.clicked += Add;
Rebuild();
return VisualElement;
}
private void Add()
{
interfaceList.Add(default);
switch (fieldInfo.GetValue(target))
{
case List<TDataType> list:
list.Add(default);
break;
case TDataType[] array:
Array.Resize(ref array, array.Length + 1);
fieldInfo.SetValue(target, array);
break;
}
Rebuild();
}
protected void Rebuild()
{
interfaceList.Clear();
interfaceList.AddRange((IEnumerable<TDataType>)fieldInfo.GetValue(target));
container.Clear();
for (var index = 0; index < interfaceList.Count; index++)
{
var index1 = index;
var entryContainer = container.Create<VisualElement>();
entryContainer.style.flexDirection = FlexDirection.Row;
var x = interfaceList[index];
var textField = entryContainer.Create<TextField>();
textField.isDelayed = true;
if(x is not null)
textField.SetValueWithoutNotify(JsonConvert.SerializeObject(x));
textField.style.flexGrow = 1;
textField.style.marginLeft = 0;
textField.RegisterValueChangedCallback(OnTextFieldOnValueChanged);
var removeButton = entryContainer.Create<Button>();
//removeButton.text = $"-{index}";
//u know how to do
removeButton.style.marginTop = 0;
removeButton.AddToClassList("icon-remove");
removeButton.clicked += OnRemoveButtonOnClicked;
container.Add(entryContainer);
continue;
void OnRemoveButtonOnClicked()
{
switch (fieldInfo.GetValue(target))
{
case List<TDataType> list:
list.RemoveAt(index1);
break;
case TDataType[] array:
interfaceList.RemoveAt(index1);
fieldInfo.SetValue(target, interfaceList.ToArray());
break;
}
Rebuild();
}
void OnTextFieldOnValueChanged(ChangeEvent<string> evt)
{
Deserialize(textField,index1,evt.newValue);
}
}
}
public void OnTick(float deltaTime)
{
}
public void Dispose()
{
// TODO 在此释放托管资源
}
protected void Deserialize(TextField textField,int index,string value)
{
try
{
var newValue = (TDataType)JsonConvert.DeserializeObject(value, typeof(TDataType));
switch (fieldInfo.GetValue(target))
{
case List<TDataType> list:
list[index] = newValue;
break;
case TDataType[] array:
array[index] = newValue;
fieldInfo.SetValue(target, array);
break;
}
Rebuild();
textField.EnableInClassList("error",false);
}
catch (Exception e)
{
textField.Query<TextElement>().First().tooltip = e.Message;
textField.EnableInClassList("error",true);
}
}
}
public sealed class UXStringIEnumerableBindingHandler : UXIEnumerableBindingHandler<string>
{
}
public sealed class UXFloatIEnumerableBindingHandler : UXIEnumerableBindingHandler<float>
{
}
public sealed class UXIntIEnumerableBindingHandler : UXIEnumerableBindingHandler<int>
{
}
public sealed class UXVector3IEnumerableBindingHandler : UXIEnumerableBindingHandler<Vector3>
{
}
public sealed class UXFloat3IEnumerableBindingHandler : UXIEnumerableBindingHandler<float3>
{
}
}