1
This commit is contained in:
@@ -101,7 +101,7 @@ namespace BITKit.UX
|
||||
[UXBindPath(UXConstant.TitleLabel)]
|
||||
private Label _titleLabel;
|
||||
[UXBindPath(UXConstant.ContextLabel)]
|
||||
private Label _contextLabel;
|
||||
private TextElement _contextLabel;
|
||||
[UXBindPath(UXConstant.MainButton)]
|
||||
private Button _comfirmButton;
|
||||
[UXBindPath(UXConstant.SecButton)]
|
||||
|
@@ -41,8 +41,14 @@ public class UXBuilder : MonoBehaviour
|
||||
public UXContainer BuildAsContainer(Func<VisualElement> createFactory) => new(Build<VisualElement>(createFactory));
|
||||
public UXContainer BuildAsContainer() => new(Build<VisualElement>());
|
||||
|
||||
public void Clear()
|
||||
public void Clear(bool all=false)
|
||||
{
|
||||
if (all)
|
||||
{
|
||||
visualElementProvider.GetVisualElement().Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var x in instances)
|
||||
{
|
||||
x.RemoveFromHierarchy();
|
||||
|
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "BITKit.UX.Chart.Runtime",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:6de01b04fa4e14662b03fa46366da151",
|
||||
"GUID:f19bbd83e3c264a5680926bf75d7e494"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [
|
||||
"_SkiaSharp"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
202
Assets/BITKit/Unity/Scripts/UX/Chart/SkiaChart.cs
Normal file
202
Assets/BITKit/Unity/Scripts/UX/Chart/SkiaChart.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class SkiaChart : VisualElement
|
||||
{
|
||||
public new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
private readonly UxmlStringAttributeDescription m_JsonAttribute = new ()
|
||||
{
|
||||
name = "Json"
|
||||
};
|
||||
private readonly UxmlBoolAttributeDescription m_allowWarningsAttribute = new ()
|
||||
{
|
||||
name = "allowWarnings",
|
||||
defaultValue = false
|
||||
};
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
var x = (SkiaChart)ve;
|
||||
x.Json = m_JsonAttribute.GetValueFromBag(bag, cc);
|
||||
x.AllowWarning = m_allowWarningsAttribute.GetValueFromBag(bag, cc);
|
||||
|
||||
}
|
||||
}
|
||||
public new class UxmlFactory : UxmlFactory<SkiaChart, UxmlTraits> { }
|
||||
public SkiaChart()
|
||||
{
|
||||
RegisterCallback<GeometryChangedEvent>(x =>
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
}); RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
||||
|
||||
style.flexDirection = FlexDirection.Row;
|
||||
_dataContainer = this.Create<VisualElement>();
|
||||
_chartContainer = this.Create<VisualElement>();
|
||||
_chartContainer.style.flexGrow = 1;
|
||||
|
||||
_dataContainer.style.justifyContent = Justify.SpaceBetween;
|
||||
|
||||
_dataContainer.name = "Data";
|
||||
_chartContainer.name = "Chart";
|
||||
}
|
||||
|
||||
private void OnCustomStyleResolved(CustomStyleResolvedEvent evt)
|
||||
{
|
||||
if(evt.customStyle.TryGetValue(new CustomStyleProperty<Color>("--chart-secondary-color"), out var helpLineColor))
|
||||
{
|
||||
_secondaryColor = helpLineColor;
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
public string Json
|
||||
{
|
||||
get => _json;
|
||||
set
|
||||
{
|
||||
_json = value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private string _json;
|
||||
public bool AllowWarning { get; set; }
|
||||
public Color SecondaryColor
|
||||
{
|
||||
get => _secondaryColor;
|
||||
set
|
||||
{
|
||||
_secondaryColor = value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private Color _secondaryColor;
|
||||
|
||||
private readonly VisualElement _dataContainer;
|
||||
private readonly VisualElement _chartContainer;
|
||||
private void Rebuild()
|
||||
{
|
||||
var data = Array.Empty<float>();
|
||||
try
|
||||
{
|
||||
data = JsonConvert.DeserializeObject<float[]>(Json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (AllowWarning)
|
||||
Debug.LogException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if(float.IsNaN(layout.width) || float.IsNaN(layout.height))
|
||||
return;
|
||||
|
||||
if (data is null or { Length: 0 }) return;
|
||||
|
||||
|
||||
|
||||
_dataContainer.Clear();
|
||||
_chartContainer.Clear();
|
||||
|
||||
var max = data.Max();
|
||||
var min = data.Min();
|
||||
//假如min是x,max是y,将中间的差用data.Length划分
|
||||
|
||||
_dataContainer.style.flexDirection = FlexDirection.ColumnReverse;
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var label = _dataContainer.Create<Label>();
|
||||
|
||||
var value = (max - min) / (data.Length - 1) * i + min;
|
||||
label.text = value.ToString();
|
||||
}
|
||||
|
||||
|
||||
if(_chartContainer.layout.width is float.NaN or <10 || _chartContainer.layout.height is float.NaN or <10)
|
||||
return;
|
||||
var info = new SKImageInfo(
|
||||
width: (int)_chartContainer.layout.width,
|
||||
height: (int)_chartContainer.layout.height,
|
||||
colorType: SKColorType.Rgba8888,
|
||||
alphaType: SKAlphaType.Premul
|
||||
);
|
||||
|
||||
using var surface = SKSurface.Create(info);
|
||||
|
||||
using var canvas = surface.Canvas;
|
||||
|
||||
using var linePaint = new SKPaint();
|
||||
linePaint.Color = resolvedStyle.color.ToSKColor();
|
||||
linePaint.StrokeWidth = resolvedStyle.unityTextOutlineWidth;
|
||||
linePaint.IsAntialias = true;
|
||||
linePaint.Style = SKPaintStyle.Stroke;
|
||||
|
||||
using var helpLinePaint = new SKPaint();
|
||||
helpLinePaint.Color = resolvedStyle.unityTextOutlineColor.ToSKColor();
|
||||
helpLinePaint.StrokeWidth = 1;
|
||||
helpLinePaint.Style = SKPaintStyle.Stroke;
|
||||
|
||||
using var fillPaint = new SKPaint();
|
||||
fillPaint.Color = new SKColor(200, 200, 200, 200);
|
||||
fillPaint.Style=SKPaintStyle.Fill;
|
||||
|
||||
DoubleBuffer<SKPoint> buffer = new();
|
||||
|
||||
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
|
||||
path.MoveTo(0,0);
|
||||
path.LineTo(0,0);
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var value = data[i];
|
||||
var posX = (float)info.Width / (data.Length - 1) * (i);
|
||||
var d = max - min;
|
||||
var p = (value - min) / d;
|
||||
var poxY = info.Height * p;
|
||||
|
||||
var currentPoint = new SKPoint(posX, poxY);
|
||||
if (buffer.TryGetRelease(out var previousPoint))
|
||||
{
|
||||
canvas.DrawLine(previousPoint, currentPoint, linePaint);
|
||||
}
|
||||
|
||||
var label = _chartContainer.Create<Label>();
|
||||
label.text = value.ToString(CultureInfo.InvariantCulture);
|
||||
label.style.position = Position.Absolute;
|
||||
label.style.left = posX;//-label.layout.width/2;
|
||||
label.style.bottom = poxY;//- label.layout.height/2;
|
||||
|
||||
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
|
||||
|
||||
path.LineTo(posX,poxY);
|
||||
|
||||
buffer.Release(currentPoint);
|
||||
}
|
||||
|
||||
path.LineTo(info.Width,0);
|
||||
path.Close();
|
||||
|
||||
|
||||
//canvas.DrawPath(path,filePaint);
|
||||
|
||||
var texture = info.ToTexture2D(surface);
|
||||
|
||||
_chartContainer.style.backgroundImage = texture;
|
||||
}
|
||||
}
|
||||
}
|
35
Assets/BITKit/Unity/Scripts/UX/Chart/SkiaExtensions.cs
Normal file
35
Assets/BITKit/Unity/Scripts/UX/Chart/SkiaExtensions.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using SkiaSharp;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public static class SkiaExtensions
|
||||
{
|
||||
public static Texture2D ToTexture2D(this SKImageInfo info,SKSurface surface)
|
||||
{
|
||||
// Okay, we're finished drawing. Now we create a Unity texture.
|
||||
TextureFormat format = (info.ColorType == SKColorType.Rgba8888) ? TextureFormat.RGBA32 : TextureFormat.BGRA32;
|
||||
var texture = new Texture2D(info.Width, info.Height, format, false, true);
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
// Pull a Skia image object out of the canvas...
|
||||
var pixmap = surface.PeekPixels();
|
||||
// Copy it to the Unity texture...
|
||||
texture.LoadRawTextureData(pixmap.GetPixels(), pixmap.RowBytes * pixmap.Height);
|
||||
texture.Apply(false, true);
|
||||
// And drop it into the RawImage object.
|
||||
|
||||
return texture;
|
||||
}
|
||||
public static SKColor ToSKColor(this Color color,byte? alpha=null)
|
||||
{
|
||||
return new SKColor((byte)(color.r * 255), (byte)(color.g * 255), (byte)(color.b * 255), alpha??(byte)(color.a * 255));
|
||||
}
|
||||
public static SKColor ToSKColor(this Color32 color)
|
||||
{
|
||||
return new SKColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "BITKit.UX.Chart.Tests",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:6de01b04fa4e14662b03fa46366da151",
|
||||
"GUID:f19bbd83e3c264a5680926bf75d7e494",
|
||||
"GUID:994a3fb33a5627740b0712e7c483cc1f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [
|
||||
"_SkiaSharp"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using SkiaSharp;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class SKPaintComponent : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Texture2D texture;
|
||||
[SerializeField] private float[] data;
|
||||
|
||||
[BIT]
|
||||
private void Draw()
|
||||
{
|
||||
var info = new SKImageInfo(
|
||||
width: 384,
|
||||
height: 128,
|
||||
colorType: SKColorType.Rgba8888,
|
||||
alphaType: SKAlphaType.Premul
|
||||
);
|
||||
|
||||
using var surface = SKSurface.Create(info);
|
||||
|
||||
|
||||
using var canvas = surface.Canvas;
|
||||
|
||||
//canvas.Clear(new Color32(31,31,31,255).ToSKColor());
|
||||
|
||||
using var linePaint = new SKPaint();
|
||||
linePaint.Color = new SKColor(255, 0, 0, 255);
|
||||
linePaint.StrokeWidth = 8;
|
||||
linePaint.IsAntialias = true;
|
||||
linePaint.Style = SKPaintStyle.Stroke;
|
||||
|
||||
using var helpLinePaint = new SKPaint();
|
||||
helpLinePaint.Color = new SKColor(200, 200, 200, 200);
|
||||
helpLinePaint.StrokeWidth = 4;
|
||||
helpLinePaint.Style = SKPaintStyle.Stroke;
|
||||
|
||||
using var textPaint = new SKPaint();
|
||||
textPaint.TextAlign = SKTextAlign.Center;
|
||||
textPaint.TextSize = 14;
|
||||
textPaint.ColorF = new SKColor(0, 255, 0, 255);
|
||||
|
||||
using var filePaint = new SKPaint();
|
||||
filePaint.Color = new SKColor(200, 200, 200, 200);
|
||||
filePaint.Style=SKPaintStyle.Fill;
|
||||
|
||||
var min = data.Min();
|
||||
var max = data.Max();
|
||||
|
||||
DoubleBuffer<SKPoint> buffer = new();
|
||||
|
||||
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
|
||||
path.MoveTo(0,0);
|
||||
path.LineTo(0,0);
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var value = data[i];
|
||||
var posX = (float)info.Width / (data.Length - 1) * (i);
|
||||
var d = max - min;
|
||||
var p = (value - min) / d;
|
||||
var poxY = info.Height * p;
|
||||
|
||||
var currentPoint = new SKPoint(posX, poxY);
|
||||
if (buffer.TryGetRelease(out var previousPoint))
|
||||
{
|
||||
canvas.DrawLine(previousPoint, currentPoint, linePaint);
|
||||
}
|
||||
|
||||
canvas.DrawText(
|
||||
value.ToString()
|
||||
, currentPoint
|
||||
, new SKFont(
|
||||
SKTypeface.FromFile(@"D:\Iris\Documents\GitHub\iFactory-YL106.Unity\Assets\BITKit\Unity\Art\Fonts\TTF\Roboto\Roboto-Regular.ttf")
|
||||
), textPaint);
|
||||
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
|
||||
|
||||
path.LineTo(posX,poxY);
|
||||
|
||||
buffer.Release(currentPoint);
|
||||
}
|
||||
|
||||
path.LineTo(info.Width,0);
|
||||
path.Close();
|
||||
|
||||
|
||||
//canvas.DrawPath(path,filePaint);
|
||||
|
||||
texture = info.ToTexture2D(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -30,10 +30,18 @@ namespace BITKit.UX
|
||||
container = this.visualElement.Q(UXConstant.ContextContainer);
|
||||
toggle = this.visualElement.Q<Toggle>(UXConstant.Toggle);
|
||||
}
|
||||
public T Get<T>(int index) where T : VisualElement => visualElement.Q<T>($"{typeof(T).Name}--{index}");
|
||||
public T Get<T>(int index = 0) where T : VisualElement => visualElement.Q<T>($"{typeof(T).Name}--{index}");
|
||||
public void SetProcess(float process)
|
||||
{
|
||||
visualElement.Q(UXConstant.ProcessBarFill).style.width =Length.Percent(Mathf.Clamp(process * 100,0,100)) ;
|
||||
var radialProgress = visualElement.Q<RadialProgress>();
|
||||
if (radialProgress is not null)
|
||||
{
|
||||
radialProgress.progress = Mathf.Clamp((int)(process*100), 0, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
visualElement.Q(UXConstant.ProcessBarFill).style.width =Length.Percent(Mathf.Clamp(process * 100,0,100)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,9 @@
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:90b448749ba9be04ebf2eb5953b53caf"
|
||||
"GUID:90b448749ba9be04ebf2eb5953b53caf",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
153
Assets/BITKit/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal file
153
Assets/BITKit/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
{
|
||||
}
|
||||
}
|
@@ -54,7 +54,7 @@ namespace BITKit.UX
|
||||
{
|
||||
Clear();
|
||||
style.flexDirection = FlexDirection.Row;
|
||||
style.justifyContent = Justify.SpaceAround;
|
||||
style.justifyContent = Justify.FlexStart;
|
||||
instanceColumns.Clear();
|
||||
|
||||
if (string.IsNullOrEmpty(Json)) return;
|
||||
@@ -64,7 +64,9 @@ namespace BITKit.UX
|
||||
var rowLength = jArray.Count;
|
||||
for (var i = 0; i < colLength; i++)
|
||||
{
|
||||
instanceColumns.Add(this.Create<VisualElement>());
|
||||
var newContainer = this.Create<VisualElement>();
|
||||
newContainer.name = $"{nameof(VisualElement)}-{i}";
|
||||
instanceColumns.Add(newContainer);
|
||||
}
|
||||
for (var y = 0; y < rowLength; y++)
|
||||
{
|
||||
|
84
Assets/BITKit/Unity/Scripts/UX/Library/ProgressBlock.cs
Normal file
84
Assets/BITKit/Unity/Scripts/UX/Library/ProgressBlock.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class ProgressBlock : VisualElement
|
||||
{
|
||||
public new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
private readonly UxmlIntAttributeDescription m_ValueAttribute = new ()
|
||||
{
|
||||
name = "Value",defaultValue = 50
|
||||
};
|
||||
private readonly UxmlIntAttributeDescription m_SeparateAttribute = new ()
|
||||
{
|
||||
name = "Separate",defaultValue = 5
|
||||
};
|
||||
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
var x = (ProgressBlock)ve;
|
||||
x.Value = m_ValueAttribute.GetValueFromBag(bag, cc);
|
||||
x.Separate = m_SeparateAttribute.GetValueFromBag(bag, cc);
|
||||
}
|
||||
}
|
||||
public ProgressBlock() : base()
|
||||
{
|
||||
|
||||
}
|
||||
public new class UxmlFactory : UxmlFactory<ProgressBlock, UxmlTraits> { }
|
||||
/// <summary>
|
||||
/// 值,默认50,范围0-100
|
||||
/// </summary>
|
||||
public int Value
|
||||
{
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
this.value = value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private int value;
|
||||
/// <summary>
|
||||
/// 分割线,默认5
|
||||
/// </summary>
|
||||
public int Separate
|
||||
{
|
||||
get => separate;
|
||||
set
|
||||
{
|
||||
this.separate = value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private int separate;
|
||||
private void Rebuild()
|
||||
{
|
||||
Clear();
|
||||
//if value is 58
|
||||
for (var i = 1; i <= Separate; i++)
|
||||
{
|
||||
var block = this.Create<VisualElement>();
|
||||
block.style.flexGrow = 1;
|
||||
|
||||
if (i * 10 < Value)
|
||||
{
|
||||
block.style.opacity = 1;
|
||||
}else if ((i + 1) * 10 > value)
|
||||
{
|
||||
block.style.opacity = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
block.style.opacity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -75,13 +75,23 @@ namespace BITKit.UX
|
||||
BITKit.UX.UXUtils.Inject(this);
|
||||
if(IsValid && autoEntry)
|
||||
UXService.Entry(this);
|
||||
|
||||
var returnButton= document.rootVisualElement.Q("return-button");
|
||||
returnButton?.RegisterCallback<MouseDownEvent>(x =>
|
||||
{
|
||||
if (x.button is 0)
|
||||
OnReturn();
|
||||
});
|
||||
}
|
||||
public bool IsEntered { get; set; }
|
||||
public void Entry()
|
||||
{
|
||||
UXService.Entry(this);
|
||||
}
|
||||
|
||||
protected virtual void OnReturn()
|
||||
{
|
||||
UXService.Return();
|
||||
}
|
||||
protected virtual void OnEnable()=>UXService.Register(this);
|
||||
protected virtual void OnDisable()=>UXService.UnRegister(this);
|
||||
protected virtual void OnPanelEntry(){}
|
||||
|
70
Assets/BITKit/Unity/Scripts/UX/UXBarService/UXBarService.cs
Normal file
70
Assets/BITKit/Unity/Scripts/UX/UXBarService/UXBarService.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
[Serializable]
|
||||
public struct UXBarDictionary:IUXBarService
|
||||
{
|
||||
public static readonly ConcurrentDictionary<string, IUXBarService> BarDictionary =new();
|
||||
[SerializeReference, SubclassSelector] private IReference path;
|
||||
|
||||
public void SetOrCreate(int id, string name, float value, object data = null)
|
||||
{
|
||||
BarDictionary[path.Value].SetOrCreate(id, name, value, data);
|
||||
}
|
||||
public void Remove(int id)
|
||||
{
|
||||
BarDictionary[path.Value].Remove(id);
|
||||
}
|
||||
}
|
||||
public class UXBarService : MonoBehaviour, IUXBarService
|
||||
{
|
||||
[SerializeField] private UIDocument document;
|
||||
[SerializeReference, SubclassSelector] private IReference path;
|
||||
[SerializeReference, SubclassSelector] private IReference containerPath;
|
||||
[SerializeField] private VisualTreeAsset template;
|
||||
|
||||
private VisualElement _container;
|
||||
private readonly ConcurrentDictionary<int, UXContainer> _bars = new();
|
||||
private void Start()
|
||||
{
|
||||
_container = document.rootVisualElement.Q<VisualElement>(containerPath.Value);
|
||||
|
||||
_container.Clear();
|
||||
|
||||
UXBarDictionary.BarDictionary.GetOrAdd(path.Value, this);
|
||||
|
||||
destroyCancellationToken.Register(() =>
|
||||
{
|
||||
UXBarDictionary.BarDictionary.TryRemove(path.Value, out _);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void SetOrCreate(int id, string name, float value, object data = null)
|
||||
{
|
||||
var bar = _bars.GetOrAdd(id, Create);
|
||||
var label = bar.Get<Label>(0);
|
||||
if(label is not null)
|
||||
label.text = name;
|
||||
bar.SetProcess(value);
|
||||
}
|
||||
private UXContainer Create(int arg)
|
||||
{
|
||||
var element = new UXContainer(_container.Create(template));
|
||||
return element;
|
||||
}
|
||||
public void Remove(int id)
|
||||
{
|
||||
if (_bars.TryRemove(id, out var bar))
|
||||
{
|
||||
bar.visualElement.RemoveFromHierarchy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -51,7 +51,12 @@ namespace BITKit.UX
|
||||
)
|
||||
{
|
||||
var bindPathAtt = fieldInfo.GetCustomAttribute<UXBindPathAttribute>();
|
||||
var ve = document.rootVisualElement.Q(bindPathAtt.Path);
|
||||
VisualElement ve = document.rootVisualElement;
|
||||
foreach (var path in bindPathAtt.Path.Split("."))
|
||||
{
|
||||
ve = ve.Q(path);
|
||||
}
|
||||
//ve = document.rootVisualElement.Q(bindPathAtt.Path);
|
||||
if(bindPathAtt.CanBeNull is false && ve is null)
|
||||
BIT4Log.LogException(new NullReferenceException($"未找到{bindPathAtt.Path}"));
|
||||
fieldInfo.SetValue(self,ve);
|
||||
|
108
Assets/BITKit/Unity/Scripts/UX/Waiting/UXWaiting.cs
Normal file
108
Assets/BITKit/Unity/Scripts/UX/Waiting/UXWaiting.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BITKit.UX;
|
||||
using UnityEngine;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using kcp2k;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.UIElements;
|
||||
namespace BITKit
|
||||
{
|
||||
public class UXWaiting : MonoBehaviour,IUXWaiting
|
||||
{
|
||||
public sealed class WaitingHandle : IUXWaitingHandle
|
||||
{
|
||||
public string Message
|
||||
{
|
||||
get => label.text;
|
||||
set => SetMessage(value);
|
||||
}
|
||||
public object Container => container;
|
||||
internal VisualElement container;
|
||||
internal Label label;
|
||||
public async void SetMessage(string message)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
if(container.panel!=null)
|
||||
{
|
||||
label.text = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private VisualTreeAsset handleTemplate;
|
||||
[SerializeField] private bool asGlobal;
|
||||
|
||||
[UXBindPath("waiting-container")]
|
||||
private VisualElement _container;
|
||||
[UXBindPath("waiting-root")]
|
||||
private VisualElement _root;
|
||||
|
||||
private Pool<IUXWaitingHandle> _pool;
|
||||
|
||||
public IUXWaitingHandle Get()
|
||||
{
|
||||
var handle = _pool.Take();
|
||||
Active();
|
||||
return handle;
|
||||
async void Active()
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
handle.As<WaitingHandle>().container.SetActive(true);
|
||||
_root.SetActive(true);
|
||||
}
|
||||
}
|
||||
public async void Release(IUXWaitingHandle handle)
|
||||
{
|
||||
_pool.Return(handle);
|
||||
Check();
|
||||
}
|
||||
private void Awake()
|
||||
{
|
||||
UXUtils.Inject(this);
|
||||
foreach (var x in _container.Children().ToArray())
|
||||
{
|
||||
x.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
_pool = new Pool<IUXWaitingHandle>(Create,OnReset,10);
|
||||
|
||||
if (asGlobal)
|
||||
{
|
||||
DI.Register<IUXWaiting>(this);
|
||||
}
|
||||
|
||||
_root.SetActive(false);
|
||||
}
|
||||
|
||||
private async void OnReset(IUXWaitingHandle obj)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
obj.As<WaitingHandle>().container.SetActive(false);
|
||||
}
|
||||
private async void Check()
|
||||
{
|
||||
await UniTask.Yield();
|
||||
await UniTask.SwitchToMainThread();
|
||||
if(destroyCancellationToken.IsCancellationRequested) return;
|
||||
_root.SetActive(_container.Children().Any(x=>x.style.display != DisplayStyle.None));
|
||||
}
|
||||
|
||||
private IUXWaitingHandle Create()
|
||||
{
|
||||
var handle = new WaitingHandle
|
||||
{
|
||||
container = _container.Create(handleTemplate),
|
||||
label = _container.Q<Label>(UXConstant.ContextLabel)
|
||||
};
|
||||
|
||||
handle.SetMessage($"未初始化");
|
||||
|
||||
handle.container.SetActive(false);
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.UIElements;
|
||||
namespace BITKit
|
||||
{
|
||||
public class WaitingScreen : MonoBehaviour
|
||||
{
|
||||
static ValidHandle isWaiting = new();
|
||||
public static void Excute(object obj)
|
||||
{
|
||||
isWaiting.AddElement(obj);
|
||||
}
|
||||
public static void Complete(object obj)
|
||||
{
|
||||
isWaiting.RemoveElement(obj);
|
||||
}
|
||||
[Header(Constant.Header.Components)]
|
||||
public UIDocument document;
|
||||
void Start()
|
||||
{
|
||||
isWaiting = new();
|
||||
isWaiting.AddListener(SetWaiting);
|
||||
|
||||
isWaiting.Invoke();
|
||||
}
|
||||
async void SetWaiting(bool wait)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
document.rootVisualElement.SetActive(wait);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user