1
This commit is contained in:
@@ -5,11 +5,14 @@
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50"
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:90b448749ba9be04ebf2eb5953b53caf",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
|
136
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal file
136
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
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(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() => 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs.meta
Normal file
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8712287847fe6ba46a47620902775417
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
93
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs
Normal file
93
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
|
||||
|
||||
namespace BITKit.IData
|
||||
{
|
||||
public class UXDataBindingService : MonoBehaviour
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
{
|
||||
_Binders.Clear();
|
||||
_Instances.Clear();
|
||||
}
|
||||
private static readonly Dictionary<Type,IUXDataBinder> _Binders = new();
|
||||
private static readonly CacheList<IUXDataBinder> _Instances = new();
|
||||
|
||||
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 Array:
|
||||
type = typeof(IEnumerable<>).MakeGenericType(fieldInfo.FieldType.GetElementType());
|
||||
break;
|
||||
case IList:
|
||||
type = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments());
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
var binders = await ReflectionHelper.GetTypes<IUXDataBinder>();
|
||||
if(destroyCancellationToken.IsCancellationRequested)return;
|
||||
|
||||
foreach (var x in binders)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs.meta
Normal file
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9e5c30fbdd2e9e448dbec24b1e72f3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
193
Src/Unity/Scripts/UX/DataBind/UXIEnumerableBindingHandler.cs
Normal file
193
Src/Unity/Scripts/UX/DataBind/UXIEnumerableBindingHandler.cs
Normal 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>
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43f1856918b6ffc4c9c35caba4007208
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user