1
This commit is contained in:
@@ -8,6 +8,7 @@ using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
#if !UNITY_WEBGL
|
||||
public class UXApplicationFile : MonoBehaviour
|
||||
{
|
||||
[SerializeReference, SubclassSelector] private IApplicationFile applicationFile;
|
||||
@@ -52,5 +53,5 @@ namespace BITKit.UX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -17,7 +17,8 @@
|
||||
"GUID:7d3ace4c6aad3684abe11aa38b6cdf99",
|
||||
"GUID:517785bb4600a5140b47eac5fa49b8fc",
|
||||
"GUID:838d3286f0973344ab6e99d3951012f7",
|
||||
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb"
|
||||
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb",
|
||||
"GUID:e4d11af1289097a4d9d8987f332a2ae8"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
@@ -8,7 +8,8 @@
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:6de01b04fa4e14662b03fa46366da151",
|
||||
"GUID:f19bbd83e3c264a5680926bf75d7e494"
|
||||
"GUID:f19bbd83e3c264a5680926bf75d7e494",
|
||||
"GUID:1c2aa13aa706ffc49a1a0044cad55436"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SkiaSharp;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
@@ -11,192 +12,255 @@ 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()
|
||||
{
|
||||
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);
|
||||
|
||||
});
|
||||
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;
|
||||
}
|
||||
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()
|
||||
{
|
||||
if (float.IsNaN(layout.width) || float.IsNaN(layout.height))
|
||||
{
|
||||
if (AllowWarning)
|
||||
BIT4Log.Log<SkiaChart>("Width or height is NaN");
|
||||
return;
|
||||
}
|
||||
|
||||
JArray[] array;
|
||||
float[] data;
|
||||
try
|
||||
{
|
||||
array = JsonConvert.DeserializeObject<JArray[]>(Json);
|
||||
data = array.SelectMany(x => x.ToObject<float[]>()).ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (AllowWarning)
|
||||
Debug.LogException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data is null or { Length: 0 })
|
||||
{
|
||||
if (AllowWarning)
|
||||
BIT4Log.Log<SkiaChart>("Data is null or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
_dataContainer.Clear();
|
||||
_chartContainer.Clear();
|
||||
// _chartContainer.style.marginBottom = 8;
|
||||
// _chartContainer.style.marginLeft = 8;
|
||||
// _chartContainer.style.marginRight = 8;
|
||||
// _chartContainer.style.marginTop = 8;
|
||||
_dataContainer.style.flexDirection = FlexDirection.ColumnReverse;
|
||||
|
||||
//假如min是x,max是y,将中间的差用data.Length划分
|
||||
var max = data.Max();
|
||||
var min = data.Min();
|
||||
|
||||
var difference = max - min;
|
||||
|
||||
var maxLength = array.Select(x => x.Count).Max();
|
||||
|
||||
max = max + difference / maxLength;
|
||||
min = min - difference / maxLength;
|
||||
difference = max - min;
|
||||
|
||||
|
||||
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划分
|
||||
//Debug.Log($"min:{min} max:{max} difference:{difference} max:{max}");
|
||||
|
||||
_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();
|
||||
}
|
||||
for (var i = 0; i <= maxLength; i++)
|
||||
{
|
||||
var label = _dataContainer.Create<Label>();
|
||||
|
||||
|
||||
if(_chartContainer.layout.width is float.NaN or <10 || _chartContainer.layout.height is float.NaN or <10)
|
||||
return;
|
||||
var info = new SKImageInfo(
|
||||
//var value = (max - min) / (data.Length - 1) * i + min;
|
||||
|
||||
var t = (float)i / maxLength;
|
||||
var value = Mathf.Lerp(min,max,t);
|
||||
label.text = Math.Round(value, 2).ToString();
|
||||
}
|
||||
|
||||
if (_chartContainer.layout.width is float.NaN or < 10 || _chartContainer.layout.height is float.NaN or < 10)
|
||||
{
|
||||
if (AllowWarning)
|
||||
BIT4Log.Log<SkiaChart>("Width or height is NaN or less than 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++)
|
||||
fillPaint.Style = SKPaintStyle.Fill;
|
||||
|
||||
|
||||
for (var index = 0; index < array.Length; index++)
|
||||
{
|
||||
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))
|
||||
var _array = array[index];
|
||||
DoubleBuffer<SKPoint> buffer = new();
|
||||
|
||||
data = _array.ToObject<float[]>();
|
||||
|
||||
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
|
||||
path.MoveTo(0, 0);
|
||||
path.LineTo(0, 0);
|
||||
|
||||
if (_array.Count is not 1 && customStyle.TryGetValue(new CustomStyleProperty<Color>($"--color-{index}"),
|
||||
out var _myStyle))
|
||||
{
|
||||
canvas.DrawLine(previousPoint, currentPoint, linePaint);
|
||||
linePaint.Color = _myStyle.ToSKColor();
|
||||
}
|
||||
|
||||
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);
|
||||
else
|
||||
{
|
||||
linePaint.Color = resolvedStyle.color.ToSKColor();
|
||||
}
|
||||
|
||||
for (var i = 1; i <= data.Length; i++)
|
||||
{
|
||||
var value = data[i-1];
|
||||
var posX = (float)info.Width / data.Length * (i);
|
||||
var p = (value - min) / difference;
|
||||
var poxY = info.Height * p;
|
||||
|
||||
var currentPoint = new SKPoint(posX, poxY);
|
||||
if (buffer.TryGetRelease(out var previousPoint))
|
||||
{
|
||||
canvas.DrawLine(previousPoint, currentPoint, linePaint);
|
||||
}
|
||||
|
||||
if (i == data.Length)
|
||||
{
|
||||
var label = _chartContainer.Create<Label>();
|
||||
label.text = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
|
||||
label.style.position = Position.Absolute;
|
||||
label.style.left = posX; //-label.layout.width/2;
|
||||
label.style.bottom = poxY; //- label.layout.height/2;
|
||||
//label.transform.position = new Vector3(-label.layout.width/2, poxY, 0);
|
||||
}
|
||||
|
||||
|
||||
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
|
||||
|
||||
path.LineTo(posX, poxY);
|
||||
|
||||
buffer.Release(currentPoint);
|
||||
}
|
||||
|
||||
path.LineTo(info.Width, 0);
|
||||
path.Close();
|
||||
}
|
||||
|
||||
path.LineTo(info.Width,0);
|
||||
path.Close();
|
||||
|
||||
|
||||
//canvas.DrawPath(path,filePaint);
|
||||
|
||||
|
||||
var texture = info.ToTexture2D(surface);
|
||||
|
||||
|
||||
_chartContainer.style.backgroundImage = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,35 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class UXDateTime : MonoBehaviour
|
||||
{
|
||||
[SerializeReference,SubclassSelector]private IReference timeFormat;
|
||||
[SerializeReference, SubclassSelector] private IReference path;
|
||||
[SerializeField] private bool isTimerNotClock;
|
||||
private DateTime _dateTime;
|
||||
private DateTime _startTime;
|
||||
private Label timeLabel;
|
||||
private string lastTime;
|
||||
private void Start()
|
||||
{
|
||||
timeLabel = GetComponent<UIDocument>().rootVisualElement.Q<Label>(path.Value);
|
||||
_startTime = DateTime.Now;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
if (isTimerNotClock)
|
||||
{
|
||||
time = new DateTime(DateTime.Now.Ticks - _startTime.Ticks);
|
||||
}
|
||||
var str = GetTime();
|
||||
if(Equals(str,lastTime)) return;
|
||||
lastTime = str;
|
||||
timeLabel.text = lastTime;
|
||||
return;
|
||||
string GetTime() => timeFormat is not null
|
||||
? time.ToString(timeFormat.Value)
|
||||
: time.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c23b988a0ca3904468edef1bd026f977
|
||||
guid: 2ed057e1d7e0e5349a687d9ba41abcc7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using BITKit;
|
||||
using BITKit.UX;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class UXWeather : MonoBehaviour
|
||||
{
|
||||
[SerializeReference,SubclassSelector] private ITicker ticker;
|
||||
[SerializeReference,SubclassSelector] private IReference api;
|
||||
private readonly HttpClient _httpClient=new();
|
||||
|
||||
[UXBindPath("temperature-label")] private Label _temperatureLabel;
|
||||
private void OnEnable()
|
||||
{
|
||||
ticker.Add(OnTick);
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
ticker.Remove(OnTick);
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
UXUtils.Inject(this);
|
||||
OnTick(Time.deltaTime);
|
||||
}
|
||||
|
||||
private async void OnTick(float obj)
|
||||
{
|
||||
var json =await _httpClient.GetStringAsync(api.Value);
|
||||
if(destroyCancellationToken.IsCancellationRequested)return;
|
||||
var jObject = JObject.Parse(json);
|
||||
var temperature = jObject["lives"]![0]!["temperature"]!.ToObject<string>();
|
||||
|
||||
await UniTask.SwitchToMainThread();
|
||||
if (destroyCancellationToken.IsCancellationRequested) return;
|
||||
_temperatureLabel.text = $"{temperature}℃";
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ce3dedf1c2068047a00040de957cb33
|
||||
guid: e21061d5a601e8a4099d30674e76803a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -6,9 +6,9 @@ namespace BITKit.UX
|
||||
{
|
||||
public class UXDrag : UXElement<VisualElement>
|
||||
{
|
||||
Vector3 m_IconStartPosition;
|
||||
Vector3 m_PointerStartPosition;
|
||||
bool isDraging;
|
||||
private Vector3 m_IconStartPosition;
|
||||
private Vector3 m_PointerStartPosition;
|
||||
protected bool isDraging { get; private set; }
|
||||
public override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
@@ -8,13 +8,29 @@ namespace BITKit.UX
|
||||
public class UXScreenJoyStick : UXDrag
|
||||
{
|
||||
[Header(Constant.Header.Settings)]
|
||||
public float range;
|
||||
public bool resetOnUp;
|
||||
public bool updateDelta;
|
||||
[SerializeField] private float range;
|
||||
[SerializeField] private bool resetOnUp;
|
||||
[SerializeField] private bool updateDelta;
|
||||
[SerializeField] private bool raw;
|
||||
[Header(Constant.Header.Events)]
|
||||
public UnityEvent<Vector2> onDelta;
|
||||
[SerializeField] private UnityEvent<Vector2> onDelta;
|
||||
[Header(Constant.Header.InternalVariables)]
|
||||
public Vector2 delta;
|
||||
[SerializeField,ReadOnly] private Vector2 delta;
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
visualElement.RegisterCallback<PointerMoveEvent>(OnPointerMove);
|
||||
}
|
||||
|
||||
private void OnPointerMove(PointerMoveEvent evt)
|
||||
{
|
||||
if (raw&& isDraging)
|
||||
{
|
||||
onDelta.Invoke(evt.deltaPosition);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 GetProcessDelta(Vector2 delta)
|
||||
{
|
||||
delta = Vector2.ClampMagnitude(delta, range);
|
||||
|
110
Src/Unity/Scripts/UX/Library/QRVisualElement.cs
Normal file
110
Src/Unity/Scripts/UX/Library/QRVisualElement.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class QRVisualElement : VisualElement
|
||||
{
|
||||
public new class UxmlTraits:VisualElement.UxmlTraits
|
||||
{
|
||||
private readonly UxmlStringAttributeDescription m_TabsAttribute = new ()
|
||||
{
|
||||
name = "value"
|
||||
};
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
var self = (QRVisualElement)ve;
|
||||
self.Value = m_TabsAttribute.GetValueFromBag(bag, cc);
|
||||
}
|
||||
}
|
||||
public new class UxmlFactory : UxmlFactory<QRVisualElement, UxmlTraits> { }
|
||||
public QRVisualElement()
|
||||
{
|
||||
RegisterCallback<GeometryChangedEvent>(x =>
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
});
|
||||
}
|
||||
public string Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
_value= value;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
private string _value;
|
||||
private Texture _texture;
|
||||
private void Rebuild()
|
||||
{
|
||||
if (float.IsNaN(layout.width) || float.IsNaN(layout.height))return;
|
||||
if(layout.width <10 || layout.height <10)return;
|
||||
|
||||
var target = _value;
|
||||
if (string.IsNullOrEmpty(_value))
|
||||
{
|
||||
target = nameof(QRVisualElement);
|
||||
}
|
||||
|
||||
style.backgroundImage = GenerateQRImageWithColor(target, (int)layout.width, (int)layout.height, Color.black, out var bitMatrix);
|
||||
|
||||
|
||||
|
||||
Texture2D GenerateQRImageWithColor(string content, int width, int height, Color color, out BitMatrix bitMatrix)
|
||||
{
|
||||
width=height=Mathf.Max(width,height);
|
||||
if (_texture)
|
||||
{
|
||||
Object.DestroyImmediate(_texture);
|
||||
}
|
||||
// 编码成color32
|
||||
MultiFormatWriter writer = new MultiFormatWriter();
|
||||
Dictionary<EncodeHintType, object> hints = new Dictionary<EncodeHintType, object>();
|
||||
//设置字符串转换格式,确保字符串信息保持正确
|
||||
hints.Add(EncodeHintType.CHARACTER_SET, "UTF-8");
|
||||
// 设置二维码边缘留白宽度(值越大留白宽度大,二维码就减小)
|
||||
hints.Add(EncodeHintType.MARGIN, 1);
|
||||
hints.Add(EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.M);
|
||||
//实例化字符串绘制二维码工具
|
||||
bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
|
||||
|
||||
// 转成texture2d
|
||||
int w = bitMatrix.Width;
|
||||
int h = bitMatrix.Height;
|
||||
Texture2D texture = new Texture2D(w, h);
|
||||
for (int x = 0; x < h; x++)
|
||||
{
|
||||
for (int y = 0; y < w; y++)
|
||||
{
|
||||
if (bitMatrix[x, y])
|
||||
{
|
||||
texture.SetPixel(y, x, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
texture.SetPixel(y, x, Color.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
texture.Apply();
|
||||
|
||||
//byte[] bytes = texture.EncodeToPNG();
|
||||
//string path = System.IO.Path.Combine(Application.dataPath, "qr.png");
|
||||
//System.IO.File.WriteAllBytes(path, bytes);
|
||||
|
||||
_texture = texture;
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Src/Unity/Scripts/UX/Library/QRVisualElement.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Library/QRVisualElement.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35f4a27da26970c4eb9965386f80281a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 614549820bd3c514eaf369e4895d1ef0
|
||||
guid: 15a54a0341e9ec640a76fb0ae731e876
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "BITKit.UX.Chart.Tests",
|
||||
"name": "BITKit.UX.Painter.Runtime",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
@@ -9,7 +9,7 @@
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:6de01b04fa4e14662b03fa46366da151",
|
||||
"GUID:f19bbd83e3c264a5680926bf75d7e494",
|
||||
"GUID:994a3fb33a5627740b0712e7c483cc1f"
|
||||
"GUID:1c2aa13aa706ffc49a1a0044cad55436"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8acc186ca11c9df42b8ab94c1e952e73
|
||||
guid: 5cbf6465b63af3e4da531a4e4d26376b
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
150
Src/Unity/Scripts/UX/Painter/SkiaPainter.cs
Normal file
150
Src/Unity/Scripts/UX/Painter/SkiaPainter.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets.Kcp;
|
||||
using SkiaSharp;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class SkiaPainter : VisualElement
|
||||
{
|
||||
public new class UxmlFactory : UxmlFactory<SkiaPainter, UxmlTraits> { }
|
||||
public SkiaPainter()
|
||||
{
|
||||
RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||
}
|
||||
|
||||
|
||||
private bool _isPainting;
|
||||
private readonly List<Vector2> _points = new();
|
||||
private SKImageInfo _info;
|
||||
private SKCanvas _canvas;
|
||||
private SKSurface _surface;
|
||||
private Rect _rect;
|
||||
private Optional<SKColor> _backgroundColor=new ();
|
||||
// private readonly SKPaint _linePaint = new()
|
||||
// {
|
||||
// Style = SKPaintStyle.Stroke,
|
||||
// Color = SKColors.Red,
|
||||
// StrokeWidth = 5,
|
||||
// };
|
||||
private readonly SKPaint _linePaint = new SKPaint
|
||||
{
|
||||
Style = SKPaintStyle.Stroke,
|
||||
Color = SKColors.Black, // 笔画颜色为黑色
|
||||
StrokeCap = SKStrokeCap.Round, // 笔画末端采用圆形
|
||||
StrokeJoin = SKStrokeJoin.Round, // 笔画连接处采用圆形
|
||||
StrokeWidth = 8, // 笔画宽度
|
||||
IsAntialias = true // 使用抗锯齿
|
||||
};
|
||||
public void ClearCanvas()
|
||||
{
|
||||
_points.Clear();
|
||||
Rebuild();
|
||||
}
|
||||
private void OnMouseMove(MouseMoveEvent evt)
|
||||
{
|
||||
if (_isPainting is false) return;
|
||||
var pos = evt.localMousePosition;
|
||||
pos.y = layout.height - pos.y;
|
||||
|
||||
var last = _points.LastOrDefault();
|
||||
|
||||
if (_rect.Contains(pos) is false)
|
||||
{
|
||||
|
||||
if (last == default) return;
|
||||
|
||||
_points.Add(default);
|
||||
return;
|
||||
}
|
||||
|
||||
var originalPos = pos;
|
||||
if (last != default)
|
||||
{
|
||||
//pos = Vector2.Lerp(last, pos, 0.1f);
|
||||
|
||||
_points.Add(Vector2.Lerp(last, pos, 0.1f));
|
||||
_points.Add(Vector2.Lerp(last, pos, 0.2f));
|
||||
//_points.Add(Vector2.Lerp(last, pos, 1f));
|
||||
}
|
||||
else
|
||||
{
|
||||
_points.Add(pos);
|
||||
}
|
||||
|
||||
//_points.Add(pos);
|
||||
|
||||
if (originalPos != pos)
|
||||
{
|
||||
//_points.Add(originalPos);
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
_isPainting = false;
|
||||
var last = _points.LastOrDefault();
|
||||
if (last != default)
|
||||
_points.Add(default);
|
||||
}
|
||||
|
||||
private void OnMouseDown(MouseDownEvent evt)
|
||||
{
|
||||
_isPainting = true;
|
||||
}
|
||||
private void OnGeometryChanged(GeometryChangedEvent evt)
|
||||
{
|
||||
if(float.IsNaN(layout.width) || layout.width < 16 || float.IsNaN(layout.height) || layout.height<16)
|
||||
return;
|
||||
_linePaint.Color = resolvedStyle.color.ToSKColor();
|
||||
_surface?.Dispose();
|
||||
_canvas?.Dispose();
|
||||
_info = new SKImageInfo((int)layout.width, (int)layout.height);
|
||||
_info.AlphaType = SKAlphaType.Premul;
|
||||
|
||||
_surface = SKSurface.Create(_info);
|
||||
_canvas = _surface.Canvas;
|
||||
_rect = new Rect(0, 0, layout.width, layout.height);
|
||||
Rebuild();
|
||||
}
|
||||
private void Rebuild()
|
||||
{
|
||||
if (_canvas is null) return;
|
||||
|
||||
_canvas.Clear(resolvedStyle.backgroundColor.ToSKColor());
|
||||
DoubleBuffer<Vector2> buffer = new();
|
||||
|
||||
_linePaint.StrokeWidth = 4;
|
||||
|
||||
foreach (var pos in _points)
|
||||
{
|
||||
if (pos == default)
|
||||
{
|
||||
buffer.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (buffer.TryGetRelease(out var previousPoint))
|
||||
{
|
||||
_canvas.DrawLine(previousPoint.x, previousPoint.y, pos.x, pos.y, _linePaint);
|
||||
}
|
||||
buffer.Release(pos);
|
||||
}
|
||||
}
|
||||
var texture = _info.ToTexture2D(_surface);
|
||||
|
||||
|
||||
style.backgroundImage = texture;
|
||||
}
|
||||
public string Base64 => _surface?.GetBase64();
|
||||
public byte[] Bytes => _surface?.GetBytes();
|
||||
}
|
||||
}
|
||||
|
11
Src/Unity/Scripts/UX/Painter/SkiaPainter.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Painter/SkiaPainter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 380b69a479ae4d546a730e8c8ca136e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Src/Unity/Scripts/UX/Player.meta
Normal file
8
Src/Unity/Scripts/UX/Player.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8127b9701e668c549abd4e3c131168ce
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
53
Src/Unity/Scripts/UX/Player/UXPlayer.cs
Normal file
53
Src/Unity/Scripts/UX/Player/UXPlayer.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BITKit;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.UX
|
||||
{
|
||||
public class UXPlayer : MonoBehaviour
|
||||
{
|
||||
[SerializeReference, SubclassSelector] private IReference rootPath;
|
||||
[SerializeField] private UnityEvent<float> onSetTime;
|
||||
private Slider _playSlider;
|
||||
private Label _currentLabel;
|
||||
private Label _totalLabel;
|
||||
private Button _playButton;
|
||||
private VisualElement _container;
|
||||
private void Start()
|
||||
{
|
||||
_container = GetComponent<UIDocument>().rootVisualElement.Q(rootPath.Value);
|
||||
_playSlider = _container.Q<Slider>("play-slider");
|
||||
_currentLabel = _container.Q<Label>("current-label");
|
||||
_totalLabel = _container.Q<Label>("total-label");
|
||||
_playButton = _container.Q<Button>("play-button");
|
||||
|
||||
_playSlider.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
onSetTime.Invoke(evt.newValue);
|
||||
});
|
||||
}
|
||||
public void SetProgress(float time)
|
||||
{
|
||||
_playSlider.SetValueWithoutNotify(time);
|
||||
}
|
||||
public void SetTime(string time)
|
||||
{
|
||||
_currentLabel.text = time;
|
||||
}
|
||||
public void SetTotalTime(string time)
|
||||
{
|
||||
_totalLabel.text = time;
|
||||
}
|
||||
public void SetPlaying(bool isPlaying)
|
||||
{
|
||||
_playButton.EnableInClassList("playing",isPlaying);
|
||||
}
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
_container.SetActive(active);
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/UX/Player/UXPlayer.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Player/UXPlayer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a70ff2ae966c9c4eb6e7c25cb59d1a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -59,7 +59,16 @@ namespace BITKit.UX
|
||||
//ve = document.rootVisualElement.Q(bindPathAtt.Path);
|
||||
if(bindPathAtt.CanBeNull is false && ve is null)
|
||||
BIT4Log.LogException(new NullReferenceException($"未找到{bindPathAtt.Path}"));
|
||||
fieldInfo.SetValue(self,ve);
|
||||
|
||||
if (fieldInfo.FieldType == typeof(UXContainer))
|
||||
{
|
||||
fieldInfo.SetValue(self, new UXContainer(ve));
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldInfo.SetValue(self, ve);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -10,6 +10,7 @@ using UnityEngine.Pool;
|
||||
using UnityEngine.UIElements;
|
||||
namespace BITKit
|
||||
{
|
||||
[CustomType(typeof(IUXWaiting))]
|
||||
public class UXWaiting : MonoBehaviour,IUXWaiting
|
||||
{
|
||||
public sealed class WaitingHandle : IUXWaitingHandle
|
||||
@@ -29,6 +30,10 @@ namespace BITKit
|
||||
{
|
||||
label.text = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
BIT4Log.Log<UXWaiting>("WaitingHandle is destroyed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +46,13 @@ namespace BITKit
|
||||
private VisualElement _root;
|
||||
|
||||
private Pool<IUXWaitingHandle> _pool;
|
||||
private readonly HashSet<IUXWaitingHandle> _handles = new();
|
||||
|
||||
public IUXWaitingHandle Get()
|
||||
{
|
||||
var handle = _pool.Take();
|
||||
Active();
|
||||
_handles.Add(handle);
|
||||
return handle;
|
||||
async void Active()
|
||||
{
|
||||
@@ -56,40 +63,42 @@ namespace BITKit
|
||||
}
|
||||
public async void Release(IUXWaitingHandle handle)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
if (destroyCancellationToken.IsCancellationRequested) return;
|
||||
_pool.Return(handle);
|
||||
_handles.Remove(handle);
|
||||
Check();
|
||||
}
|
||||
private void Awake()
|
||||
{
|
||||
UXUtils.Inject(this);
|
||||
foreach (var x in _container.Children().ToArray())
|
||||
{
|
||||
x.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
_container.Clear();
|
||||
_pool = new Pool<IUXWaitingHandle>(Create,OnReset,10);
|
||||
|
||||
if (asGlobal)
|
||||
{
|
||||
DI.Register<IUXWaiting>(this);
|
||||
}
|
||||
|
||||
_root.SetActive(false);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Check();
|
||||
}
|
||||
|
||||
private async void OnReset(IUXWaitingHandle obj)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
obj.As<WaitingHandle>().container.SetActive(false);
|
||||
}
|
||||
[BIT]
|
||||
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));
|
||||
_root.SetActive(_handles.Count>0);
|
||||
}
|
||||
|
||||
private IUXWaitingHandle Create()
|
||||
{
|
||||
var handle = new WaitingHandle
|
||||
|
Reference in New Issue
Block a user